diff --git a/.github/linux-appimage-qt.sh b/.github/linux-appimage-qt.sh index 870172457..203d214e3 100755 --- a/.github/linux-appimage-qt.sh +++ b/.github/linux-appimage-qt.sh @@ -19,8 +19,12 @@ chmod a+x linuxdeploy-x86_64.AppImage chmod a+x linuxdeploy-plugin-qt-x86_64.AppImage chmod a+x linuxdeploy-plugin-checkrt-x86_64.sh + # Build AppImage ./linuxdeploy-x86_64.AppImage --appdir AppDir ./linuxdeploy-plugin-checkrt-x86_64.sh --appdir AppDir + +cp -a "$GITHUB_WORKSPACE/build/translations" AppDir/usr/bin + ./linuxdeploy-x86_64.AppImage --appdir AppDir -d "$GITHUB_WORKSPACE"/.github/shadps4.desktop -e "$GITHUB_WORKSPACE"/build/shadps4 -i "$GITHUB_WORKSPACE"/.github/shadps4.png --plugin qt --output appimage mv Shadps4-x86_64.AppImage Shadps4-qt.AppImage diff --git a/.github/workflows/linux-qt.yml b/.github/workflows/linux-qt.yml index 5611ae50f..06e048c0e 100644 --- a/.github/workflows/linux-qt.yml +++ b/.github/workflows/linux-qt.yml @@ -23,7 +23,7 @@ jobs: - name: Install misc packages run: > - sudo apt-get update && sudo apt install libx11-dev libxext-dev libwayland-dev libfuse2 clang build-essential qt6-base-dev + sudo apt-get update && sudo apt install libx11-dev libxext-dev libwayland-dev libfuse2 clang build-essential qt6-base-dev qt6-tools-dev - name: Configure CMake run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DENABLE_QT_GUI=ON @@ -34,8 +34,14 @@ jobs: - name: Run AppImage packaging script run: ./.github/linux-appimage-qt.sh + - name: Get date and git hash + id: vars + run: | + echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT + echo "shorthash=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT + - name: Upload executable uses: actions/upload-artifact@v4 with: - name: shadps4-linux-qt + name: shadps4-linux-qt-${{ steps.vars.outputs.date }}-${{ steps.vars.outputs.shorthash }} path: Shadps4-qt.AppImage \ No newline at end of file diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index ef77a16c8..ee1340984 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -31,10 +31,16 @@ jobs: - name: Build run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel + - name: Get date and git hash + id: vars + run: | + echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT + echo "shorthash=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT + - name: Upload executable uses: actions/upload-artifact@v4 with: - name: shadps4-ubuntu64 + name: shadps4-ubuntu64-${{ steps.vars.outputs.date }}-${{ steps.vars.outputs.shorthash }} path: | ${{github.workspace}}/build/shadps4 @@ -44,5 +50,5 @@ jobs: - name: Upload executable uses: actions/upload-artifact@v4 with: - name: shadps4-sdl-appimage + name: shadps4-sdl-appimage-${{ steps.vars.outputs.date }}-${{ steps.vars.outputs.shorthash }} path: Shadps4-sdl.AppImage diff --git a/.github/workflows/macos-qt.yml b/.github/workflows/macos-qt.yml index 4b3672dff..f04d3091c 100644 --- a/.github/workflows/macos-qt.yml +++ b/.github/workflows/macos-qt.yml @@ -38,7 +38,7 @@ jobs: host: mac target: desktop arch: clang_64 - archives: qtbase + archives: qtbase qttools - name: Configure CMake run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_OSX_ARCHITECTURES=x86_64 -DENABLE_QT_GUI=ON @@ -53,8 +53,15 @@ jobs: macdeployqt upload/shadps4.app tar cf shadps4-macos-qt.tar.gz -C upload . + - name: Get date and git hash + id: vars + run: | + echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT + echo "shorthash=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT + + - name: Upload executable uses: actions/upload-artifact@v4 with: - name: shadps4-macos-qt + name: shadps4-macos-qt-${{ steps.vars.outputs.date }}-${{ steps.vars.outputs.shorthash }} path: shadps4-macos-qt.tar.gz diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 1a2a6eff6..0eb0ad17a 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -45,8 +45,14 @@ jobs: install_name_tool -add_rpath "@loader_path" upload/shadps4 tar cf shadps4-macos-sdl.tar.gz -C upload . + - name: Get date and git hash + id: vars + run: | + echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT + echo "shorthash=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT + - name: Upload executable uses: actions/upload-artifact@v4 with: - name: shadps4-macos-sdl + name: shadps4-macos-sdl-${{ steps.vars.outputs.date }}-${{ steps.vars.outputs.shorthash }} path: shadps4-macos-sdl.tar.gz diff --git a/.github/workflows/windows-qt.yml b/.github/workflows/windows-qt.yml index 06a16eb5b..83b1a908b 100644 --- a/.github/workflows/windows-qt.yml +++ b/.github/workflows/windows-qt.yml @@ -28,7 +28,7 @@ jobs: host: windows target: desktop arch: win64_msvc2019_64 - archives: qtbase + archives: qtbase qttools - name: Configure CMake run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -T ClangCL -DENABLE_QT_GUI=ON @@ -41,9 +41,16 @@ jobs: mkdir upload move build/Release/shadPS4.exe upload windeployqt --dir upload upload/shadPS4.exe + + - name: Get date and git hash + id: vars + shell: pwsh + run: | + echo "date=$(Get-Date -Format 'yyyy-MM-dd')" >> $env:GITHUB_OUTPUT + echo "shorthash=$(git rev-parse --short HEAD)" >> $env:GITHUB_OUTPUT - name: Upload executable uses: actions/upload-artifact@v4 with: - name: shadps4-win64-qt + name: shadps4-win64-qt-${{ steps.vars.outputs.date }}-${{ steps.vars.outputs.shorthash }} path: upload diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 499124863..413277927 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -26,9 +26,16 @@ jobs: - name: Build run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel + - name: Get date and git hash + id: vars + shell: pwsh + run: | + echo "date=$(Get-Date -Format 'yyyy-MM-dd')" >> $env:GITHUB_OUTPUT + echo "shorthash=$(git rev-parse --short HEAD)" >> $env:GITHUB_OUTPUT + - name: Upload executable uses: actions/upload-artifact@v4 with: - name: shadps4-win64-sdl + name: shadps4-win64-sdl-${{ steps.vars.outputs.date }}-${{ steps.vars.outputs.shorthash }} path: | ${{github.workspace}}/build/Release/shadPS4.exe diff --git a/.gitignore b/.gitignore index 2a3145085..087f29683 100644 --- a/.gitignore +++ b/.gitignore @@ -409,3 +409,6 @@ FodyWeavers.xsd /out/* /third-party/out/* /src/common/scm_rev.cpp + +# for macOS +**/.DS_Store \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index 60fb5fbbf..95b0fc0bb 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,66 +1,87 @@ -[submodule "externals/discord-rpc"] - path = externals/discord-rpc - url = https://github.com/shadps4-emu/ext-discord-rpc.git [submodule "externals/cryptopp-cmake"] path = externals/cryptopp-cmake url = https://github.com/shadps4-emu/ext-cryptopp-cmake.git + shallow = true [submodule "externals/cryptopp"] path = externals/cryptopp url = https://github.com/shadps4-emu/ext-cryptopp.git + shallow = true [submodule "externals/cryptoppwin"] path = externals/cryptoppwin url = https://github.com/shadps4-emu/ext-cryptoppwin.git + shallow = true [submodule "externals/zlib-ng"] path = externals/zlib-ng url = https://github.com/shadps4-emu/ext-zlib-ng.git + shallow = true [submodule "externals/sdl3"] path = externals/sdl3 url = https://github.com/shadps4-emu/ext-SDL.git + shallow = true [submodule "externals/fmt"] path = externals/fmt url = https://github.com/shadps4-emu/ext-fmt.git + shallow = true [submodule "externals/vulkan-headers"] path = externals/vulkan-headers url = https://github.com/KhronosGroup/Vulkan-Headers.git + shallow = true [submodule "externals/vma"] path = externals/vma url = https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git + shallow = true [submodule "externals/glslang"] path = externals/glslang url = https://github.com/KhronosGroup/glslang.git + shallow = true [submodule "externals/robin-map"] path = externals/robin-map url = https://github.com/Tessil/robin-map.git + shallow = true [submodule "externals/xbyak"] path = externals/xbyak url = https://github.com/herumi/xbyak.git + shallow = true [submodule "externals/winpthreads"] path = externals/winpthreads url = https://github.com/shadps4-emu/winpthreads.git + shallow = true [submodule "externals/magic_enum"] path = externals/magic_enum url = https://github.com/Neargye/magic_enum.git + shallow = true [submodule "externals/toml11"] path = externals/toml11 url = https://github.com/ToruNiina/toml11.git + shallow = true [submodule "externals/zydis"] path = externals/zydis url = https://github.com/zyantific/zydis.git + shallow = true [submodule "externals/sirit"] path = externals/sirit url = https://github.com/shadps4-emu/sirit.git + shallow = true [submodule "externals/xxhash"] path = externals/xxhash url = https://github.com/Cyan4973/xxHash.git + shallow = true [submodule "externals/tracy"] path = externals/tracy url = https://github.com/shadps4-emu/tracy.git + shallow = true [submodule "externals/ext-boost"] path = externals/ext-boost url = https://github.com/shadps4-emu/ext-boost.git + shallow = true [submodule "externals/date"] path = externals/date url = https://github.com/HowardHinnant/date.git + shallow = true [submodule "externals/ffmpeg-core"] path = externals/ffmpeg-core - url = https://github.com/shadps4-emu/ext-ffmpeg-core + url = https://github.com/shadps4-emu/ext-ffmpeg-core.git + shallow = true +[submodule "externals/half"] + path = externals/half + url = https://github.com/ROCm/half.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 679325ec8..6c40f0a8f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,6 +17,18 @@ endif() project(shadPS4) +# Forcing PIE makes sure that the base address is high enough so that it doesn't clash with the PS4 memory. +if(UNIX AND NOT APPLE) + set(CMAKE_POSITION_INDEPENDENT_CODE TRUE) + + # check PIE support at link time + include(CheckPIESupported) + check_pie_supported(OUTPUT_VARIABLE pie_check LANGUAGES C CXX) + if(NOT CMAKE_C_LINK_PIE_SUPPORTED OR NOT CMAKE_CXX_LINK_PIE_SUPPORTED) + message(WARNING "PIE is not supported at link time: ${pie_check}") + endif() +endif() + option(ENABLE_QT_GUI "Enable the Qt GUI. If not selected then the emulator uses a minimal SDL-based UI instead" OFF) # This function should be passed a list of all files in a target. It will automatically generate file groups @@ -66,9 +78,8 @@ git_branch_name(GIT_BRANCH) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/src/common/scm_rev.cpp.in" "${CMAKE_CURRENT_SOURCE_DIR}/src/common/scm_rev.cpp" @ONLY) find_package(Boost 1.84.0 CONFIG) -find_package(cryptopp 8.9.0 MODULE) find_package(FFmpeg 5.1.2 MODULE) -find_package(fmt 10.2.1 CONFIG) +find_package(fmt 10.2.0 CONFIG) find_package(glslang 14.2.0 CONFIG) find_package(magic_enum 0.9.6 CONFIG) find_package(RenderDoc 1.6.0 MODULE) @@ -79,8 +90,12 @@ find_package(VulkanHeaders 1.3.289 CONFIG) find_package(VulkanMemoryAllocator 3.1.0 CONFIG) find_package(xbyak 7.07 CONFIG) find_package(xxHash 0.8.2 MODULE) -find_package(zlib-ng 2.2.0 MODULE) -find_package(Zydis 4.1.0 CONFIG) +find_package(zlib-ng 2.1.7 MODULE) +find_package(Zydis 5.0.0 CONFIG) + +if (NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR NOT MSVC) + find_package(cryptopp 8.9.0 MODULE) +endif() if (APPLE) find_package(date 3.0.1 CONFIG) @@ -92,10 +107,6 @@ check_symbol_exists(pthread_mutex_timedlock "pthread.h" HAVE_PTHREAD_MUTEX_TIMED if(HAVE_PTHREAD_MUTEX_TIMEDLOCK OR WIN32) add_compile_options(-DHAVE_PTHREAD_MUTEX_TIMEDLOCK) endif() -check_symbol_exists(sem_timedwait "semaphore.h" HAVE_SEM_TIMEDWAIT) -if(HAVE_SEM_TIMEDWAIT OR WIN32) - add_compile_options(-DHAVE_SEM_TIMEDWAIT) -endif() if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") # libc++ requires -fexperimental-library to enable std::jthread and std::stop_token support. @@ -110,11 +121,27 @@ add_subdirectory(externals) include_directories(src) if(ENABLE_QT_GUI) - find_package(Qt6 REQUIRED COMPONENTS Widgets Concurrent) + find_package(Qt6 REQUIRED COMPONENTS Widgets Concurrent LinguistTools Network) qt_standard_project_setup() set(CMAKE_AUTORCC ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) + + set(QT_TRANSLATIONS "${PROJECT_SOURCE_DIR}/src/qt_gui/translations") + file(GLOB_RECURSE TRANSLATIONS_TS ${QT_TRANSLATIONS}/*.ts) + + set_source_files_properties(${TRANSLATIONS_TS} PROPERTIES OUTPUT_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/translations") + qt_add_translation(TRANSLATIONS_QM ${TRANSLATIONS_TS}) + + set(TRANSLATIONS_QRC ${CMAKE_CURRENT_BINARY_DIR}/translations/translations.qrc) + file(WRITE ${TRANSLATIONS_QRC} "\n") + foreach (QM ${TRANSLATIONS_QM}) + get_filename_component(QM_FILE ${QM} NAME) + file(APPEND ${TRANSLATIONS_QRC} "${QM_FILE}\n") + endforeach (QM) + file(APPEND ${TRANSLATIONS_QRC} "") + + qt_add_resources(TRANSLATIONS ${TRANSLATIONS_QRC}) endif() set(AUDIO_CORE src/audio_core/sdl_audio.cpp @@ -133,9 +160,10 @@ set(AUDIO_LIB src/core/libraries/audio/audioin.cpp set(GNM_LIB src/core/libraries/gnmdriver/gnmdriver.cpp src/core/libraries/gnmdriver/gnmdriver.h + src/core/libraries/gnmdriver/gnm_error.h ) -set(KERNEL_LIB +set(KERNEL_LIB src/core/libraries/kernel/event_flag/event_flag.cpp src/core/libraries/kernel/event_flag/event_flag.h src/core/libraries/kernel/event_flag/event_flag_obj.cpp @@ -193,6 +221,7 @@ set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp src/core/libraries/app_content/app_content.h src/core/libraries/rtc/rtc.cpp src/core/libraries/rtc/rtc.h + src/core/libraries/rtc/rtc_error.h src/core/libraries/disc_map/disc_map.cpp src/core/libraries/disc_map/disc_map.h src/core/libraries/disc_map/disc_map_codes.h @@ -208,6 +237,12 @@ set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp src/core/libraries/avplayer/avplayer_state.h src/core/libraries/avplayer/avplayer.cpp src/core/libraries/avplayer/avplayer.h + src/core/libraries/ngs2/ngs2.cpp + src/core/libraries/ngs2/ngs2.h + src/core/libraries/ngs2/ngs2_error.h + src/core/libraries/ngs2/ngs2_impl.cpp + src/core/libraries/ngs2/ngs2_impl.h + src/core/libraries/ajm/ajm_error.h ) set(VIDEOOUT_LIB src/core/libraries/videoout/buffer.h @@ -217,21 +252,7 @@ set(VIDEOOUT_LIB src/core/libraries/videoout/buffer.h src/core/libraries/videoout/video_out.h ) -set(LIBC_SOURCES src/core/libraries/libc/libc.cpp - src/core/libraries/libc/libc.h - src/core/libraries/libc/printf.h - src/core/libraries/libc/va_ctx.h - src/core/libraries/libc/libc_cxa.cpp - src/core/libraries/libc/libc_cxa.h - src/core/libraries/libc/libc_stdio.cpp - src/core/libraries/libc/libc_stdio.h - src/core/libraries/libc/libc_math.cpp - src/core/libraries/libc/libc_math.h - src/core/libraries/libc/libc_string.cpp - src/core/libraries/libc/libc_string.h - src/core/libraries/libc/libc_stdlib.cpp - src/core/libraries/libc/libc_stdlib.h - src/core/libraries/libc_internal/libc_internal.cpp +set(LIBC_SOURCES src/core/libraries/libc_internal/libc_internal.cpp src/core/libraries/libc_internal/libc_internal.h ) @@ -257,6 +278,7 @@ set(PLAYGO_LIB src/core/libraries/playgo/playgo.cpp set(RANDOM_LIB src/core/libraries/random/random.cpp src/core/libraries/random/random.h + src/core/libraries/random/random_error.h ) set(USBD_LIB src/core/libraries/usbd/usbd.cpp @@ -270,6 +292,7 @@ set(NP_LIBS src/core/libraries/np_manager/np_manager.cpp src/core/libraries/np_trophy/np_trophy.cpp src/core/libraries/np_trophy/np_trophy.h ) + set(MISC_LIBS src/core/libraries/screenshot/screenshot.cpp src/core/libraries/screenshot/screenshot.h ) @@ -336,7 +359,7 @@ set(CORE src/core/aerolib/stubs.cpp src/core/cpu_patches.cpp src/core/cpu_patches.h src/core/crypto/crypto.cpp - src/core/crypto/crypto.h + src/core/crypto/crypto.h src/core/crypto/keys.h src/core/file_format/pfs.h src/core/file_format/pkg.cpp @@ -361,7 +384,7 @@ set(CORE src/core/aerolib/stubs.cpp src/core/loader/elf.h src/core/loader/symbols_resolver.h src/core/loader/symbols_resolver.cpp - src/core/libraries/error_codes.h + src/core/libraries/error_codes.h src/core/libraries/libs.h src/core/libraries/libs.cpp ${AUDIO_LIB} @@ -419,7 +442,6 @@ set(SHADER_RECOMPILER src/shader_recompiler/exception.h src/shader_recompiler/backend/spirv/spirv_emit_context.h src/shader_recompiler/frontend/translate/data_share.cpp src/shader_recompiler/frontend/translate/export.cpp - src/shader_recompiler/frontend/translate/flat_memory.cpp src/shader_recompiler/frontend/translate/scalar_alu.cpp src/shader_recompiler/frontend/translate/scalar_memory.cpp src/shader_recompiler/frontend/translate/translate.cpp @@ -439,7 +461,7 @@ set(SHADER_RECOMPILER src/shader_recompiler/exception.h src/shader_recompiler/frontend/opcodes.h src/shader_recompiler/frontend/structured_control_flow.cpp src/shader_recompiler/frontend/structured_control_flow.h - src/shader_recompiler/ir/passes/constant_propogation_pass.cpp + src/shader_recompiler/ir/passes/constant_propagation_pass.cpp src/shader_recompiler/ir/passes/dead_code_elimination_pass.cpp src/shader_recompiler/ir/passes/identity_removal_pass.cpp src/shader_recompiler/ir/passes/ir_passes.h @@ -477,6 +499,7 @@ set(VIDEO_CORE src/video_core/amdgpu/liverpool.cpp src/video_core/amdgpu/pm4_cmds.h src/video_core/amdgpu/pm4_opcodes.h src/video_core/amdgpu/resource.h + src/video_core/amdgpu/default_context.cpp src/video_core/buffer_cache/buffer.cpp src/video_core/buffer_cache/buffer.h src/video_core/buffer_cache/buffer_cache.cpp @@ -510,6 +533,8 @@ set(VIDEO_CORE src/video_core/amdgpu/liverpool.cpp src/video_core/renderer_vulkan/vk_resource_pool.h src/video_core/renderer_vulkan/vk_scheduler.cpp src/video_core/renderer_vulkan/vk_scheduler.h + src/video_core/renderer_vulkan/vk_shader_cache.cpp + src/video_core/renderer_vulkan/vk_shader_cache.h src/video_core/renderer_vulkan/vk_shader_util.cpp src/video_core/renderer_vulkan/vk_shader_util.h src/video_core/renderer_vulkan/vk_swapchain.cpp @@ -552,6 +577,10 @@ qt_add_resources(RESOURCE_FILES src/shadps4.qrc) set(QT_GUI src/qt_gui/about_dialog.cpp src/qt_gui/about_dialog.h src/qt_gui/about_dialog.ui + src/qt_gui/cheats_patches.cpp + src/qt_gui/cheats_patches.h + src/qt_gui/memory_patcher.cpp + src/qt_gui/memory_patcher.h src/qt_gui/main_window_ui.h src/qt_gui/main_window.cpp src/qt_gui/main_window.h @@ -579,6 +608,7 @@ set(QT_GUI src/qt_gui/about_dialog.cpp src/qt_gui/main.cpp ${EMULATOR} ${RESOURCE_FILES} + ${TRANSLATIONS} ) endif() @@ -618,7 +648,7 @@ target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAlloca if (APPLE) # Reserve system-managed memory space. - target_link_options(shadps4 PRIVATE -Wl,-no_pie,-no_fixup_chains,-no_huge,-pagezero_size,0x400000,-segaddr,GUEST_SYSTEM,0x400000,-image_base,0x10000000000) + target_link_options(shadps4 PRIVATE -Wl,-no_pie,-no_fixup_chains,-no_huge,-pagezero_size,0x4000,-segaddr,TCB_SPACE,0x4000,-segaddr,GUEST_SYSTEM,0x400000,-image_base,0x20000000000) # Link MoltenVK for Vulkan support find_library(MOLTENVK MoltenVK REQUIRED) @@ -626,6 +656,9 @@ if (APPLE) # Replacement for std::chrono::time_zone target_link_libraries(shadps4 PRIVATE date::date-tz) + + # Half float conversions for F16C patches + target_link_libraries(shadps4 PRIVATE half) endif() if (NOT ENABLE_QT_GUI) @@ -639,27 +672,45 @@ else() endif() if (ENABLE_QT_GUI) - target_link_libraries(shadps4 PRIVATE Qt6::Widgets Qt6::Concurrent) + target_link_libraries(shadps4 PRIVATE Qt6::Widgets Qt6::Concurrent Qt6::Network) + add_definitions(-DENABLE_QT_GUI) endif() if (WIN32) - target_link_libraries(shadps4 PRIVATE mincore winpthreads clang_rt.builtins-x86_64.lib) - add_definitions(-D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE -D_SCL_SECURE_NO_WARNINGS) + target_link_libraries(shadps4 PRIVATE mincore winpthreads) + + if (MSVC) + # MSVC likes putting opinions on what people can use, disable: + add_definitions(-D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE -D_SCL_SECURE_NO_WARNINGS) + endif() + add_definitions(-DNOMINMAX -DWIN32_LEAN_AND_MEAN) + if (MSVC) # Needed for conflicts with time.h of windows.h add_definitions(-D_TIMESPEC_DEFINED) endif() + # Target Windows 10 RS5 add_definitions(-DNTDDI_VERSION=0x0A000006 -D_WIN32_WINNT=0x0A00 -DWINVER=0x0A00) - # Increase stack commit area - target_link_options(shadps4 PRIVATE /STACK:0x200000,0x200000) + + if (MSVC) + target_link_libraries(shadps4 PRIVATE clang_rt.builtins-x86_64.lib) + endif() + # Disable ASLR so we can reserve the user area if (MSVC) target_link_options(shadps4 PRIVATE /DYNAMICBASE:NO) else() target_link_options(shadps4 PRIVATE -Wl,--disable-dynamicbase) endif() + + # Increase stack commit area (Needed, otherwise there are crashes) + if (MSVC) + target_link_options(shadps4 PRIVATE /STACK:0x200000,0x200000) + else() + target_link_options(shadps4 PRIVATE -Wl,--stack,2097152) + endif() endif() if (WIN32) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a428598c7..cdda7c8c0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -69,7 +69,7 @@ enum class SomeEnum { * Note that the asterisks are indented by one space to align to the first line. */ struct Position { - // Always intitialize member variables! + // Always initialize member variables! int x{}; int y{}; }; diff --git a/README.md b/README.md index 7089b3c01..c99142c78 100644 --- a/README.md +++ b/README.md @@ -32,27 +32,30 @@ SPDX-License-Identifier: GPL-2.0-or-later

-# shadPS4 +# General information -shadPS4 is an early **PlayStation 4** emulator for **Windows**, **Linux** and **macOS** written in C++ +shadPS4 is an early **PlayStation 4** emulator for **Windows**, **Linux** and **macOS** written in C++. If you encounter problems or have doubts, do not hesitate to look at the [**Quickstart**](https://github.com/shadps4-emu/shadPS4/blob/main/documents/Quickstart/Quickstart.md). To verify that a game works, you can look at [**shadPS4 Game Compatibility**](https://github.com/shadps4-emu/shadps4-game-compatibility). -To discuss shadPS4 development or suggest ideas, join the [**Discord server**](https://discord.gg/MyZRaBngxA). +To discuss shadPS4 development, suggest ideas or to ask for help, join our [**Discord server**](https://discord.gg/MyZRaBngxA). -To get the latest news, go to our [**X (twitter)**](https://x.com/shadps4) or our [**website**](https://shadps4.net/). +To get the latest news, go to our [**X (Twitter)**](https://x.com/shadps4) or our [**website**](https://shadps4.net/). # Status -In development, small games are working like [**Sonic Mania**](https://www.youtube.com/watch?v=AAHoNzhHyCU), [**Undertale**](https://youtu.be/5zIvdy65Ro4), [**Dysmantle**](https://youtu.be/b9xzhLBdESE) and others... +> [!IMPORTANT] +> shadPS4 is early in developement, don't expect a flawless experience. + +Currently, the emulator successfully runs small games like [**Sonic Mania**](https://www.youtube.com/watch?v=AAHoNzhHyCU), [**Undertale**](https://youtu.be/5zIvdy65Ro4) and it can even *somewhat* run [**Bloodborne**](https://www.youtube.com/watch?v=wC6s0avpQRE). # Why -The project started as a fun project. Due to limited free time, it will probably take a while before shadPS4 is able to run anything decent, but we're trying to make small, regular commits. +This project began as a fun project. Given our limited free time, it may take some time before shadPS4 can run more complex games, but we're committed to making small, regular updates. -# Build +# Building ## Windows @@ -62,7 +65,7 @@ Check the build instructions for [**Windows**](https://github.com/shadps4-emu/sh Check the build instructions for [**Linux**](https://github.com/shadps4-emu/shadPS4/blob/main/documents/building-linux.md). -## Build status +## Building status
Windows @@ -91,9 +94,16 @@ Check the build instructions for [**Linux**](https://github.com/shadps4-emu/shad |macOS Qt Build|[![macOS-qt](https://github.com/shadps4-emu/shadPS4/actions/workflows/macos-qt.yml/badge.svg)](https://github.com/shadps4-emu/shadPS4/actions/workflows/macos-qt.yml)
-# Keyboard Mapping +# Debugging and reporting issues -| Controller button | Keyboard | +For more information on how to test, debug and report issues with the emulator or games, read the [Debugging documentation](https://github.com/shadps4-emu/shadPS4/blob/main/documents/Debugging/Debugging.md). + +# Keyboard mapping + +> [!NOTE] +> Xbox and DualShock controllers work out of the box. + +| Controller button | Keyboard equivelant | |-------------|-------------| LEFT AXIS UP | W | LEFT AXIS DOWN | S | @@ -150,4 +160,4 @@ Open a PR and we'll check it :) # License -- [**GPL-2.0 license**](https://github.com/shadps4-emu/shadPS4/blob/main/LICENSE) \ No newline at end of file +- [**GPL-2.0 license**](https://github.com/shadps4-emu/shadPS4/blob/main/LICENSE) diff --git a/documents/Debugging/Debugging.md b/documents/Debugging/Debugging.md new file mode 100644 index 000000000..ef9aab879 --- /dev/null +++ b/documents/Debugging/Debugging.md @@ -0,0 +1,156 @@ + + + # Debugging and reporting issues about shadPS4 and games + +This document covers information about debugging, troubleshooting and reporting developer-side issues related to shadPS4 and games. + +## Setup + +This section will guide you through setting up tools for debugging the emulator. This list will likely expand as more tools and platforms receive consistent setups. + +
+Windows and Visual Studio + +Make sure you have the project set up for building on Windows with Visual Studio and CMake: [Build shadPS4 for Windows +](https://github.com/shadps4-emu/shadPS4/blob/main/documents/building-windows.md) + +1. Open the project folder in Visual Studio **as a folder**. _Do not run `cmake ..` or other commands that set up the project._ + +2. In the Solution Explorer, click the **Switch between solutions and available views** button.\ + ![image](https://github.com/user-attachments/assets/4e2be2b1-ba5a-4451-9ab2-f4ecf246213d) + +3. Double-click on **CMake Targets View**.\ + ![image](https://github.com/user-attachments/assets/5ce7cf90-cd61-4cfa-bef5-645909827290) + +4. Under **shadPS4 Project**, right-click on the **shadps4 (executable)** solution and click **Set as Startup Item**. This will let you start and debug shadPS4 using the VS debug buttons, as well as the default F5 shortcut.\ + ![image](https://github.com/user-attachments/assets/34c7c047-28a3-499f-be8f-df781134d104) + +5. Right-click the **shadps4 (executable)** solution once more and click **Add debug configuration**. + +6. Add an `"args: []"` section into the first `configurations` entry.\ + List your game path as an argument, as if you were launching the non-GUI emulator from the command line. + ![image](https://github.com/user-attachments/assets/8c7c3e69-f38f-4d6b-bdfd-4f1c41c50be7) + +7. Set the appropriate CMake configuration for debugging or testing. + - For debugging the emulator and games within it, select `x64-Clang-Debug`. + - For testing the emulator with compiler optimizations as a release build, it is recommended to select `x64-Clang-RelWithDebInfo`, + as debug symbols will still be generated in case you encounter release configuration-exclusive bugs/errors. + ![image](https://github.com/user-attachments/assets/0d975f7a-7bea-4f89-87ef-5d685bea4381) + +Launch and debug the emulator through **Debug > Start Debugging** (F5 by default), or **Debug > Start Without Debugging** (Ctrl+F5 by default) when testing games for performance. + +
+ +## Configuration + +You can configure the emulator by editing the `config.toml` file found in the `user` folder created after starting the application. + +
+ Some configuration entries worth changing + +- `[General]` + + - `logType`: Configures logging synchronization (`sync`/`async`) + - By default, the emulator logs messages asynchronously for better performance. Some log messages may end up being received out-of-order. + - It can be beneficial to set this to `sync` in order for the log to accurately maintain message order, at the cost of performance. + - When communicating about issues with games and the log messages aren't clear due to potentially confusing order, set this to `sync` and send that log as well. + - `logFilter`: Sets the logging category for various logging classes. + - Format: `: ...` + - Multiple classes can be set by separating them with a space. (example: `Render:Warning Debug:Critical Lib.Pad:Error`) + - Sub-classes can be specified in the same format as seen in the console/log (such as `Core.Linker`). + - All classes and sub-classes can be set by specifying a `*` symbol. (example: `Kernel.*:Critical`) + - Valid log levels: `Trace, Debug, Info, Warning, Error, Critical` - in this order, setting a level silences all levels preceding it and logs every level after it. + - Examples: + - If the log is being spammed with messages coming from Lib.Pad, you can use `Lib.Pad:Critical` to only log critical-level messages. + - If you'd like to mute everything, but still want to receive messages from Vulkan rendering: `*:Critical Render.Vulkan:Info` + + - `Fullscreen`: Display the game in a full screen borderless window. + +- `[GPU]` + - `dumpShaders`: Dump shaders that are loaded by the emulator. Dump path: `../user/shader/dumps` + - `nullGpu`: Disables rendering. + - `screenWidth` and `screenHeight`: Configures the game window width and height. + +- `[Vulkan]` + - `validation`-related settings: Use when debugging Vulkan. + - `rdocEnable`: Automatically hook RenderDoc when installed. Useful for debugging shaders and game rendering. + - `rdocMarkersEnable`: Enable automatic RenderDoc event annotation + +- `[LLE]` + - `libc`: Use LLE with `libc`. + +
+ +## Quick analysis + +This section will provide some preliminary steps to take and tips on what to do when you encounter scenarios that require debugging. + +
+When a game crashes and breaks in the debugger + +1. Analyze the log + - A console will open by default when you launch the emulator. It shows the same log messages that go into the log file found at `/user/log/shad_log.txt`. + + - It is recommended that you start analyzing the log bottom-up first: + - Are there any critical or error-level messages at the end of the log that would point to a reason for the game crashing? + - Do any of the last few messages contain information about the game loading files? + - Did the game window draw anything on-screen? + + - Continue analyzing the log from the start to see other errors (such as with initialization, memory mapping, linker errors etc.) + +2. Analyze the stack trace + - When the emulator is launched through a debugger, it will **break** when an exception or violation is encountered.\ + _(**breaking** in this context means pausing execution of the program before it continues or stops altogether. + Breaks can be intentional as well - these are set with various kinds of **breakpoints**.)_ + + - Default setups of most debuggers include a **Stack trace** window/panel that lists the functions the program has called before breaking. + + - The stack trace entries can be navigated to and will show the relevant function, as well as switch to the state that the program was in at the time of execution.\ + Use the **Locals** and **Watch** windows to investigate variables and other code in these contexts. + + 3. Identify the reason for the crash + - **Logs aren't always accurate in determining the reason for a crash.**\ + Some log entries are reported as errors but may not be fatal for the execution to stop. `Critical` entries are most likely to be the cause for crashes. + + - Pinpoint the area of the emulator where the crash occured\ + If the stack trace ends with functions that are relevant to rendering, it is safe to assume that the issue is with **rendering**.\ + Similarly, if a crash is in a library responsible for playing videos, your issue can be narrowed down to the scope of video playback in the emulator. + + - **⚠ Some crashes are intentional** + - If you identify **Access violations for writing operations** where the function is (or in cases of game libraries, _looks like_ it is) copying memory, + it most likely is an **intentional exception** meant to catch game data being written by the game. + This is used by the emulator developers to identify procedures that have to do with game data changing. + - Debugging tools usually include an option to not break on certain types of exceptions. **Exclude access violations and other intentional exceptions when debugging to skip these exceptions.** + - You can also identify such cases if the game works in Release builds of the emulator. These intentional exceptions are development-time only. + - Attempt to **Continue** and observe whether the stack trace and/or variables and registers change when you encounter exceptions. + +
+ +## Reporting and communicating about issues + +When communicating with the project about game-specific issues, specify an **uniquely identifable game name** along with its `CUSA-xxxxx` code that is specific to the region/variant of the game you're testing.\ +The version number is also important to add at least in the description, especially if you can verify that the game behaves differently across versions.\ +Accurately identifying games will help other developers that own that game recognize your issue by its title and jump in to help test and debug it. + +- Examples of good naming schemes: + - Amplitude (2016) `CUSA02480` + - Rock Band 4 (`CUSA02084`) v1.0 + - inFamous: Second Son \[`CUSA-00004`\] +- Examples of unideal naming schemes: + - _The Witness_ + - _GTA 5_ + - _Watch Dogs_ + +- If your issue is small or you aren't sure whether you have properly identified something, [join the Discord server](https://discord.gg/MyZRaBngxA) and use the #development channel + to concisely explain the issue, as well as any findings you currently have. + +- It is recommended that you check the [game compatibility issue tracker](https://github.com/shadps4-emu/shadps4-game-compatibility/issues) and post very short summaries of progress changes there, + (such as the game now booting into the menu or getting in-game) for organizational and status update purposes. + +- ⚠ **Do not post theoretical, unproven game-specific issues in the emulator issue tracker that you cannot verify and locate in the emulator source code as being a bug.**\ + Do, however, add information about the game you experienced the issue in, so that it can be tested in a reproducible environment. + - Good example: "_Crash in `Shader::Gcn::CFG::EmitBlocks()`, out of bounds list access_" -> _issue description shares stack trace, points to code in the repository and provides relevant information_ + - Bad example: "_Amplitude crashes on boot, access violation_" -> _issue description reiterates title, focuses on the game instead of the emulator and refuses to elaborate_ diff --git a/documents/Quickstart/1.png b/documents/Quickstart/1.png deleted file mode 100644 index 6dc0ce2e3..000000000 Binary files a/documents/Quickstart/1.png and /dev/null differ diff --git a/documents/Quickstart/Quickstart.md b/documents/Quickstart/Quickstart.md index 4c51b288a..4dd897d85 100644 --- a/documents/Quickstart/Quickstart.md +++ b/documents/Quickstart/Quickstart.md @@ -58,4 +58,24 @@ To install PKG files (game and updates), you will need the Qt application (with ## Configure the emulator -You can configure the emulator in the "user" folder (created after the first start of the application) then in the "config.toml" file. Here you can find lots of parameters to set with True or False. +You can configure the emulator by editing the `config.toml` file found in the `user` folder created after starting the application.\ +Some settings may be related to more technical development and debugging. For more information on those, see [Debugging](https://github.com/shadps4-emu/shadPS4/blob/main/documents/Debugging/Debugging.md#configuration). + +Here's a list of configuration entries that are worth changing: + +- `[General]` + + - `Fullscreen`: Display the game in a full screen borderless window. + + - `logType`: Configures logging synchronization (`sync`/`async`) + - It can be beneficial to set this to `sync` in order for the log to accurately maintain message order, at the cost of performance. + - Use when sending logs to developers. See more about [reporting issues](https://github.com/shadps4-emu/shadPS4/blob/main/documents/Debugging/Debugging.md#reporting-and-communicating-about-issues). + - `logFilter`: Sets the logging category for various logging classes. + - Format: `: ...`, `: <*:level> ...` + - Valid log levels: `Trace, Debug, Info, Warning, Error, Critical` - in this order, setting a level silences all levels preceding it and logs every level after it. + - Examples: + - If the log is being spammed with messages coming from Lib.Pad, you can use `Lib.Pad:Critical` to only log critical-level messages. + - If you'd like to mute everything, but still want to receive messages from Vulkan rendering: `*:Error Render.Vulkan:Info` + +- `[GPU]` + - `screenWidth` and `screenHeight`: Configures the game window width and height. \ No newline at end of file diff --git a/documents/building-windows.md b/documents/building-windows.md index e00ed90d9..21fd87154 100644 --- a/documents/building-windows.md +++ b/documents/building-windows.md @@ -5,21 +5,96 @@ SPDX-License-Identifier: GPL-2.0-or-later # Build shadPS4 for Windows -## Download Visual Studio Community 2022 +This tutorial reads as if you have none of the prerequisites already installed. If you do, just ignore the steps regarding installation. +If you are building to contribute to the project, please omit `--depth 1` from the git invokations. -Download link: [**Visual Studio 2022**](https://visualstudio.microsoft.com/vs/) +Note: **ARM64 is not supported!** As of writing, it will not build nor run. The instructions with respect to ARM64 are for developers only. -## Requirements +## Option 1: Visual Studio 2022 -### From Visual Studio Community +### (Prerequisite) Download the Community edition from [**Visual Studio 2022**](https://visualstudio.microsoft.com/vs/) -- Desktop development with C++ +Once you are within the installer: +1. Select `Desktop development with C++` +2. Go to "Individual Components" tab +3. Search and select `C++ Clang Compiler for Windows` and `MSBuild support for LLVM` +4. Continue the installation -### From individual components tab install +### (Prerequisite) Download [**Qt**](https://doc.qt.io/qt-6/get-and-install-qt.html) -- C++ Clang Compiler for Windows (17.0.3) -- MSBuild support for LLVM (Clang-cl) toolset +Beware, this requires you to create a Qt account. If you do not want to do this, please follow the MSYS2/MinGW compilation method instead. -- ## Compiling +1. Under the current, non beta version of Qt (at the time of writing 6.7.2), select the option `MSVC 2019 64-bit` or similar. + If you are on Windows on ARM / Qualcomm Snapdragon Elite X, select `MSVC 2019 ARM64` instead. -- Open Visual Studio Community and select the **x64-Clang-Release**, **x64-Clang-Debug** or **x64-Clang-RelWithDebInfo**. It should compile just fine. + Go through the installation normally. If you know what you are doing, you may unselect individual components that eat up too much disk space. + +2. Download and install [Qt Visual Studio Tools](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.QtVisualStudioTools2022) + +Once you are finished, you will have to configure Qt within Visual Studio: +1. Tools -> Options -> Qt -> Versions +2. Add a new Qt version and navigate it to the correct folder. Should look like so: `C:\Qt\6.7.2\msvc2019_64` +3. Enable the default checkmark on the new version you just created. + +### (Prerequisite) Download [**Git for Windows**](https://git-scm.com/download/win) + +Go through the Git for Windows installation as normal + +### Cloning the source code + +1. Open Git for Windows, navigate to a place where you want to store the shadPS4 source code folder +2. Clone the repository by running + `git clone --depth 1 --recursive https://github.com/shadps4-emu/shadPS4` + +### Compiling with Visual Studio GUI + +1. Open up Visual Studio, select `Open a local folder` and select the folder with the shadPS4 source code. The folder should contain `CMakeLists.txt` +2. Change x64-Clang-Debug to x64-Clang-Release if you want a regular, non-debug build. +3. If you want to build shadPS4 with the Qt Gui: + 1. Click x64-Clang-Release and select "Manage Configurations" + 2. Look for "CMake command arguments" and add to the text field + `-DENABLE_QT_GUI=ON -DCMAKE_PREFIX_PATH=C:\Qt\6.7.2\msvc2019_64` + (Change Qt path if you've installed it to non-default path) + 3. Press CTRL+S to save and wait a moment for CMake generation +4. Change the project to build to shadps4.exe +5. Build -> Build All + +Your shadps4.exe will be in `c:\path\to\source\Build\x64-Clang-Release\` + +To automatically populate the necessary files to run shadPS4.exe, run in a command prompt or terminal: +`C:\Qt\6.7.2\msvc2019_64\bin\windeployqt.exe c:\path\to\shadps4.exe` +(Change Qt path if you've installed it to non-default path) + +## Option 2: MSYS2/MinGW + +### (Prerequisite) Download [**MSYS2**](https://www.msys2.org/) + +Go through the MSYS2 installation as normal + +If you are building to distribute, please omit `-DCMAKE_CXX_FLAGS="-O2 -march=native"` within the build configuration step. + +Normal x86-based computers, follow: +1. Open "MSYS2 MINGW64" from your new applications +2. Run `pacman -Syu`, let it complete; +3. Run `pacman -S --needed git mingw-w64-x86_64-binutils mingw-w64-x86_64-clang mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-qt6-base` +4. Run `git clone --depth 1 --recursive https://github.com/shadps4-emu/shadPS4` +5. Run `cd shadPS4` +6. Run `cmake -S . -B build -DCMAKE_CXX_COMPILER="clang++.exe" -DCMAKE_C_COMPILER="clang.exe" -DCMAKE_CXX_FLAGS="-O2 -march=native"` +7. Run `cmake --build build` +8. To run the finished product, run `./build/shadPS4.exe` + +ARM64-based computers, follow: +1. Open "MSYS2 CLANGARM64" from your new applications +2. Run `pacman -Syu`, let it complete; +3. Run `pacman -S --needed git mingw-w64-clang-aarch64-binutils mingw-w64-clang-aarch64-clang mingw-w64-clang-aarch64-cmake mingw-w64-clang-aarch64-ninja mingw-w64-clang-aarch64-qt6-base` +4. Run `git clone --depth 1 --recursive https://github.com/shadps4-emu/shadPS4` +5. Run `cd shadPS4` +6. Run `cmake -S . -B build -DCMAKE_CXX_COMPILER="clang++.exe" -DCMAKE_C_COMPILER="clang.exe" -DCMAKE_CXX_FLAGS="-O2 -march=native"` +7. Run `cmake --build build` +8. To run the finished product, run `./build/shadPS4.exe` + +## Note on MSYS2 builds + +These builds may not be easily copyable to people who do not also have a MSYS2 installation. +If you want to distribute these builds, you need to copy over the correct DLLs into a distribution folder. +In order to run them, you must be within the MSYS2 shell environment. \ No newline at end of file diff --git a/documents/changelog.txt b/documents/changelog.txt index 7f9c25268..0e189f58d 100644 --- a/documents/changelog.txt +++ b/documents/changelog.txt @@ -1,32 +1,44 @@ +v0.2.0 15/08/2024 - codename validptr +================= +- Adding macOS support +- Big shader recompiler improvements +- Core improvements +- GUI improvements + +v0.1.0 01/07/2024 - codename madturtle +================= +- Added a shader recompiler, with this we have a lot of games that starts to work +- Rewrote a big part of core + v0.0.3 23/03/2024 - codename salad ================= --Switching to std::thread --Use unique_ptr where possible --Replace printf/scanf with type safe fmt --Implemented sceKernelGetProcessTime --Implemented sceKernelGetProcessTimeCounter , sceKernelGetProcessTimeCounterFrequency --Pause emu with P button --Timers rewrote with std::chrono --Added sceSystemServiceGetStatus --Initial FileSystem implementation --Initial TLS work --New logging implementation --Some functions implemented for userService,systemService --Added sceAudioOut module and output using sdl audio +- Switching to std::thread +- Use unique_ptr where possible +- Replace printf/scanf with type safe fmt +- Implemented sceKernelGetProcessTime +- Implemented sceKernelGetProcessTimeCounter, sceKernelGetProcessTimeCounterFrequency +- Pause emu with P button +- Timers rewrote with std::chrono +- Added sceSystemServiceGetStatus +- Initial FileSystem implementation +- Initial TLS work +- New logging implementation +- Some functions implemented for userService, systemService +- Added sceAudioOut module and output using SDL audio v0.0.2 21/10/2023 ================= --using cstdint header in variable types --run_main_entry: Rewrite in asm for stack setup --printf libc implementation for work with sysv_abi --initial pad emulation (only digital pad atm) --Implemented sceVideoOutIsFlipPending --Added auto stubs , now unsupported hle function will resolve as empty stubs --Rewrote libc_cxa functions --Libc implementations ( _ZdlPv,_Znwm,rand,_Fsin,qsort,free,strncpy,memmove,atan2f,pow,_Sin) --ET_SCE_DYNAMIC behaves as valid for execution now. --Initial FileSystem work (not yet usable). +- Using cstdint header in variable types +- run_main_entry: Rewrite in asm for stack setup +- Printf libc implementation for work with sysv_abi +- Initial pad emulation (only digital pad atm) +- Implemented sceVideoOutIsFlipPending +- Added auto stubs, now unsupported hle function will resolve as empty stubs +- Rewrote libc_cxa functions +- Libc implementations ( _ZdlPv,_Znwm,rand,_Fsin,qsort,free,strncpy,memmove,atan2f,pow,_Sin) +- ET_SCE_DYNAMIC behaves as valid for execution now +- Initial FileSystem work (not yet usable) v0.0.1 29/09/2023 ================= -First public release . Everything is new \ No newline at end of file +First public release. Everything is new. \ No newline at end of file diff --git a/documents/readme.txt b/documents/readme.txt deleted file mode 100644 index 748dd060b..000000000 --- a/documents/readme.txt +++ /dev/null @@ -1,35 +0,0 @@ -shadPS4 - A PS4 emulator -========================= - -1. Intro -2. Current status -3. Contributors -4. Greetings - -1.Intro -======= -shadPS4 is a Play Station 4 emulator for Windows and Linux. Although atm it can't run a lot of stuff, we are working torwards to make it more compatible. - -2.Current status -================ -shadPS4 is a HLE emulator. Currently on a small amount of functions is emulated, which is one of the reasons compatibility is low. - -3.Contributors -============== -- georgemoralis -- raphaelthegreat -- skmp -- wheremyfoodat - -4.Greetings -=========== -I would like to thank the following people for helping me so far, with coding or moral support. - -- wheremyfoodat - or @rodakinos for believed me -- paris - or OFFTKP for not believing me and that made me a better coder :D -- skmp - or kornilios for being good old friend -- PandaBad - our beloved stalker -- emufan4568 - for advices -- velocity - for talking 1-2 times per year on discord server. We miss you velocity - -- probably more, will include in the next readme :D diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 0b19034d6..de0317ff9 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -25,11 +25,6 @@ if (NOT TARGET fmt::fmt) add_subdirectory(fmt) endif() -# Discord-RPC -set(BUILD_EXAMPLES OFF CACHE BOOL "") -add_subdirectory(discord-rpc) -target_include_directories(discord-rpc INTERFACE ./discord-rpc/include) - if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND MSVC) # If it is clang and MSVC we will add a static lib # CryptoPP @@ -147,11 +142,17 @@ if (WIN32) target_compile_options(sirit PUBLIC "-Wno-error=unused-command-line-argument") endif() -# date -if (APPLE AND NOT TARGET date::date-tz) - option(BUILD_TZ_LIB "" ON) - option(USE_SYSTEM_TZ_DB "" ON) - add_subdirectory(date) +if (APPLE) + # half + add_library(half INTERFACE) + target_include_directories(half INTERFACE half/include) + + # date + if (NOT TARGET date::date-tz) + option(BUILD_TZ_LIB "" ON) + option(USE_SYSTEM_TZ_DB "" ON) + add_subdirectory(date) + endif() endif() # Tracy diff --git a/externals/discord-rpc b/externals/discord-rpc deleted file mode 160000 index 4ec218155..000000000 --- a/externals/discord-rpc +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4ec218155d73bcb8022f8f7ca72305d801f84beb diff --git a/externals/glslang b/externals/glslang index 4422273e8..12cbda959 160000 --- a/externals/glslang +++ b/externals/glslang @@ -1 +1 @@ -Subproject commit 4422273e8464d20b9d8dd403cbfc3049e09a5f23 +Subproject commit 12cbda959b6df2af119a76a73ff906c2bed36884 diff --git a/externals/half b/externals/half new file mode 160000 index 000000000..1ddada225 --- /dev/null +++ b/externals/half @@ -0,0 +1 @@ +Subproject commit 1ddada225144cac0de8f6b5c0dd9acffd99a2e68 diff --git a/externals/robin-map b/externals/robin-map index 1115dad3f..2c48a1a50 160000 --- a/externals/robin-map +++ b/externals/robin-map @@ -1 +1 @@ -Subproject commit 1115dad3ffa0994e3f43b693d9b9cc99944c64c1 +Subproject commit 2c48a1a50203bbaf1e3d0d64c5d726d56f8d3bb3 diff --git a/externals/sirit b/externals/sirit index 8db09231c..37090c74c 160000 --- a/externals/sirit +++ b/externals/sirit @@ -1 +1 @@ -Subproject commit 8db09231c448b913ae905d5237ce2eca46e3fe87 +Subproject commit 37090c74cc6e680f2bc334cac8fd182f7634a1f6 diff --git a/externals/toml11 b/externals/toml11 index 26d403e46..4b7401272 160000 --- a/externals/toml11 +++ b/externals/toml11 @@ -1 +1 @@ -Subproject commit 26d403e46102269e5314199cd313e82e4e17d99a +Subproject commit 4b740127230472779c4a4d71e1a75aaa3a367a2d diff --git a/scripts/aerolib.inl b/scripts/aerolib.inl index a87c3cc4e..64ee32ea2 100644 --- a/scripts/aerolib.inl +++ b/scripts/aerolib.inl @@ -114108,7 +114108,7 @@ STUB( _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_7Matches2V124RequestCompetitiveResultEE7add_refEv) STUB("efPahl2FufA", _ZN3sce2Np9CppWebApi30CommunicationRestrictionStatus2V35Error8fromJsonERKNS_4Json5ValueE) -STUB("efX3lrPwdKA", sceAppContentAddcontMountByEntitlemetId) +STUB("efX3lrPwdKA", sceAppContentAddcontMountByEntitlementId) STUB("efXnxYFN5oE", _ZNSt11range_errorD0Ev) STUB("efcwuDLsAM0", _ZThn120_NK7WebCore16HTMLMediaElement5mutedEv) STUB("efhGArzWdxE", _ZN7bmalloc6IsoTLS15s_didInitializeE) @@ -129493,7 +129493,7 @@ STUB( STUB("kJlYH5uMAWI", sceNetResolverDestroy) STUB("kJmdxo4uM+8", _ZNSt9money_putIwSt19ostreambuf_iteratorIwSt11char_traitsIwEEE5_InitERKSt8_Locinfo) -STUB("kJmjt81mXKQ", sceAppContentAddcontEnqueueDownloadByEntitlemetId) +STUB("kJmjt81mXKQ", sceAppContentAddcontEnqueueDownloadByEntitlementId) STUB( "kJoY9lMIFzY", _ZN3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_21AdvancedPlayerProfile2V138MatchCompletionRateDisconnectedMetricsEEEEmmEi) diff --git a/scripts/ps4_names.txt b/scripts/ps4_names.txt index caad2b4df..6f95cc734 100644 --- a/scripts/ps4_names.txt +++ b/scripts/ps4_names.txt @@ -80897,10 +80897,10 @@ sceAppCheckerExecute sceAppCheckerExecuteEx sceAppContentAddcontDelete sceAppContentAddcontEnqueueDownload -sceAppContentAddcontEnqueueDownloadByEntitlemetId +sceAppContentAddcontEnqueueDownloadByEntitlementId sceAppContentAddcontEnqueueDownloadSp sceAppContentAddcontMount -sceAppContentAddcontMountByEntitlemetId +sceAppContentAddcontMountByEntitlementId sceAppContentAddcontShrink sceAppContentAddcontUnmount sceAppContentAppParamGetInt diff --git a/src/audio_core/sdl_audio.h b/src/audio_core/sdl_audio.h index 7844bd61b..07e82145e 100644 --- a/src/audio_core/sdl_audio.h +++ b/src/audio_core/sdl_audio.h @@ -22,15 +22,15 @@ public: private: struct PortOut { - bool isOpen = false; - int type = 0; + SDL_AudioStream* stream = nullptr; u32 samples_num = 0; - u8 sample_size = 0; u32 freq = 0; u32 format = -1; + int type = 0; int channels_num = 0; int volume[8] = {}; - SDL_AudioStream* stream = nullptr; + u8 sample_size = 0; + bool isOpen = false; }; std::shared_mutex m_mutex; std::array portsOut; // main up to 8 ports , BGM 1 port , voice up to 4 ports , diff --git a/src/common/config.cpp b/src/common/config.cpp index 24db6b039..8d87ed3c3 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -17,10 +17,12 @@ static s32 gpuId = -1; // Vulkan physical device index. Set to negative for auto static std::string logFilter; static std::string logType = "async"; static std::string userName = "shadPS4"; +static bool useSpecialPad = false; +static int specialPadClass = 1; static bool isDebugDump = false; -static bool isLibc = true; static bool isShowSplash = false; static bool isNullGpu = false; +static bool shouldCopyGPUBuffers = false; static bool shouldDumpShaders = false; static bool shouldDumpPM4 = false; static u32 vblankDivider = 1; @@ -46,13 +48,10 @@ u32 m_window_size_H = 720; std::vector m_pkg_viewer; std::vector m_elf_viewer; std::vector m_recent_files; +std::string emulator_language = "en"; // Settings u32 m_language = 1; // english -bool isLleLibc() { - return isLibc; -} - bool isNeoMode() { return isNeo; } @@ -85,6 +84,14 @@ std::string getUserName() { return userName; } +bool getUseSpecialPad() { + return useSpecialPad; +} + +int getSpecialPadClass() { + return specialPadClass; +} + bool debugDump() { return isDebugDump; } @@ -97,6 +104,10 @@ bool nullGpu() { return isNullGpu; } +bool copyGPUCmdBuffers() { + return shouldCopyGPUBuffers; +} + bool dumpShaders() { return shouldDumpShaders; } @@ -153,6 +164,10 @@ void setNullGpu(bool enable) { isNullGpu = enable; } +void setCopyGPUCmdBuffers(bool enable) { + shouldCopyGPUBuffers = enable; +} + void setDumpShaders(bool enable) { shouldDumpShaders = enable; } @@ -189,18 +204,26 @@ void setNeoMode(bool enable) { isNeo = enable; } -void setLogType(std::string type) { +void setLogType(const std::string& type) { logType = type; } -void setLogFilter(std::string type) { +void setLogFilter(const std::string& type) { logFilter = type; } -void setUserName(std::string type) { +void setUserName(const std::string& type) { userName = type; } +void setUseSpecialPad(bool use) { + useSpecialPad = use; +} + +void setSpecialPadClass(int type) { + specialPadClass = type; +} + void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h) { main_window_geometry_x = x; main_window_geometry_y = y; @@ -219,10 +242,10 @@ void setIconSize(u32 size) { void setIconSizeGrid(u32 size) { m_icon_size_grid = size; } -void setSliderPositon(u32 pos) { +void setSliderPosition(u32 pos) { m_slider_pos = pos; } -void setSliderPositonGrid(u32 pos) { +void setSliderPositionGrid(u32 pos) { m_slider_pos_grid = pos; } void setTableMode(u32 mode) { @@ -234,19 +257,23 @@ void setMainWindowWidth(u32 width) { void setMainWindowHeight(u32 height) { m_window_size_H = height; } -void setPkgViewer(std::vector pkgList) { +void setPkgViewer(const std::vector& pkgList) { m_pkg_viewer.resize(pkgList.size()); m_pkg_viewer = pkgList; } -void setElfViewer(std::vector elfList) { +void setElfViewer(const std::vector& elfList) { m_elf_viewer.resize(elfList.size()); m_elf_viewer = elfList; } -void setRecentFiles(std::vector recentFiles) { +void setRecentFiles(const std::vector& recentFiles) { m_recent_files.resize(recentFiles.size()); m_recent_files = recentFiles; } +void setEmulatorLanguage(std::string language) { + emulator_language = language; +} + u32 getMainWindowGeometryX() { return main_window_geometry_x; } @@ -271,10 +298,10 @@ u32 getIconSize() { u32 getIconSizeGrid() { return m_icon_size_grid; } -u32 getSliderPositon() { +u32 getSliderPosition() { return m_slider_pos; } -u32 getSliderPositonGrid() { +u32 getSliderPositionGrid() { return m_slider_pos_grid; } u32 getTableMode() { @@ -296,6 +323,10 @@ std::vector getRecentFiles() { return m_recent_files; } +std::string getEmulatorLanguage() { + return emulator_language; +} + u32 GetLanguage() { return m_language; } @@ -326,12 +357,20 @@ void load(const std::filesystem::path& path) { isShowSplash = toml::find_or(general, "showSplash", true); } + if (data.contains("Input")) { + const toml::value& input = data.at("Input"); + + useSpecialPad = toml::find_or(input, "useSpecialPad", false); + specialPadClass = toml::find_or(input, "specialPadClass", 1); + } + if (data.contains("GPU")) { const toml::value& gpu = data.at("GPU"); screenWidth = toml::find_or(gpu, "screenWidth", screenWidth); screenHeight = toml::find_or(gpu, "screenHeight", screenHeight); isNullGpu = toml::find_or(gpu, "nullGpu", false); + shouldCopyGPUBuffers = toml::find_or(gpu, "copyGPUBuffers", false); shouldDumpShaders = toml::find_or(gpu, "dumpShaders", false); shouldDumpPM4 = toml::find_or(gpu, "dumpPM4", false); vblankDivider = toml::find_or(gpu, "vblankDivider", 1); @@ -354,12 +393,6 @@ void load(const std::filesystem::path& path) { isDebugDump = toml::find_or(debug, "DebugDump", false); } - if (data.contains("LLE")) { - const toml::value& lle = data.at("LLE"); - - isLibc = toml::find_or(lle, "libc", true); - } - if (data.contains("GUI")) { const toml::value& gui = data.at("GUI"); @@ -379,6 +412,7 @@ void load(const std::filesystem::path& path) { m_elf_viewer = toml::find_or>(gui, "elfDirs", {}); m_recent_files = toml::find_or>(gui, "recentFiles", {}); m_table_mode = toml::find_or(gui, "gameTableMode", 0); + emulator_language = toml::find_or(gui, "emulatorLanguage", "en"); } if (data.contains("Settings")) { @@ -412,9 +446,12 @@ void save(const std::filesystem::path& path) { data["General"]["logType"] = logType; data["General"]["userName"] = userName; data["General"]["showSplash"] = isShowSplash; + data["Input"]["useSpecialPad"] = useSpecialPad; + data["Input"]["specialPadClass"] = specialPadClass; data["GPU"]["screenWidth"] = screenWidth; data["GPU"]["screenHeight"] = screenHeight; data["GPU"]["nullGpu"] = isNullGpu; + data["GPU"]["copyGPUBuffers"] = shouldCopyGPUBuffers; data["GPU"]["dumpShaders"] = shouldDumpShaders; data["GPU"]["dumpPM4"] = shouldDumpPM4; data["GPU"]["vblankDivider"] = vblankDivider; @@ -425,7 +462,6 @@ void save(const std::filesystem::path& path) { data["Vulkan"]["rdocEnable"] = rdocEnable; data["Vulkan"]["rdocMarkersEnable"] = rdocMarkersEnable; data["Debug"]["DebugDump"] = isDebugDump; - data["LLE"]["libc"] = isLibc; data["GUI"]["theme"] = mw_themes; data["GUI"]["iconSize"] = m_icon_size; data["GUI"]["sliderPos"] = m_slider_pos; @@ -442,6 +478,7 @@ void save(const std::filesystem::path& path) { data["GUI"]["pkgDirs"] = m_pkg_viewer; data["GUI"]["elfDirs"] = m_elf_viewer; data["GUI"]["recentFiles"] = m_recent_files; + data["GUI"]["emulatorLanguage"] = emulator_language; data["Settings"]["consoleLanguage"] = m_language; @@ -458,6 +495,8 @@ void setDefaultValues() { logFilter = ""; logType = "async"; userName = "shadPS4"; + useSpecialPad = false; + specialPadClass = 1; isDebugDump = false; isShowSplash = false; isNullGpu = false; @@ -466,6 +505,7 @@ void setDefaultValues() { vblankDivider = 1; vkValidation = false; rdocEnable = false; + emulator_language = "en"; m_language = 1; gpuId = -1; } diff --git a/src/common/config.h b/src/common/config.h index 3006f2e2a..11e7d8827 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -17,14 +17,17 @@ std::string getLogFilter(); std::string getLogType(); std::string getUserName(); +bool getUseSpecialPad(); +int getSpecialPadClass(); + u32 getScreenWidth(); u32 getScreenHeight(); s32 getGpuId(); bool debugDump(); -bool isLleLibc(); bool showSplash(); bool nullGpu(); +bool copyGPUCmdBuffers(); bool dumpShaders(); bool dumpPM4(); bool isRdocEnabled(); @@ -34,6 +37,7 @@ u32 vblankDiv(); void setDebugDump(bool enable); void setShowSplash(bool enable); void setNullGpu(bool enable); +void setCopyGPUCmdBuffers(bool enable); void setDumpShaders(bool enable); void setDumpPM4(bool enable); void setVblankDiv(u32 value); @@ -43,10 +47,13 @@ void setScreenHeight(u32 height); void setFullscreenMode(bool enable); void setLanguage(u32 language); void setNeoMode(bool enable); -void setUserName(std::string type); +void setUserName(const std::string& type); -void setLogType(std::string type); -void setLogFilter(std::string type); +void setUseSpecialPad(bool use); +void setSpecialPadClass(int type); + +void setLogType(const std::string& type); +void setLogFilter(const std::string& type); void setVkValidation(bool enable); void setVkSyncValidation(bool enable); @@ -62,14 +69,15 @@ void setGameInstallDir(const std::string& dir); void setMainWindowTheme(u32 theme); void setIconSize(u32 size); void setIconSizeGrid(u32 size); -void setSliderPositon(u32 pos); -void setSliderPositonGrid(u32 pos); +void setSliderPosition(u32 pos); +void setSliderPositionGrid(u32 pos); void setTableMode(u32 mode); void setMainWindowWidth(u32 width); void setMainWindowHeight(u32 height); -void setPkgViewer(std::vector pkgList); -void setElfViewer(std::vector elfList); -void setRecentFiles(std::vector recentFiles); +void setPkgViewer(const std::vector& pkgList); +void setElfViewer(const std::vector& elfList); +void setRecentFiles(const std::vector& recentFiles); +void setEmulatorLanguage(std::string language); u32 getMainWindowGeometryX(); u32 getMainWindowGeometryY(); @@ -79,14 +87,15 @@ std::string getGameInstallDir(); u32 getMainWindowTheme(); u32 getIconSize(); u32 getIconSizeGrid(); -u32 getSliderPositon(); -u32 getSliderPositonGrid(); +u32 getSliderPosition(); +u32 getSliderPositionGrid(); u32 getTableMode(); u32 getMainWindowWidth(); u32 getMainWindowHeight(); std::vector getPkgViewer(); std::vector getElfViewer(); std::vector getRecentFiles(); +std::string getEmulatorLanguage(); void setDefaultValues(); diff --git a/src/common/debug.h b/src/common/debug.h index 50022a156..596ad7b84 100644 --- a/src/common/debug.h +++ b/src/common/debug.h @@ -29,7 +29,7 @@ static inline bool IsProfilerConnected() { #define TRACK_ALLOC(ptr, size, pool) TracyAllocN(std::bit_cast(ptr), (size), (pool)) #define TRACK_FREE(ptr, pool) TracyFreeN(std::bit_cast(ptr), (pool)) -enum MarkersPallete : int { +enum MarkersPalette : int { EmulatorMarkerColor = 0x264653, RendererMarkerColor = 0x2a9d8f, HleMarkerColor = 0xe9c46a, diff --git a/src/common/io_file.cpp b/src/common/io_file.cpp index e1c22d2a1..fbc37a10c 100644 --- a/src/common/io_file.cpp +++ b/src/common/io_file.cpp @@ -217,7 +217,7 @@ void IOFile::Close() { file = nullptr; #ifdef _WIN64 - if (file_mapping) { + if (file_mapping && file_access_mode == FileAccessMode::ReadWrite) { CloseHandle(std::bit_cast(file_mapping)); } #endif @@ -259,8 +259,7 @@ uintptr_t IOFile::GetFileMapping() { mapping = CreateFileMapping2(hfile, NULL, FILE_MAP_WRITE, PAGE_READWRITE, SEC_COMMIT, 0, NULL, NULL, 0); } else { - mapping = CreateFileMapping2(hfile, NULL, FILE_MAP_READ, PAGE_READONLY, SEC_COMMIT, 0, NULL, - NULL, 0); + mapping = hfile; } file_mapping = std::bit_cast(mapping); diff --git a/src/common/native_clock.cpp b/src/common/native_clock.cpp index 20d0737a6..c3fa637aa 100644 --- a/src/common/native_clock.cpp +++ b/src/common/native_clock.cpp @@ -18,16 +18,16 @@ NativeClock::NativeClock() us_rdtsc_factor{GetFixedPoint64Factor(std::micro::den, rdtsc_frequency)}, ms_rdtsc_factor{GetFixedPoint64Factor(std::milli::den, rdtsc_frequency)} {} -u64 NativeClock::GetTimeNS() const { - return MultiplyHigh(GetUptime(), ns_rdtsc_factor); +u64 NativeClock::GetTimeNS(u64 base_ptc /*= 0*/) const { + return MultiplyHigh(GetUptime() - base_ptc, ns_rdtsc_factor); } -u64 NativeClock::GetTimeUS() const { - return MultiplyHigh(GetUptime(), us_rdtsc_factor); +u64 NativeClock::GetTimeUS(u64 base_ptc /*= 0*/) const { + return MultiplyHigh(GetUptime() - base_ptc, us_rdtsc_factor); } -u64 NativeClock::GetTimeMS() const { - return MultiplyHigh(GetUptime(), ms_rdtsc_factor); +u64 NativeClock::GetTimeMS(u64 base_ptc /*= 0*/) const { + return MultiplyHigh(GetUptime() - base_ptc, ms_rdtsc_factor); } u64 NativeClock::GetUptime() const { diff --git a/src/common/native_clock.h b/src/common/native_clock.h index 227c8d1ae..b5e389452 100644 --- a/src/common/native_clock.h +++ b/src/common/native_clock.h @@ -16,9 +16,9 @@ public: return rdtsc_frequency; } - u64 GetTimeNS() const; - u64 GetTimeUS() const; - u64 GetTimeMS() const; + u64 GetTimeNS(u64 base_ptc = 0) const; + u64 GetTimeUS(u64 base_ptc = 0) const; + u64 GetTimeMS(u64 base_ptc = 0) const; u64 GetUptime() const; u64 GetProcessTimeUS() const; diff --git a/src/common/path_util.cpp b/src/common/path_util.cpp index c1e8a5c0a..e6c1fc1af 100644 --- a/src/common/path_util.cpp +++ b/src/common/path_util.cpp @@ -8,6 +8,7 @@ #ifdef __APPLE__ #include +#include #include #endif @@ -26,23 +27,52 @@ namespace Common::FS { namespace fs = std::filesystem; #ifdef __APPLE__ +using IsTranslocatedURLFunc = Boolean (*)(CFURLRef path, bool* isTranslocated, + CFErrorRef* __nullable error); +using CreateOriginalPathForURLFunc = CFURLRef __nullable (*)(CFURLRef translocatedPath, + CFErrorRef* __nullable error); + +static CFURLRef UntranslocateBundlePath(const CFURLRef bundle_path) { + if (void* security_handle = + dlopen("/System/Library/Frameworks/Security.framework/Security", RTLD_LAZY)) { + SCOPE_EXIT { + dlclose(security_handle); + }; + + const auto IsTranslocatedURL = reinterpret_cast( + dlsym(security_handle, "SecTranslocateIsTranslocatedURL")); + const auto CreateOriginalPathForURL = reinterpret_cast( + dlsym(security_handle, "SecTranslocateCreateOriginalPathForURL")); + + bool is_translocated = false; + if (IsTranslocatedURL && CreateOriginalPathForURL && + IsTranslocatedURL(bundle_path, &is_translocated, nullptr) && is_translocated) { + return CreateOriginalPathForURL(bundle_path, nullptr); + } + } + return nullptr; +} + static std::filesystem::path GetBundleParentDirectory() { if (CFBundleRef bundle_ref = CFBundleGetMainBundle()) { if (CFURLRef bundle_url_ref = CFBundleCopyBundleURL(bundle_ref)) { SCOPE_EXIT { CFRelease(bundle_url_ref); }; - if (CFStringRef bundle_path_ref = - CFURLCopyFileSystemPath(bundle_url_ref, kCFURLPOSIXPathStyle)) { - SCOPE_EXIT { - CFRelease(bundle_path_ref); - }; - char app_bundle_path[MAXPATHLEN]; - if (CFStringGetFileSystemRepresentation(bundle_path_ref, app_bundle_path, - sizeof(app_bundle_path))) { - std::filesystem::path bundle_path{app_bundle_path}; - return bundle_path.parent_path(); + + CFURLRef untranslocated_url_ref = UntranslocateBundlePath(bundle_url_ref); + SCOPE_EXIT { + if (untranslocated_url_ref) { + CFRelease(untranslocated_url_ref); } + }; + + char app_bundle_path[MAXPATHLEN]; + if (CFURLGetFileSystemRepresentation( + untranslocated_url_ref ? untranslocated_url_ref : bundle_url_ref, true, + reinterpret_cast(app_bundle_path), sizeof(app_bundle_path))) { + std::filesystem::path bundle_path{app_bundle_path}; + return bundle_path.parent_path(); } } } @@ -52,11 +82,20 @@ static std::filesystem::path GetBundleParentDirectory() { static auto UserPaths = [] { #ifdef __APPLE__ - std::filesystem::current_path(GetBundleParentDirectory()); + // Start by assuming the base directory is the bundle's parent directory. + std::filesystem::path base_dir = GetBundleParentDirectory(); + std::filesystem::path user_dir = base_dir / PORTABLE_DIR; + // Check if the "user" directory exists in the current path: + if (!std::filesystem::exists(user_dir)) { + // If it doesn't exist, use the new hardcoded path: + user_dir = + std::filesystem::path(getenv("HOME")) / "Library" / "Application Support" / "shadPS4"; + } +#else + const auto user_dir = std::filesystem::current_path() / PORTABLE_DIR; #endif std::unordered_map paths; - const auto user_dir = std::filesystem::current_path() / PORTABLE_DIR; const auto create_path = [&](PathType shad_path, const fs::path& new_path) { std::filesystem::create_directory(new_path); @@ -74,6 +113,9 @@ static auto UserPaths = [] { create_path(PathType::SysModuleDir, user_dir / SYSMODULES_DIR); create_path(PathType::DownloadDir, user_dir / DOWNLOAD_DIR); create_path(PathType::CapturesDir, user_dir / CAPTURES_DIR); + create_path(PathType::CheatsDir, user_dir / CHEATS_DIR); + create_path(PathType::PatchesDir, user_dir / PATCHES_DIR); + create_path(PathType::AddonsDir, user_dir / ADDONS_DIR); return paths; }(); @@ -122,4 +164,4 @@ void SetUserPath(PathType shad_path, const fs::path& new_path) { UserPaths.insert_or_assign(shad_path, new_path); } -} // namespace Common::FS +} // namespace Common::FS \ No newline at end of file diff --git a/src/common/path_util.h b/src/common/path_util.h index 263edd46e..bee93c1b9 100644 --- a/src/common/path_util.h +++ b/src/common/path_util.h @@ -20,6 +20,9 @@ enum class PathType { SysModuleDir, // Where system modules are stored. DownloadDir, // Where downloads/temp files are stored. CapturesDir, // Where rdoc captures are stored. + CheatsDir, // Where cheats are stored. + PatchesDir, // Where patches are stored. + AddonsDir, // Where additional content is stored. }; constexpr auto PORTABLE_DIR = "user"; @@ -35,6 +38,9 @@ constexpr auto TEMPDATA_DIR = "temp"; constexpr auto SYSMODULES_DIR = "sys_modules"; constexpr auto DOWNLOAD_DIR = "download"; constexpr auto CAPTURES_DIR = "captures"; +constexpr auto CHEATS_DIR = "cheats"; +constexpr auto PATCHES_DIR = "patches"; +constexpr auto ADDONS_DIR = "addcont"; // Filenames constexpr auto LOG_FILE = "shad_log.txt"; diff --git a/src/common/slot_vector.h b/src/common/slot_vector.h index f0982e290..36e647971 100644 --- a/src/common/slot_vector.h +++ b/src/common/slot_vector.h @@ -28,9 +28,13 @@ struct SlotId { template class SlotVector { - constexpr static std::size_t InitialCapacity = 1024; + constexpr static std::size_t InitialCapacity = 2048; public: + SlotVector() { + Reserve(InitialCapacity); + } + ~SlotVector() noexcept { std::size_t index = 0; for (u64 bits : stored_bitset) { @@ -67,19 +71,6 @@ public: return SlotId{index}; } - template - [[nodiscard]] SlotId swap_and_insert(SlotId existing_id, Args&&... args) noexcept { - const u32 index = FreeValueIndex(); - T& existing_value = values[existing_id.index].object; - - new (&values[index].object) T(std::move(existing_value)); - existing_value.~T(); - new (&values[existing_id.index].object) T(std::forward(args)...); - SetStorageBit(index); - - return SlotId{index}; - } - void erase(SlotId id) noexcept { values[id.index].object.~T(); free_list.push_back(id.index); @@ -151,7 +142,8 @@ private: const std::size_t old_free_size = free_list.size(); free_list.resize(old_free_size + (new_capacity - values_capacity)); - std::iota(free_list.begin() + old_free_size, free_list.end(), + const std::size_t new_free_size = free_list.size(); + std::iota(free_list.rbegin(), free_list.rbegin() + new_free_size - old_free_size, static_cast(values_capacity)); delete[] values; diff --git a/src/common/uint128.h b/src/common/uint128.h index c44357916..77c4a271d 100644 --- a/src/common/uint128.h +++ b/src/common/uint128.h @@ -94,7 +94,7 @@ namespace Common { // This function divides a u128 by a u32 value and produces two u64 values: // the result of division and the remainder -[[nodiscard]] static inline std::pair Divide128On32(u128 dividend, u32 divisor) { +[[nodiscard]] static inline std::pair Divide128On32(const u128& dividend, u32 divisor) { u64 remainder = dividend[0] % divisor; u64 accum = dividend[0] / divisor; if (dividend[1] == 0) diff --git a/src/core/address_space.cpp b/src/core/address_space.cpp index dec761ba9..9dddf1d86 100644 --- a/src/core/address_space.cpp +++ b/src/core/address_space.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include "common/alignment.h" #include "common/assert.h" #include "common/error.h" #include "core/address_space.h" @@ -130,9 +131,10 @@ struct AddressSpace::Impl { } void* Map(VAddr virtual_addr, PAddr phys_addr, size_t size, ULONG prot, uintptr_t fd = 0) { + const size_t aligned_size = Common::AlignUp(size, 16_KB); const auto it = placeholders.find(virtual_addr); ASSERT_MSG(it != placeholders.end(), "Cannot map already mapped region"); - ASSERT_MSG(virtual_addr >= it->lower() && virtual_addr + size <= it->upper(), + ASSERT_MSG(virtual_addr >= it->lower() && virtual_addr + aligned_size <= it->upper(), "Map range must be fully contained in a placeholder"); // Windows only allows splitting a placeholder into two. @@ -141,7 +143,7 @@ struct AddressSpace::Impl { // one at the start and at the end. const VAddr placeholder_start = it->lower(); const VAddr placeholder_end = it->upper(); - const VAddr virtual_end = virtual_addr + size; + const VAddr virtual_end = virtual_addr + aligned_size; // If the placeholder doesn't exactly start at virtual_addr, split it at the start. if (placeholder_start != virtual_addr) { @@ -162,11 +164,23 @@ struct AddressSpace::Impl { void* ptr = nullptr; if (phys_addr != -1) { HANDLE backing = fd ? reinterpret_cast(fd) : backing_handle; - ptr = MapViewOfFile3(backing, process, reinterpret_cast(virtual_addr), phys_addr, - size, MEM_REPLACE_PLACEHOLDER, prot, nullptr, 0); + if (fd && prot == PAGE_READONLY) { + DWORD resultvar; + ptr = VirtualAlloc2(process, reinterpret_cast(virtual_addr), aligned_size, + MEM_RESERVE | MEM_COMMIT | MEM_REPLACE_PLACEHOLDER, + PAGE_READWRITE, nullptr, 0); + bool ret = ReadFile(backing, ptr, size, &resultvar, NULL); + ASSERT_MSG(ret, "ReadFile failed. {}", Common::GetLastErrorMsg()); + ret = VirtualProtect(ptr, size, prot, &resultvar); + ASSERT_MSG(ret, "VirtualProtect failed. {}", Common::GetLastErrorMsg()); + } else { + ptr = MapViewOfFile3(backing, process, reinterpret_cast(virtual_addr), + phys_addr, aligned_size, MEM_REPLACE_PLACEHOLDER, prot, + nullptr, 0); + } } else { ptr = - VirtualAlloc2(process, reinterpret_cast(virtual_addr), size, + VirtualAlloc2(process, reinterpret_cast(virtual_addr), aligned_size, MEM_RESERVE | MEM_COMMIT | MEM_REPLACE_PLACEHOLDER, prot, nullptr, 0); } ASSERT_MSG(ptr, "{}", Common::GetLastErrorMsg()); @@ -465,12 +479,12 @@ void* AddressSpace::MapFile(VAddr virtual_addr, size_t size, size_t offset, u32 } void AddressSpace::Unmap(VAddr virtual_addr, size_t size, VAddr start_in_vma, VAddr end_in_vma, - PAddr phys_base, bool is_exec, bool has_backing) { + PAddr phys_base, bool is_exec, bool has_backing, bool readonly_file) { #ifdef _WIN32 // There does not appear to be comparable support for partial unmapping on Windows. // Unfortunately, a least one title was found to require this. The workaround is to unmap // the entire allocation and remap the portions outside of the requested unmapping range. - impl->Unmap(virtual_addr, size, has_backing); + impl->Unmap(virtual_addr, size, has_backing && !readonly_file); // TODO: Determine if any titles require partial unmapping support for flexible allocations. ASSERT_MSG(has_backing || (start_in_vma == 0 && end_in_vma == size), diff --git a/src/core/address_space.h b/src/core/address_space.h index 53041bccb..2a3488d57 100644 --- a/src/core/address_space.h +++ b/src/core/address_space.h @@ -92,7 +92,7 @@ public: /// Unmaps specified virtual memory area. void Unmap(VAddr virtual_addr, size_t size, VAddr start_in_vma, VAddr end_in_vma, - PAddr phys_base, bool is_exec, bool has_backing); + PAddr phys_base, bool is_exec, bool has_backing, bool readonly_file); void Protect(VAddr virtual_addr, size_t size, MemoryPermission perms); diff --git a/src/core/aerolib/aerolib.inl b/src/core/aerolib/aerolib.inl index a87c3cc4e..64ee32ea2 100644 --- a/src/core/aerolib/aerolib.inl +++ b/src/core/aerolib/aerolib.inl @@ -114108,7 +114108,7 @@ STUB( _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_7Matches2V124RequestCompetitiveResultEE7add_refEv) STUB("efPahl2FufA", _ZN3sce2Np9CppWebApi30CommunicationRestrictionStatus2V35Error8fromJsonERKNS_4Json5ValueE) -STUB("efX3lrPwdKA", sceAppContentAddcontMountByEntitlemetId) +STUB("efX3lrPwdKA", sceAppContentAddcontMountByEntitlementId) STUB("efXnxYFN5oE", _ZNSt11range_errorD0Ev) STUB("efcwuDLsAM0", _ZThn120_NK7WebCore16HTMLMediaElement5mutedEv) STUB("efhGArzWdxE", _ZN7bmalloc6IsoTLS15s_didInitializeE) @@ -129493,7 +129493,7 @@ STUB( STUB("kJlYH5uMAWI", sceNetResolverDestroy) STUB("kJmdxo4uM+8", _ZNSt9money_putIwSt19ostreambuf_iteratorIwSt11char_traitsIwEEE5_InitERKSt8_Locinfo) -STUB("kJmjt81mXKQ", sceAppContentAddcontEnqueueDownloadByEntitlemetId) +STUB("kJmjt81mXKQ", sceAppContentAddcontEnqueueDownloadByEntitlementId) STUB( "kJoY9lMIFzY", _ZN3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_21AdvancedPlayerProfile2V138MatchCompletionRateDisconnectedMetricsEEEEmmEi) diff --git a/src/core/aerolib/stubs.cpp b/src/core/aerolib/stubs.cpp index 5e2d55cdc..2634fc46a 100644 --- a/src/core/aerolib/stubs.cpp +++ b/src/core/aerolib/stubs.cpp @@ -13,7 +13,7 @@ namespace Core::AeroLib { // on lookup, setting up the nid_entry they are matched with // // If it runs out of stubs with name information, it will return -// a default implemetnation without function name details +// a default implementation without function name details // Up to 512, larger values lead to more resolve stub slots // and to longer compile / CI times diff --git a/src/core/cpu_patches.cpp b/src/core/cpu_patches.cpp index 42318822b..151d34986 100644 --- a/src/core/cpu_patches.cpp +++ b/src/core/cpu_patches.cpp @@ -15,6 +15,7 @@ #else #include #ifdef __APPLE__ +#include #include #endif #endif @@ -30,6 +31,12 @@ static Xbyak::Reg ZydisToXbyakRegister(const ZydisRegister reg) { if (reg >= ZYDIS_REGISTER_RAX && reg <= ZYDIS_REGISTER_R15) { return Xbyak::Reg64(reg - ZYDIS_REGISTER_RAX + Xbyak::Operand::RAX); } + if (reg >= ZYDIS_REGISTER_XMM0 && reg <= ZYDIS_REGISTER_XMM31) { + return Xbyak::Xmm(reg - ZYDIS_REGISTER_XMM0 + xmm0.getIdx()); + } + if (reg >= ZYDIS_REGISTER_YMM0 && reg <= ZYDIS_REGISTER_YMM31) { + return Xbyak::Ymm(reg - ZYDIS_REGISTER_YMM0 + ymm0.getIdx()); + } UNREACHABLE_MSG("Unsupported register: {}", static_cast(reg)); } @@ -66,6 +73,12 @@ static Xbyak::Address ZydisToXbyakMemoryOperand(const ZydisDecodedOperand& opera return ptr[expression]; } +static u64 ZydisToXbyakImmediateOperand(const ZydisDecodedOperand& operand) { + ASSERT_MSG(operand.type == ZYDIS_OPERAND_TYPE_IMMEDIATE, + "Expected immediate operand, got type: {}", static_cast(operand.type)); + return operand.imm.value.u; +} + static std::unique_ptr ZydisToXbyakOperand(const ZydisDecodedOperand& operand) { switch (operand.type) { case ZYDIS_OPERAND_TYPE_REGISTER: { @@ -110,51 +123,127 @@ static Xbyak::Reg AllocateScratchRegister( #ifdef __APPLE__ -static constexpr u32 MaxSavedRegisters = 3; -static pthread_key_t register_save_slots[MaxSavedRegisters]; -static std::once_flag register_save_init_flag; +static pthread_key_t stack_pointer_slot; +static pthread_key_t patch_stack_slot; +static std::once_flag patch_context_slots_init_flag; +static constexpr u32 patch_stack_size = 0x1000; static_assert(sizeof(void*) == sizeof(u64), "Cannot fit a register inside a thread local storage slot."); -static void InitializeRegisterSaveSlots() { - for (u32 i = 0; i < MaxSavedRegisters; i++) { - ASSERT_MSG(pthread_key_create(®ister_save_slots[i], nullptr) == 0, - "Unable to allocate thread-local register save slot {}", i); - } +static void FreePatchStack(void* patch_stack) { + // Subtract back to the bottom of the stack for free. + std::free(static_cast(patch_stack) - patch_stack_size); } +static void InitializePatchContextSlots() { + ASSERT_MSG(pthread_key_create(&stack_pointer_slot, nullptr) == 0, + "Unable to allocate thread-local register for stack pointer."); + ASSERT_MSG(pthread_key_create(&patch_stack_slot, FreePatchStack) == 0, + "Unable to allocate thread-local register for patch stack."); +} + +void InitializeThreadPatchStack() { + std::call_once(patch_context_slots_init_flag, InitializePatchContextSlots); + + pthread_setspecific(patch_stack_slot, + static_cast(std::malloc(patch_stack_size)) + patch_stack_size); +} + +/// Saves the stack pointer to thread local storage and loads the patch stack. +static void SaveStack(Xbyak::CodeGenerator& c) { + std::call_once(patch_context_slots_init_flag, InitializePatchContextSlots); + + // Save original stack pointer and load patch stack. + c.putSeg(gs); + c.mov(qword[reinterpret_cast(stack_pointer_slot * sizeof(void*))], rsp); + c.putSeg(gs); + c.mov(rsp, qword[reinterpret_cast(patch_stack_slot * sizeof(void*))]); +} + +/// Restores the stack pointer from thread local storage. +static void RestoreStack(Xbyak::CodeGenerator& c) { + std::call_once(patch_context_slots_init_flag, InitializePatchContextSlots); + + // Save patch stack pointer and load original stack. + c.putSeg(gs); + c.mov(qword[reinterpret_cast(patch_stack_slot * sizeof(void*))], rsp); + c.putSeg(gs); + c.mov(rsp, qword[reinterpret_cast(stack_pointer_slot * sizeof(void*))]); +} + +#else + +// These utilities are not implemented as we can't save anything to thread local storage without +// temporary registers. +void InitializeThreadPatchStack() { + // No-op +} + +/// Saves the stack pointer to thread local storage and loads the patch stack. +static void SaveStack(Xbyak::CodeGenerator& c) { + UNIMPLEMENTED(); +} + +/// Restores the stack pointer from thread local storage. +static void RestoreStack(Xbyak::CodeGenerator& c) { + UNIMPLEMENTED(); +} + +#endif + +/// Switches to the patch stack, saves registers, and restores the original stack. static void SaveRegisters(Xbyak::CodeGenerator& c, const std::initializer_list regs) { - ASSERT_MSG(regs.size() <= MaxSavedRegisters, "Not enough space to save {} registers.", - regs.size()); - - std::call_once(register_save_init_flag, &InitializeRegisterSaveSlots); - - u32 index = 0; + SaveStack(c); for (const auto& reg : regs) { - const auto offset = reinterpret_cast(register_save_slots[index++] * sizeof(void*)); - - c.putSeg(gs); - c.mov(qword[offset], reg.cvt64()); + c.push(reg.cvt64()); } + RestoreStack(c); } +/// Switches to the patch stack, restores registers, and restores the original stack. static void RestoreRegisters(Xbyak::CodeGenerator& c, const std::initializer_list regs) { - ASSERT_MSG(regs.size() <= MaxSavedRegisters, "Not enough space to restore {} registers.", - regs.size()); - - std::call_once(register_save_init_flag, &InitializeRegisterSaveSlots); - - u32 index = 0; + SaveStack(c); for (const auto& reg : regs) { - const auto offset = reinterpret_cast(register_save_slots[index++] * sizeof(void*)); + c.pop(reg.cvt64()); + } + RestoreStack(c); +} - c.putSeg(gs); - c.mov(reg.cvt64(), qword[offset]); +/// Switches to the patch stack and stores all registers. +static void SaveContext(Xbyak::CodeGenerator& c) { + SaveStack(c); + for (int reg = Xbyak::Operand::RAX; reg <= Xbyak::Operand::R15; reg++) { + c.push(Xbyak::Reg64(reg)); + } + for (int reg = 0; reg <= 7; reg++) { + c.sub(rsp, 32); + c.vmovdqu(ptr[rsp], Xbyak::Ymm(reg)); } } +/// Restores all registers and restores the original stack. +/// If the destination is a register, it is not restored to preserve the output. +static void RestoreContext(Xbyak::CodeGenerator& c, const Xbyak::Operand& dst) { + for (int reg = 7; reg >= 0; reg--) { + if ((!dst.isXMM() && !dst.isYMM()) || dst.getIdx() != reg) { + c.vmovdqu(Xbyak::Ymm(reg), ptr[rsp]); + } + c.add(rsp, 32); + } + for (int reg = Xbyak::Operand::R15; reg >= Xbyak::Operand::RAX; reg--) { + if (!dst.isREG() || dst.getIdx() != reg) { + c.pop(Xbyak::Reg64(reg)); + } else { + c.add(rsp, 8); + } + } + RestoreStack(c); +} + +#ifdef __APPLE__ + static void GenerateANDN(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) { const auto dst = ZydisToXbyakRegisterOperand(operands[0]); const auto src1 = ZydisToXbyakRegisterOperand(operands[1]); @@ -204,9 +293,9 @@ static void GenerateBEXTR(const ZydisDecodedOperand* operands, Xbyak::CodeGenera c.and_(dst, scratch2); if (dst.getIdx() == shift.getIdx()) { - RestoreRegisters(c, {scratch1, scratch2}); + RestoreRegisters(c, {scratch2, scratch1}); } else { - RestoreRegisters(c, {scratch1, scratch2, shift}); + RestoreRegisters(c, {shift, scratch2, scratch1}); } } @@ -258,10 +347,138 @@ static void GenerateBLSR(const ZydisDecodedOperand* operands, Xbyak::CodeGenerat RestoreRegisters(c, {scratch}); } -bool FilterRosetta2Only(const ZydisDecodedOperand*) { +static __attribute__((sysv_abi)) void PerformVCVTPH2PS(float* out, const half_float::half* in, + const u32 count) { + for (u32 i = 0; i < count; i++) { + out[i] = half_float::half_cast(in[i]); + } +} + +static void GenerateVCVTPH2PS(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) { + const auto dst = ZydisToXbyakRegisterOperand(operands[0]); + const auto src = ZydisToXbyakOperand(operands[1]); + + const auto float_count = dst.getBit() / 32; + const auto byte_count = float_count * 4; + + SaveContext(c); + + // Allocate stack space for outputs and load into first parameter. + c.sub(rsp, byte_count); + c.mov(rdi, rsp); + + if (src->isXMM()) { + // Allocate stack space for inputs and load into second parameter. + c.sub(rsp, byte_count); + c.mov(rsi, rsp); + + // Move input to the allocated space. + c.movdqu(ptr[rsp], *reinterpret_cast(src.get())); + } else { + c.lea(rsi, src->getAddress()); + } + + // Load float count into third parameter. + c.mov(rdx, float_count); + + c.mov(rax, reinterpret_cast(PerformVCVTPH2PS)); + c.call(rax); + + if (src->isXMM()) { + // Clean up after inputs space. + c.add(rsp, byte_count); + } + + // Load outputs into destination register and clean up space. + if (dst.isYMM()) { + c.vmovdqu(*reinterpret_cast(&dst), ptr[rsp]); + } else { + c.movdqu(*reinterpret_cast(&dst), ptr[rsp]); + } + c.add(rsp, byte_count); + + RestoreContext(c, dst); +} + +using SingleToHalfFloatConverter = half_float::half (*)(float); +static const SingleToHalfFloatConverter SingleToHalfFloatConverters[4] = { + half_float::half_cast, + half_float::half_cast, + half_float::half_cast, + half_float::half_cast, +}; + +static __attribute__((sysv_abi)) void PerformVCVTPS2PH(half_float::half* out, const float* in, + const u32 count, const u8 rounding_mode) { + const auto conversion_func = SingleToHalfFloatConverters[rounding_mode]; + + for (u32 i = 0; i < count; i++) { + out[i] = conversion_func(in[i]); + } +} + +static void GenerateVCVTPS2PH(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) { + const auto dst = ZydisToXbyakOperand(operands[0]); + const auto src = ZydisToXbyakRegisterOperand(operands[1]); + const auto ctrl = ZydisToXbyakImmediateOperand(operands[2]); + + const auto float_count = src.getBit() / 32; + const auto byte_count = float_count * 4; + + SaveContext(c); + + if (dst->isXMM()) { + // Allocate stack space for outputs and load into first parameter. + c.sub(rsp, byte_count); + c.mov(rdi, rsp); + } else { + c.lea(rdi, dst->getAddress()); + } + + // Allocate stack space for inputs and load into second parameter. + c.sub(rsp, byte_count); + c.mov(rsi, rsp); + + // Move input to the allocated space. + if (src.isYMM()) { + c.vmovdqu(ptr[rsp], *reinterpret_cast(&src)); + } else { + c.movdqu(ptr[rsp], *reinterpret_cast(&src)); + } + + // Load float count into third parameter. + c.mov(rdx, float_count); + + // Load rounding mode into fourth parameter. + if (ctrl & 4) { + // Load from MXCSR.RC. + c.stmxcsr(ptr[rsp - 4]); + c.mov(rcx, ptr[rsp - 4]); + c.shr(rcx, 13); + c.and_(rcx, 3); + } else { + c.mov(rcx, ctrl & 3); + } + + c.mov(rax, reinterpret_cast(PerformVCVTPS2PH)); + c.call(rax); + + // Clean up after inputs space. + c.add(rsp, byte_count); + + if (dst->isXMM()) { + // Load outputs into destination register and clean up space. + c.movdqu(*reinterpret_cast(dst.get()), ptr[rsp]); + c.add(rsp, byte_count); + } + + RestoreContext(c, *dst); +} + +static bool FilterRosetta2Only(const ZydisDecodedOperand*) { int ret = 0; size_t size = sizeof(ret); - if (sysctlbyname("sysctl.proc_translated", &ret, &size, NULL, 0) != 0) { + if (sysctlbyname("sysctl.proc_translated", &ret, &size, nullptr, 0) != 0) { return false; } return ret; @@ -282,7 +499,6 @@ static bool FilterTcbAccess(const ZydisDecodedOperand* operands) { static void GenerateTcbAccess(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) { const auto dst = ZydisToXbyakRegisterOperand(operands[0]); - const auto slot = GetTcbKey(); #if defined(_WIN32) // The following logic is based on the Kernel32.dll asm of TlsGetValue @@ -290,6 +506,8 @@ static void GenerateTcbAccess(const ZydisDecodedOperand* operands, Xbyak::CodeGe static constexpr u32 TlsExpansionSlotsOffset = 0x1780; static constexpr u32 TlsMinimumAvailable = 64; + const auto slot = GetTcbKey(); + // Load the pointer to the table of TLS slots. c.putSeg(gs); if (slot < TlsMinimumAvailable) { @@ -303,11 +521,6 @@ static void GenerateTcbAccess(const ZydisDecodedOperand* operands, Xbyak::CodeGe // Load the pointer to our buffer. c.mov(dst, qword[dst + tls_index * sizeof(LPVOID)]); } -#elif defined(__APPLE__) - // The following logic is based on the Darwin implementation of _os_tsd_get_direct, used by - // pthread_getspecific https://github.com/apple/darwin-xnu/blob/main/libsyscall/os/tsd.h#L89-L96 - c.putSeg(gs); - c.mov(dst, qword[reinterpret_cast(slot * sizeof(void*))]); #else const auto src = ZydisToXbyakMemoryOperand(operands[1]); @@ -331,20 +544,24 @@ struct PatchInfo { }; static const std::unordered_map Patches = { -#if defined(_WIN32) || defined(__APPLE__) - // Windows and Apple need a trampoline. +#if defined(_WIN32) + // Windows needs a trampoline. {ZYDIS_MNEMONIC_MOV, {FilterTcbAccess, GenerateTcbAccess, true}}, -#else +#elif !defined(__APPLE__) {ZYDIS_MNEMONIC_MOV, {FilterTcbAccess, GenerateTcbAccess, false}}, #endif #ifdef __APPLE__ - // BMI1 instructions that are not supported by Rosetta 2 on Apple Silicon. + // Patches for instruction sets not supported by Rosetta 2. + // BMI1 {ZYDIS_MNEMONIC_ANDN, {FilterRosetta2Only, GenerateANDN, true}}, {ZYDIS_MNEMONIC_BEXTR, {FilterRosetta2Only, GenerateBEXTR, true}}, {ZYDIS_MNEMONIC_BLSI, {FilterRosetta2Only, GenerateBLSI, true}}, {ZYDIS_MNEMONIC_BLSMSK, {FilterRosetta2Only, GenerateBLSMSK, true}}, {ZYDIS_MNEMONIC_BLSR, {FilterRosetta2Only, GenerateBLSR, true}}, + // F16C + {ZYDIS_MNEMONIC_VCVTPH2PS, {FilterRosetta2Only, GenerateVCVTPH2PS, true}}, + {ZYDIS_MNEMONIC_VCVTPS2PH, {FilterRosetta2Only, GenerateVCVTPS2PH, true}}, #endif }; diff --git a/src/core/cpu_patches.h b/src/core/cpu_patches.h index 45adbeda3..9126074ed 100644 --- a/src/core/cpu_patches.h +++ b/src/core/cpu_patches.h @@ -9,6 +9,12 @@ class CodeGenerator; namespace Core { +/// Initializes a stack for the current thread for use by patch implementations. +void InitializeThreadPatchStack(); + +/// Cleans up the patch stack for the current thread. +void CleanupThreadPatchStack(); + /// Patches CPU instructions that cannot run as-is on the host. void PatchInstructions(u64 segment_addr, u64 segment_size, Xbyak::CodeGenerator& c); diff --git a/src/core/crypto/crypto.cpp b/src/core/crypto/crypto.cpp index 630faa344..aa1c96724 100644 --- a/src/core/crypto/crypto.cpp +++ b/src/core/crypto/crypto.cpp @@ -6,18 +6,18 @@ CryptoPP::RSA::PrivateKey Crypto::key_pkg_derived_key3_keyset_init() { CryptoPP::InvertibleRSAFunction params; - params.SetPrime1(CryptoPP::Integer(pkg_derived_key3_keyset.Prime1, 0x80)); - params.SetPrime2(CryptoPP::Integer(pkg_derived_key3_keyset.Prime2, 0x80)); + params.SetPrime1(CryptoPP::Integer(PkgDerivedKey3Keyset::Prime1, 0x80)); + params.SetPrime2(CryptoPP::Integer(PkgDerivedKey3Keyset::Prime2, 0x80)); - params.SetPublicExponent(CryptoPP::Integer(pkg_derived_key3_keyset.PublicExponent, 4)); - params.SetPrivateExponent(CryptoPP::Integer(pkg_derived_key3_keyset.PrivateExponent, 0x100)); + params.SetPublicExponent(CryptoPP::Integer(PkgDerivedKey3Keyset::PublicExponent, 4)); + params.SetPrivateExponent(CryptoPP::Integer(PkgDerivedKey3Keyset::PrivateExponent, 0x100)); - params.SetModPrime1PrivateExponent(CryptoPP::Integer(pkg_derived_key3_keyset.Exponent1, 0x80)); - params.SetModPrime2PrivateExponent(CryptoPP::Integer(pkg_derived_key3_keyset.Exponent2, 0x80)); + params.SetModPrime1PrivateExponent(CryptoPP::Integer(PkgDerivedKey3Keyset::Exponent1, 0x80)); + params.SetModPrime2PrivateExponent(CryptoPP::Integer(PkgDerivedKey3Keyset::Exponent2, 0x80)); - params.SetModulus(CryptoPP::Integer(pkg_derived_key3_keyset.Modulus, 0x100)); + params.SetModulus(CryptoPP::Integer(PkgDerivedKey3Keyset::Modulus, 0x100)); params.SetMultiplicativeInverseOfPrime2ModPrime1( - CryptoPP::Integer(pkg_derived_key3_keyset.Coefficient, 0x80)); + CryptoPP::Integer(PkgDerivedKey3Keyset::Coefficient, 0x80)); CryptoPP::RSA::PrivateKey privateKey(params); @@ -26,18 +26,18 @@ CryptoPP::RSA::PrivateKey Crypto::key_pkg_derived_key3_keyset_init() { CryptoPP::RSA::PrivateKey Crypto::FakeKeyset_keyset_init() { CryptoPP::InvertibleRSAFunction params; - params.SetPrime1(CryptoPP::Integer(FakeKeyset_keyset.Prime1, 0x80)); - params.SetPrime2(CryptoPP::Integer(FakeKeyset_keyset.Prime2, 0x80)); + params.SetPrime1(CryptoPP::Integer(FakeKeyset::Prime1, 0x80)); + params.SetPrime2(CryptoPP::Integer(FakeKeyset::Prime2, 0x80)); - params.SetPublicExponent(CryptoPP::Integer(FakeKeyset_keyset.PublicExponent, 4)); - params.SetPrivateExponent(CryptoPP::Integer(FakeKeyset_keyset.PrivateExponent, 0x100)); + params.SetPublicExponent(CryptoPP::Integer(FakeKeyset::PublicExponent, 4)); + params.SetPrivateExponent(CryptoPP::Integer(FakeKeyset::PrivateExponent, 0x100)); - params.SetModPrime1PrivateExponent(CryptoPP::Integer(FakeKeyset_keyset.Exponent1, 0x80)); - params.SetModPrime2PrivateExponent(CryptoPP::Integer(FakeKeyset_keyset.Exponent2, 0x80)); + params.SetModPrime1PrivateExponent(CryptoPP::Integer(FakeKeyset::Exponent1, 0x80)); + params.SetModPrime2PrivateExponent(CryptoPP::Integer(FakeKeyset::Exponent2, 0x80)); - params.SetModulus(CryptoPP::Integer(FakeKeyset_keyset.Modulus, 0x100)); + params.SetModulus(CryptoPP::Integer(FakeKeyset::Modulus, 0x100)); params.SetMultiplicativeInverseOfPrime2ModPrime1( - CryptoPP::Integer(FakeKeyset_keyset.Coefficient, 0x80)); + CryptoPP::Integer(FakeKeyset::Coefficient, 0x80)); CryptoPP::RSA::PrivateKey privateKey(params); @@ -46,25 +46,22 @@ CryptoPP::RSA::PrivateKey Crypto::FakeKeyset_keyset_init() { CryptoPP::RSA::PrivateKey Crypto::DebugRifKeyset_init() { CryptoPP::InvertibleRSAFunction params; - params.SetPrime1( - CryptoPP::Integer(DebugRifKeyset_keyset.Prime1, sizeof(DebugRifKeyset_keyset.Prime1))); - params.SetPrime2( - CryptoPP::Integer(DebugRifKeyset_keyset.Prime2, sizeof(DebugRifKeyset_keyset.Prime2))); + params.SetPrime1(CryptoPP::Integer(DebugRifKeyset::Prime1, sizeof(DebugRifKeyset::Prime1))); + params.SetPrime2(CryptoPP::Integer(DebugRifKeyset::Prime2, sizeof(DebugRifKeyset::Prime2))); - params.SetPublicExponent(CryptoPP::Integer(DebugRifKeyset_keyset.PrivateExponent, - sizeof(DebugRifKeyset_keyset.PrivateExponent))); - params.SetPrivateExponent(CryptoPP::Integer(DebugRifKeyset_keyset.PrivateExponent, - sizeof(DebugRifKeyset_keyset.PrivateExponent))); + params.SetPublicExponent( + CryptoPP::Integer(DebugRifKeyset::PublicExponent, sizeof(DebugRifKeyset::PublicExponent))); + params.SetPrivateExponent(CryptoPP::Integer(DebugRifKeyset::PrivateExponent, + sizeof(DebugRifKeyset::PrivateExponent))); - params.SetModPrime1PrivateExponent(CryptoPP::Integer(DebugRifKeyset_keyset.Exponent1, - sizeof(DebugRifKeyset_keyset.Exponent1))); - params.SetModPrime2PrivateExponent(CryptoPP::Integer(DebugRifKeyset_keyset.Exponent2, - sizeof(DebugRifKeyset_keyset.Exponent2))); + params.SetModPrime1PrivateExponent( + CryptoPP::Integer(DebugRifKeyset::Exponent1, sizeof(DebugRifKeyset::Exponent1))); + params.SetModPrime2PrivateExponent( + CryptoPP::Integer(DebugRifKeyset::Exponent2, sizeof(DebugRifKeyset::Exponent2))); - params.SetModulus( - CryptoPP::Integer(DebugRifKeyset_keyset.Modulus, sizeof(DebugRifKeyset_keyset.Modulus))); - params.SetMultiplicativeInverseOfPrime2ModPrime1(CryptoPP::Integer( - DebugRifKeyset_keyset.Coefficient, sizeof(DebugRifKeyset_keyset.Coefficient))); + params.SetModulus(CryptoPP::Integer(DebugRifKeyset::Modulus, sizeof(DebugRifKeyset::Modulus))); + params.SetMultiplicativeInverseOfPrime2ModPrime1( + CryptoPP::Integer(DebugRifKeyset::Coefficient, sizeof(DebugRifKeyset::Coefficient))); CryptoPP::RSA::PrivateKey privateKey(params); diff --git a/src/core/crypto/crypto.h b/src/core/crypto/crypto.h index 0c5bc9955..83249bd7d 100644 --- a/src/core/crypto/crypto.h +++ b/src/core/crypto/crypto.h @@ -17,10 +17,6 @@ class Crypto { public: - PkgDerivedKey3Keyset pkg_derived_key3_keyset; - FakeKeyset FakeKeyset_keyset; - DebugRifKeyset DebugRifKeyset_keyset; - CryptoPP::RSA::PrivateKey key_pkg_derived_key3_keyset_init(); CryptoPP::RSA::PrivateKey FakeKeyset_keyset_init(); CryptoPP::RSA::PrivateKey DebugRifKeyset_init(); diff --git a/src/core/crypto/keys.h b/src/core/crypto/keys.h index 37b66bd09..441082481 100644 --- a/src/core/crypto/keys.h +++ b/src/core/crypto/keys.h @@ -7,384 +7,299 @@ class FakeKeyset { public: // Constructor - const CryptoPP::byte* Exponent1; + static constexpr CryptoPP::byte Exponent1[] = { + 0x6D, 0x48, 0xE0, 0x54, 0x40, 0x25, 0xC8, 0x41, 0x29, 0x52, 0x42, 0x27, 0xEB, 0xD2, 0xC7, + 0xAB, 0x6B, 0x9C, 0x27, 0x0A, 0xB4, 0x1F, 0x94, 0x4E, 0xFA, 0x42, 0x1D, 0xB7, 0xBC, 0xB9, + 0xAE, 0xBC, 0x04, 0x6F, 0x75, 0x8F, 0x10, 0x5F, 0x89, 0xAC, 0xAB, 0x9C, 0xD2, 0xFA, 0xE6, + 0xA4, 0x13, 0x83, 0x68, 0xD4, 0x56, 0x38, 0xFE, 0xE5, 0x2B, 0x78, 0x44, 0x9C, 0x34, 0xE6, + 0x5A, 0xA0, 0xBE, 0x05, 0x70, 0xAD, 0x15, 0xC3, 0x2D, 0x31, 0xAC, 0x97, 0x5D, 0x88, 0xFC, + 0xC1, 0x62, 0x3D, 0xE2, 0xED, 0x11, 0xDB, 0xB6, 0x9E, 0xFC, 0x5A, 0x5A, 0x03, 0xF6, 0xCF, + 0x08, 0xD4, 0x5D, 0x90, 0xC9, 0x2A, 0xB9, 0x9B, 0xCF, 0xC8, 0x1A, 0x65, 0xF3, 0x5B, 0xE8, + 0x7F, 0xCF, 0xA5, 0xA6, 0x4C, 0x5C, 0x2A, 0x12, 0x0F, 0x92, 0xA5, 0xE3, 0xF0, 0x17, 0x1E, + 0x9A, 0x97, 0x45, 0x86, 0xFD, 0xDB, 0x54, 0x25}; // exponent2 = d mod (q - 1) - const CryptoPP::byte* Exponent2; + static constexpr CryptoPP::byte Exponent2[] = { + 0x2A, 0x51, 0xCE, 0x02, 0x44, 0x28, 0x50, 0xE8, 0x30, 0x20, 0x7C, 0x9C, 0x55, 0xBF, 0x60, + 0x39, 0xBC, 0xD1, 0xF0, 0xE7, 0x68, 0xF8, 0x08, 0x5B, 0x61, 0x1F, 0xA7, 0xBF, 0xD0, 0xE8, + 0x8B, 0xB5, 0xB1, 0xD5, 0xD9, 0x16, 0xAC, 0x75, 0x0C, 0x6D, 0xF2, 0xE0, 0xB5, 0x97, 0x75, + 0xD2, 0x68, 0x16, 0x1F, 0x00, 0x7D, 0x8B, 0x17, 0xE8, 0x78, 0x48, 0x41, 0x71, 0x2B, 0x18, + 0x96, 0x80, 0x11, 0xDB, 0x68, 0x39, 0x9C, 0xD6, 0xE0, 0x72, 0x42, 0x86, 0xF0, 0x1B, 0x16, + 0x0D, 0x3E, 0x12, 0x94, 0x3D, 0x25, 0xA8, 0xA9, 0x30, 0x9E, 0x54, 0x5A, 0xD6, 0x36, 0x6C, + 0xD6, 0x8C, 0x20, 0x62, 0x8F, 0xA1, 0x6B, 0x1F, 0x7C, 0x6D, 0xB2, 0xB1, 0xC1, 0x2E, 0xAD, + 0x36, 0x02, 0x9C, 0x3A, 0xCA, 0x2F, 0x09, 0xD2, 0x45, 0x9E, 0xEB, 0xF2, 0xBC, 0x6C, 0xAA, + 0x3B, 0x3E, 0x90, 0xBC, 0x38, 0x67, 0x35, 0x4D}; // e - const CryptoPP::byte* PublicExponent; + static constexpr CryptoPP::byte PublicExponent[] = {0, 1, 0, 1}; // (InverseQ)(q) = 1 mod p - const CryptoPP::byte* Coefficient; + static constexpr CryptoPP::byte Coefficient[] = { + 0x0B, 0x67, 0x1C, 0x0D, 0x6C, 0x57, 0xD3, 0xE7, 0x05, 0x65, 0x94, 0x31, 0x56, 0x55, 0xFD, + 0x28, 0x08, 0xFA, 0x05, 0x8A, 0xCC, 0x55, 0x39, 0x61, 0x97, 0x63, 0xA0, 0x16, 0x27, 0x3D, + 0xED, 0xC1, 0x16, 0x40, 0x2A, 0x12, 0xEA, 0x6F, 0xD9, 0xD8, 0x58, 0x56, 0xA8, 0x56, 0x8B, + 0x0D, 0x38, 0x5E, 0x1E, 0x80, 0x3B, 0x5F, 0x40, 0x80, 0x6F, 0x62, 0x4F, 0x28, 0xA2, 0x69, + 0xF3, 0xD3, 0xF7, 0xFD, 0xB2, 0xC3, 0x52, 0x43, 0x20, 0x92, 0x9D, 0x97, 0x8D, 0xA0, 0x15, + 0x07, 0x15, 0x6E, 0xA4, 0x0D, 0x56, 0xD3, 0x37, 0x1A, 0xC4, 0x9E, 0xDF, 0x02, 0x49, 0xB8, + 0x0A, 0x84, 0x62, 0xF5, 0xFA, 0xB9, 0x3F, 0xA4, 0x09, 0x76, 0xCC, 0xAA, 0xB9, 0x9B, 0xA6, + 0x4F, 0xC1, 0x6A, 0x64, 0xCE, 0xD8, 0x77, 0xAB, 0x4B, 0xF9, 0xA0, 0xAE, 0xDA, 0xF1, 0x67, + 0x87, 0x7C, 0x98, 0x5C, 0x7E, 0xB8, 0x73, 0xF5}; // n = p * q - const CryptoPP::byte* Modulus; + static constexpr CryptoPP::byte Modulus[] = { + 0xC6, 0xCF, 0x71, 0xE7, 0xE5, 0x9A, 0xF0, 0xD1, 0x2A, 0x2C, 0x45, 0x8B, 0xF9, 0x2A, 0x0E, + 0xC1, 0x43, 0x05, 0x8B, 0xC3, 0x71, 0x17, 0x80, 0x1D, 0xCD, 0x49, 0x7D, 0xDE, 0x35, 0x9D, + 0x25, 0x9B, 0xA0, 0xD7, 0xA0, 0xF2, 0x7D, 0x6C, 0x08, 0x7E, 0xAA, 0x55, 0x02, 0x68, 0x2B, + 0x23, 0xC6, 0x44, 0xB8, 0x44, 0x18, 0xEB, 0x56, 0xCF, 0x16, 0xA2, 0x48, 0x03, 0xC9, 0xE7, + 0x4F, 0x87, 0xEB, 0x3D, 0x30, 0xC3, 0x15, 0x88, 0xBF, 0x20, 0xE7, 0x9D, 0xFF, 0x77, 0x0C, + 0xDE, 0x1D, 0x24, 0x1E, 0x63, 0xA9, 0x4F, 0x8A, 0xBF, 0x5B, 0xBE, 0x60, 0x19, 0x68, 0x33, + 0x3B, 0xFC, 0xED, 0x9F, 0x47, 0x4E, 0x5F, 0xF8, 0xEA, 0xCB, 0x3D, 0x00, 0xBD, 0x67, 0x01, + 0xF9, 0x2C, 0x6D, 0xC6, 0xAC, 0x13, 0x64, 0xE7, 0x67, 0x14, 0xF3, 0xDC, 0x52, 0x69, 0x6A, + 0xB9, 0x83, 0x2C, 0x42, 0x30, 0x13, 0x1B, 0xB2, 0xD8, 0xA5, 0x02, 0x0D, 0x79, 0xED, 0x96, + 0xB1, 0x0D, 0xF8, 0xCC, 0x0C, 0xDF, 0x81, 0x95, 0x4F, 0x03, 0x58, 0x09, 0x57, 0x0E, 0x80, + 0x69, 0x2E, 0xFE, 0xFF, 0x52, 0x77, 0xEA, 0x75, 0x28, 0xA8, 0xFB, 0xC9, 0xBE, 0xBF, 0x9F, + 0xBB, 0xB7, 0x79, 0x8E, 0x18, 0x05, 0xE1, 0x80, 0xBD, 0x50, 0x34, 0x94, 0x81, 0xD3, 0x53, + 0xC2, 0x69, 0xA2, 0xD2, 0x4C, 0xCF, 0x6C, 0xF4, 0x57, 0x2C, 0x10, 0x4A, 0x3F, 0xFB, 0x22, + 0xFD, 0x8B, 0x97, 0xE2, 0xC9, 0x5B, 0xA6, 0x2B, 0xCD, 0xD6, 0x1B, 0x6B, 0xDB, 0x68, 0x7F, + 0x4B, 0xC2, 0xA0, 0x50, 0x34, 0xC0, 0x05, 0xE5, 0x8D, 0xEF, 0x24, 0x67, 0xFF, 0x93, 0x40, + 0xCF, 0x2D, 0x62, 0xA2, 0xA0, 0x50, 0xB1, 0xF1, 0x3A, 0xA8, 0x3D, 0xFD, 0x80, 0xD1, 0xF9, + 0xB8, 0x05, 0x22, 0xAF, 0xC8, 0x35, 0x45, 0x90, 0x58, 0x8E, 0xE3, 0x3A, 0x7C, 0xBD, 0x3E, + 0x27}; // p - const CryptoPP::byte* Prime1; + static constexpr CryptoPP::byte Prime1[] = { + 0xFE, 0xF6, 0xBF, 0x1D, 0x69, 0xAB, 0x16, 0x25, 0x08, 0x47, 0x55, 0x6B, 0x86, 0xE4, 0x35, + 0x88, 0x72, 0x2A, 0xB1, 0x3D, 0xF8, 0xB6, 0x44, 0xCA, 0xB3, 0xAB, 0x19, 0xD1, 0x04, 0x24, + 0x28, 0x0A, 0x74, 0x55, 0xB8, 0x15, 0x45, 0x09, 0xCC, 0x13, 0x1C, 0xF2, 0xBA, 0x37, 0xA9, + 0x03, 0x90, 0x8F, 0x02, 0x10, 0xFF, 0x25, 0x79, 0x86, 0xCC, 0x18, 0x50, 0x9A, 0x10, 0x5F, + 0x5B, 0x4C, 0x1C, 0x4E, 0xB0, 0xA7, 0xE3, 0x59, 0xB1, 0x2D, 0xA0, 0xC6, 0xB0, 0x20, 0x2C, + 0x21, 0x33, 0x12, 0xB3, 0xAF, 0x72, 0x34, 0x83, 0xCD, 0x52, 0x2F, 0xAF, 0x0F, 0x20, 0x5A, + 0x1B, 0xC0, 0xE2, 0xA3, 0x76, 0x34, 0x0F, 0xD7, 0xFC, 0xC1, 0x41, 0xC9, 0xF9, 0x79, 0x40, + 0x17, 0x42, 0x21, 0x3E, 0x9D, 0xFD, 0xC7, 0xC1, 0x50, 0xDE, 0x44, 0x5A, 0xC9, 0x31, 0x89, + 0x6A, 0x78, 0x05, 0xBE, 0x65, 0xB4, 0xE8, 0x2D}; // q - const CryptoPP::byte* Prime2; - const CryptoPP::byte* PrivateExponent; - - // Constructor - FakeKeyset() { - // Initialize PrivateExponent - PrivateExponent = new CryptoPP::byte[0x100]{ - 0x7F, 0x76, 0xCD, 0x0E, 0xE2, 0xD4, 0xDE, 0x05, 0x1C, 0xC6, 0xD9, 0xA8, 0x0E, 0x8D, - 0xFA, 0x7B, 0xCA, 0x1E, 0xAA, 0x27, 0x1A, 0x40, 0xF8, 0xF1, 0x22, 0x87, 0x35, 0xDD, - 0xDB, 0xFD, 0xEE, 0xF8, 0xC2, 0xBC, 0xBD, 0x01, 0xFB, 0x8B, 0xE2, 0x3E, 0x63, 0xB2, - 0xB1, 0x22, 0x5C, 0x56, 0x49, 0x6E, 0x11, 0xBE, 0x07, 0x44, 0x0B, 0x9A, 0x26, 0x66, - 0xD1, 0x49, 0x2C, 0x8F, 0xD3, 0x1B, 0xCF, 0xA4, 0xA1, 0xB8, 0xD1, 0xFB, 0xA4, 0x9E, - 0xD2, 0x21, 0x28, 0x83, 0x09, 0x8A, 0xF6, 0xA0, 0x0B, 0xA3, 0xD6, 0x0F, 0x9B, 0x63, - 0x68, 0xCC, 0xBC, 0x0C, 0x4E, 0x14, 0x5B, 0x27, 0xA4, 0xA9, 0xF4, 0x2B, 0xB9, 0xB8, - 0x7B, 0xC0, 0xE6, 0x51, 0xAD, 0x1D, 0x77, 0xD4, 0x6B, 0xB9, 0xCE, 0x20, 0xD1, 0x26, - 0x66, 0x7E, 0x5E, 0x9E, 0xA2, 0xE9, 0x6B, 0x90, 0xF3, 0x73, 0xB8, 0x52, 0x8F, 0x44, - 0x11, 0x03, 0x0C, 0x13, 0x97, 0x39, 0x3D, 0x13, 0x22, 0x58, 0xD5, 0x43, 0x82, 0x49, - 0xDA, 0x6E, 0x7C, 0xA1, 0xC5, 0x8C, 0xA5, 0xB0, 0x09, 0xE0, 0xCE, 0x3D, 0xDF, 0xF4, - 0x9D, 0x3C, 0x97, 0x15, 0xE2, 0x6A, 0xC7, 0x2B, 0x3C, 0x50, 0x93, 0x23, 0xDB, 0xBA, - 0x4A, 0x22, 0x66, 0x44, 0xAC, 0x78, 0xBB, 0x0E, 0x1A, 0x27, 0x43, 0xB5, 0x71, 0x67, - 0xAF, 0xF4, 0xAB, 0x48, 0x46, 0x93, 0x73, 0xD0, 0x42, 0xAB, 0x93, 0x63, 0xE5, 0x6C, - 0x9A, 0xDE, 0x50, 0x24, 0xC0, 0x23, 0x7D, 0x99, 0x79, 0x3F, 0x22, 0x07, 0xE0, 0xC1, - 0x48, 0x56, 0x1B, 0xDF, 0x83, 0x09, 0x12, 0xB4, 0x2D, 0x45, 0x6B, 0xC9, 0xC0, 0x68, - 0x85, 0x99, 0x90, 0x79, 0x96, 0x1A, 0xD7, 0xF5, 0x4D, 0x1F, 0x37, 0x83, 0x40, 0x4A, - 0xEC, 0x39, 0x37, 0xA6, 0x80, 0x92, 0x7D, 0xC5, 0x80, 0xC7, 0xD6, 0x6F, 0xFE, 0x8A, - 0x79, 0x89, 0xC6, 0xB1}; - - // Initialize Exponent1 - Exponent1 = new CryptoPP::byte[0x80]{ - 0x6D, 0x48, 0xE0, 0x54, 0x40, 0x25, 0xC8, 0x41, 0x29, 0x52, 0x42, 0x27, 0xEB, - 0xD2, 0xC7, 0xAB, 0x6B, 0x9C, 0x27, 0x0A, 0xB4, 0x1F, 0x94, 0x4E, 0xFA, 0x42, - 0x1D, 0xB7, 0xBC, 0xB9, 0xAE, 0xBC, 0x04, 0x6F, 0x75, 0x8F, 0x10, 0x5F, 0x89, - 0xAC, 0xAB, 0x9C, 0xD2, 0xFA, 0xE6, 0xA4, 0x13, 0x83, 0x68, 0xD4, 0x56, 0x38, - 0xFE, 0xE5, 0x2B, 0x78, 0x44, 0x9C, 0x34, 0xE6, 0x5A, 0xA0, 0xBE, 0x05, 0x70, - 0xAD, 0x15, 0xC3, 0x2D, 0x31, 0xAC, 0x97, 0x5D, 0x88, 0xFC, 0xC1, 0x62, 0x3D, - 0xE2, 0xED, 0x11, 0xDB, 0xB6, 0x9E, 0xFC, 0x5A, 0x5A, 0x03, 0xF6, 0xCF, 0x08, - 0xD4, 0x5D, 0x90, 0xC9, 0x2A, 0xB9, 0x9B, 0xCF, 0xC8, 0x1A, 0x65, 0xF3, 0x5B, - 0xE8, 0x7F, 0xCF, 0xA5, 0xA6, 0x4C, 0x5C, 0x2A, 0x12, 0x0F, 0x92, 0xA5, 0xE3, - 0xF0, 0x17, 0x1E, 0x9A, 0x97, 0x45, 0x86, 0xFD, 0xDB, 0x54, 0x25 - - }; - - Exponent2 = new CryptoPP::byte[0x80]{ - 0x2A, 0x51, 0xCE, 0x02, 0x44, 0x28, 0x50, 0xE8, 0x30, 0x20, 0x7C, 0x9C, 0x55, - 0xBF, 0x60, 0x39, 0xBC, 0xD1, 0xF0, 0xE7, 0x68, 0xF8, 0x08, 0x5B, 0x61, 0x1F, - 0xA7, 0xBF, 0xD0, 0xE8, 0x8B, 0xB5, 0xB1, 0xD5, 0xD9, 0x16, 0xAC, 0x75, 0x0C, - 0x6D, 0xF2, 0xE0, 0xB5, 0x97, 0x75, 0xD2, 0x68, 0x16, 0x1F, 0x00, 0x7D, 0x8B, - 0x17, 0xE8, 0x78, 0x48, 0x41, 0x71, 0x2B, 0x18, 0x96, 0x80, 0x11, 0xDB, 0x68, - 0x39, 0x9C, 0xD6, 0xE0, 0x72, 0x42, 0x86, 0xF0, 0x1B, 0x16, 0x0D, 0x3E, 0x12, - 0x94, 0x3D, 0x25, 0xA8, 0xA9, 0x30, 0x9E, 0x54, 0x5A, 0xD6, 0x36, 0x6C, 0xD6, - 0x8C, 0x20, 0x62, 0x8F, 0xA1, 0x6B, 0x1F, 0x7C, 0x6D, 0xB2, 0xB1, 0xC1, 0x2E, - 0xAD, 0x36, 0x02, 0x9C, 0x3A, 0xCA, 0x2F, 0x09, 0xD2, 0x45, 0x9E, 0xEB, 0xF2, - 0xBC, 0x6C, 0xAA, 0x3B, 0x3E, 0x90, 0xBC, 0x38, 0x67, 0x35, 0x4D}; - - PublicExponent = new CryptoPP::byte[4]{0, 1, 0, 1}; - - Coefficient = new CryptoPP::byte[0x80]{ - 0x0B, 0x67, 0x1C, 0x0D, 0x6C, 0x57, 0xD3, 0xE7, 0x05, 0x65, 0x94, 0x31, 0x56, - 0x55, 0xFD, 0x28, 0x08, 0xFA, 0x05, 0x8A, 0xCC, 0x55, 0x39, 0x61, 0x97, 0x63, - 0xA0, 0x16, 0x27, 0x3D, 0xED, 0xC1, 0x16, 0x40, 0x2A, 0x12, 0xEA, 0x6F, 0xD9, - 0xD8, 0x58, 0x56, 0xA8, 0x56, 0x8B, 0x0D, 0x38, 0x5E, 0x1E, 0x80, 0x3B, 0x5F, - 0x40, 0x80, 0x6F, 0x62, 0x4F, 0x28, 0xA2, 0x69, 0xF3, 0xD3, 0xF7, 0xFD, 0xB2, - 0xC3, 0x52, 0x43, 0x20, 0x92, 0x9D, 0x97, 0x8D, 0xA0, 0x15, 0x07, 0x15, 0x6E, - 0xA4, 0x0D, 0x56, 0xD3, 0x37, 0x1A, 0xC4, 0x9E, 0xDF, 0x02, 0x49, 0xB8, 0x0A, - 0x84, 0x62, 0xF5, 0xFA, 0xB9, 0x3F, 0xA4, 0x09, 0x76, 0xCC, 0xAA, 0xB9, 0x9B, - 0xA6, 0x4F, 0xC1, 0x6A, 0x64, 0xCE, 0xD8, 0x77, 0xAB, 0x4B, 0xF9, 0xA0, 0xAE, - 0xDA, 0xF1, 0x67, 0x87, 0x7C, 0x98, 0x5C, 0x7E, 0xB8, 0x73, 0xF5}; - - Modulus = new CryptoPP::byte[0x100]{ - 0xC6, 0xCF, 0x71, 0xE7, 0xE5, 0x9A, 0xF0, 0xD1, 0x2A, 0x2C, 0x45, 0x8B, 0xF9, 0x2A, - 0x0E, 0xC1, 0x43, 0x05, 0x8B, 0xC3, 0x71, 0x17, 0x80, 0x1D, 0xCD, 0x49, 0x7D, 0xDE, - 0x35, 0x9D, 0x25, 0x9B, 0xA0, 0xD7, 0xA0, 0xF2, 0x7D, 0x6C, 0x08, 0x7E, 0xAA, 0x55, - 0x02, 0x68, 0x2B, 0x23, 0xC6, 0x44, 0xB8, 0x44, 0x18, 0xEB, 0x56, 0xCF, 0x16, 0xA2, - 0x48, 0x03, 0xC9, 0xE7, 0x4F, 0x87, 0xEB, 0x3D, 0x30, 0xC3, 0x15, 0x88, 0xBF, 0x20, - 0xE7, 0x9D, 0xFF, 0x77, 0x0C, 0xDE, 0x1D, 0x24, 0x1E, 0x63, 0xA9, 0x4F, 0x8A, 0xBF, - 0x5B, 0xBE, 0x60, 0x19, 0x68, 0x33, 0x3B, 0xFC, 0xED, 0x9F, 0x47, 0x4E, 0x5F, 0xF8, - 0xEA, 0xCB, 0x3D, 0x00, 0xBD, 0x67, 0x01, 0xF9, 0x2C, 0x6D, 0xC6, 0xAC, 0x13, 0x64, - 0xE7, 0x67, 0x14, 0xF3, 0xDC, 0x52, 0x69, 0x6A, 0xB9, 0x83, 0x2C, 0x42, 0x30, 0x13, - 0x1B, 0xB2, 0xD8, 0xA5, 0x02, 0x0D, 0x79, 0xED, 0x96, 0xB1, 0x0D, 0xF8, 0xCC, 0x0C, - 0xDF, 0x81, 0x95, 0x4F, 0x03, 0x58, 0x09, 0x57, 0x0E, 0x80, 0x69, 0x2E, 0xFE, 0xFF, - 0x52, 0x77, 0xEA, 0x75, 0x28, 0xA8, 0xFB, 0xC9, 0xBE, 0xBF, 0x9F, 0xBB, 0xB7, 0x79, - 0x8E, 0x18, 0x05, 0xE1, 0x80, 0xBD, 0x50, 0x34, 0x94, 0x81, 0xD3, 0x53, 0xC2, 0x69, - 0xA2, 0xD2, 0x4C, 0xCF, 0x6C, 0xF4, 0x57, 0x2C, 0x10, 0x4A, 0x3F, 0xFB, 0x22, 0xFD, - 0x8B, 0x97, 0xE2, 0xC9, 0x5B, 0xA6, 0x2B, 0xCD, 0xD6, 0x1B, 0x6B, 0xDB, 0x68, 0x7F, - 0x4B, 0xC2, 0xA0, 0x50, 0x34, 0xC0, 0x05, 0xE5, 0x8D, 0xEF, 0x24, 0x67, 0xFF, 0x93, - 0x40, 0xCF, 0x2D, 0x62, 0xA2, 0xA0, 0x50, 0xB1, 0xF1, 0x3A, 0xA8, 0x3D, 0xFD, 0x80, - 0xD1, 0xF9, 0xB8, 0x05, 0x22, 0xAF, 0xC8, 0x35, 0x45, 0x90, 0x58, 0x8E, 0xE3, 0x3A, - 0x7C, 0xBD, 0x3E, 0x27}; - - Prime1 = new CryptoPP::byte[0x80]{ - 0xFE, 0xF6, 0xBF, 0x1D, 0x69, 0xAB, 0x16, 0x25, 0x08, 0x47, 0x55, 0x6B, 0x86, - 0xE4, 0x35, 0x88, 0x72, 0x2A, 0xB1, 0x3D, 0xF8, 0xB6, 0x44, 0xCA, 0xB3, 0xAB, - 0x19, 0xD1, 0x04, 0x24, 0x28, 0x0A, 0x74, 0x55, 0xB8, 0x15, 0x45, 0x09, 0xCC, - 0x13, 0x1C, 0xF2, 0xBA, 0x37, 0xA9, 0x03, 0x90, 0x8F, 0x02, 0x10, 0xFF, 0x25, - 0x79, 0x86, 0xCC, 0x18, 0x50, 0x9A, 0x10, 0x5F, 0x5B, 0x4C, 0x1C, 0x4E, 0xB0, - 0xA7, 0xE3, 0x59, 0xB1, 0x2D, 0xA0, 0xC6, 0xB0, 0x20, 0x2C, 0x21, 0x33, 0x12, - 0xB3, 0xAF, 0x72, 0x34, 0x83, 0xCD, 0x52, 0x2F, 0xAF, 0x0F, 0x20, 0x5A, 0x1B, - 0xC0, 0xE2, 0xA3, 0x76, 0x34, 0x0F, 0xD7, 0xFC, 0xC1, 0x41, 0xC9, 0xF9, 0x79, - 0x40, 0x17, 0x42, 0x21, 0x3E, 0x9D, 0xFD, 0xC7, 0xC1, 0x50, 0xDE, 0x44, 0x5A, - 0xC9, 0x31, 0x89, 0x6A, 0x78, 0x05, 0xBE, 0x65, 0xB4, 0xE8, 0x2D}; - - Prime2 = new CryptoPP::byte[0x80]{ - 0xC7, 0x9E, 0x47, 0x58, 0x00, 0x7D, 0x62, 0x82, 0xB0, 0xD2, 0x22, 0x81, 0xD4, - 0xA8, 0x97, 0x1B, 0x79, 0x0C, 0x3A, 0xB0, 0xD7, 0xC9, 0x30, 0xE3, 0xC3, 0x53, - 0x8E, 0x57, 0xEF, 0xF0, 0x9B, 0x9F, 0xB3, 0x90, 0x52, 0xC6, 0x94, 0x22, 0x36, - 0xAA, 0xE6, 0x4A, 0x5F, 0x72, 0x1D, 0x70, 0xE8, 0x76, 0x58, 0xC8, 0xB2, 0x91, - 0xCE, 0x9C, 0xC3, 0xE9, 0x09, 0x7F, 0x2E, 0x47, 0x97, 0xCC, 0x90, 0x39, 0x15, - 0x35, 0x31, 0xDE, 0x1F, 0x0C, 0x8C, 0x0D, 0xC1, 0xC2, 0x92, 0xBE, 0x97, 0xBF, - 0x2F, 0x91, 0xA1, 0x8C, 0x7D, 0x50, 0xA8, 0x21, 0x2F, 0xD7, 0xA2, 0x9A, 0x7E, - 0xB5, 0xA7, 0x2A, 0x90, 0x02, 0xD9, 0xF3, 0x3D, 0xD1, 0xEB, 0xB8, 0xE0, 0x5A, - 0x79, 0x9E, 0x7D, 0x8D, 0xCA, 0x18, 0x6D, 0xBD, 0x9E, 0xA1, 0x80, 0x28, 0x6B, - 0x2A, 0xFE, 0x51, 0x24, 0x9B, 0x6F, 0x4D, 0x84, 0x77, 0x80, 0x23}; - }; + static constexpr CryptoPP::byte Prime2[] = { + 0xC7, 0x9E, 0x47, 0x58, 0x00, 0x7D, 0x62, 0x82, 0xB0, 0xD2, 0x22, 0x81, 0xD4, 0xA8, 0x97, + 0x1B, 0x79, 0x0C, 0x3A, 0xB0, 0xD7, 0xC9, 0x30, 0xE3, 0xC3, 0x53, 0x8E, 0x57, 0xEF, 0xF0, + 0x9B, 0x9F, 0xB3, 0x90, 0x52, 0xC6, 0x94, 0x22, 0x36, 0xAA, 0xE6, 0x4A, 0x5F, 0x72, 0x1D, + 0x70, 0xE8, 0x76, 0x58, 0xC8, 0xB2, 0x91, 0xCE, 0x9C, 0xC3, 0xE9, 0x09, 0x7F, 0x2E, 0x47, + 0x97, 0xCC, 0x90, 0x39, 0x15, 0x35, 0x31, 0xDE, 0x1F, 0x0C, 0x8C, 0x0D, 0xC1, 0xC2, 0x92, + 0xBE, 0x97, 0xBF, 0x2F, 0x91, 0xA1, 0x8C, 0x7D, 0x50, 0xA8, 0x21, 0x2F, 0xD7, 0xA2, 0x9A, + 0x7E, 0xB5, 0xA7, 0x2A, 0x90, 0x02, 0xD9, 0xF3, 0x3D, 0xD1, 0xEB, 0xB8, 0xE0, 0x5A, 0x79, + 0x9E, 0x7D, 0x8D, 0xCA, 0x18, 0x6D, 0xBD, 0x9E, 0xA1, 0x80, 0x28, 0x6B, 0x2A, 0xFE, 0x51, + 0x24, 0x9B, 0x6F, 0x4D, 0x84, 0x77, 0x80, 0x23}; + static constexpr CryptoPP::byte PrivateExponent[] = { + 0x7F, 0x76, 0xCD, 0x0E, 0xE2, 0xD4, 0xDE, 0x05, 0x1C, 0xC6, 0xD9, 0xA8, 0x0E, 0x8D, 0xFA, + 0x7B, 0xCA, 0x1E, 0xAA, 0x27, 0x1A, 0x40, 0xF8, 0xF1, 0x22, 0x87, 0x35, 0xDD, 0xDB, 0xFD, + 0xEE, 0xF8, 0xC2, 0xBC, 0xBD, 0x01, 0xFB, 0x8B, 0xE2, 0x3E, 0x63, 0xB2, 0xB1, 0x22, 0x5C, + 0x56, 0x49, 0x6E, 0x11, 0xBE, 0x07, 0x44, 0x0B, 0x9A, 0x26, 0x66, 0xD1, 0x49, 0x2C, 0x8F, + 0xD3, 0x1B, 0xCF, 0xA4, 0xA1, 0xB8, 0xD1, 0xFB, 0xA4, 0x9E, 0xD2, 0x21, 0x28, 0x83, 0x09, + 0x8A, 0xF6, 0xA0, 0x0B, 0xA3, 0xD6, 0x0F, 0x9B, 0x63, 0x68, 0xCC, 0xBC, 0x0C, 0x4E, 0x14, + 0x5B, 0x27, 0xA4, 0xA9, 0xF4, 0x2B, 0xB9, 0xB8, 0x7B, 0xC0, 0xE6, 0x51, 0xAD, 0x1D, 0x77, + 0xD4, 0x6B, 0xB9, 0xCE, 0x20, 0xD1, 0x26, 0x66, 0x7E, 0x5E, 0x9E, 0xA2, 0xE9, 0x6B, 0x90, + 0xF3, 0x73, 0xB8, 0x52, 0x8F, 0x44, 0x11, 0x03, 0x0C, 0x13, 0x97, 0x39, 0x3D, 0x13, 0x22, + 0x58, 0xD5, 0x43, 0x82, 0x49, 0xDA, 0x6E, 0x7C, 0xA1, 0xC5, 0x8C, 0xA5, 0xB0, 0x09, 0xE0, + 0xCE, 0x3D, 0xDF, 0xF4, 0x9D, 0x3C, 0x97, 0x15, 0xE2, 0x6A, 0xC7, 0x2B, 0x3C, 0x50, 0x93, + 0x23, 0xDB, 0xBA, 0x4A, 0x22, 0x66, 0x44, 0xAC, 0x78, 0xBB, 0x0E, 0x1A, 0x27, 0x43, 0xB5, + 0x71, 0x67, 0xAF, 0xF4, 0xAB, 0x48, 0x46, 0x93, 0x73, 0xD0, 0x42, 0xAB, 0x93, 0x63, 0xE5, + 0x6C, 0x9A, 0xDE, 0x50, 0x24, 0xC0, 0x23, 0x7D, 0x99, 0x79, 0x3F, 0x22, 0x07, 0xE0, 0xC1, + 0x48, 0x56, 0x1B, 0xDF, 0x83, 0x09, 0x12, 0xB4, 0x2D, 0x45, 0x6B, 0xC9, 0xC0, 0x68, 0x85, + 0x99, 0x90, 0x79, 0x96, 0x1A, 0xD7, 0xF5, 0x4D, 0x1F, 0x37, 0x83, 0x40, 0x4A, 0xEC, 0x39, + 0x37, 0xA6, 0x80, 0x92, 0x7D, 0xC5, 0x80, 0xC7, 0xD6, 0x6F, 0xFE, 0x8A, 0x79, 0x89, 0xC6, + 0xB1}; }; class DebugRifKeyset { public: - // Constructor // std::uint8_t* PrivateExponent; - const CryptoPP::byte* Exponent1; + static constexpr CryptoPP::byte Exponent1[] = { + 0xCD, 0x9A, 0x61, 0xB0, 0xB8, 0xD5, 0xB4, 0xE4, 0xE4, 0xF6, 0xAB, 0xF7, 0x27, 0xB7, 0x56, + 0x59, 0x6B, 0xB9, 0x11, 0xE7, 0xF4, 0x83, 0xAF, 0xB9, 0x73, 0x99, 0x7F, 0x49, 0xA2, 0x9C, + 0xF0, 0xB5, 0x6D, 0x37, 0x82, 0x14, 0x15, 0xF1, 0x04, 0x8A, 0xD4, 0x8E, 0xEB, 0x2E, 0x1F, + 0xE2, 0x81, 0xA9, 0x62, 0x6E, 0xB1, 0x68, 0x75, 0x62, 0xF3, 0x0F, 0xFE, 0xD4, 0x91, 0x87, + 0x98, 0x78, 0xBF, 0x26, 0xB5, 0x07, 0x58, 0xD0, 0xEE, 0x3F, 0x21, 0xE8, 0xC8, 0x0F, 0x5F, + 0xFA, 0x1C, 0x64, 0x74, 0x49, 0x52, 0xEB, 0xE7, 0xEE, 0xDE, 0xBA, 0x23, 0x26, 0x4A, 0xF6, + 0x9C, 0x1A, 0x09, 0x3F, 0xB9, 0x0B, 0x36, 0x26, 0x1A, 0xBE, 0xA9, 0x76, 0xE6, 0xF2, 0x69, + 0xDE, 0xFF, 0xAF, 0xCC, 0x0C, 0x9A, 0x66, 0x03, 0x86, 0x0A, 0x1F, 0x49, 0xA4, 0x10, 0xB6, + 0xBC, 0xC3, 0x7C, 0x88, 0xE8, 0xCE, 0x4B, 0xD9}; // exponent2 = d mod (q - 1) - const CryptoPP::byte* Exponent2; + static constexpr CryptoPP::byte Exponent2[] = { + 0xB3, 0x73, 0xA3, 0x59, 0xE6, 0x97, 0xC0, 0xAB, 0x3B, 0x68, 0xFC, 0x39, 0xAC, 0xDB, 0x44, + 0xB1, 0xB4, 0x9E, 0x35, 0x4D, 0xBE, 0xC5, 0x36, 0x69, 0x6C, 0x3D, 0xC5, 0xFC, 0xFE, 0x4B, + 0x2F, 0xDC, 0x86, 0x80, 0x46, 0x96, 0x40, 0x1A, 0x0D, 0x6E, 0xFA, 0x8C, 0xE0, 0x47, 0x91, + 0xAC, 0xAD, 0x95, 0x2B, 0x8E, 0x1F, 0xF2, 0x0A, 0x45, 0xF8, 0x29, 0x95, 0x70, 0xC6, 0x88, + 0x5F, 0x71, 0x03, 0x99, 0x79, 0xBC, 0x84, 0x71, 0xBD, 0xE8, 0x84, 0x8C, 0x0E, 0xD4, 0x7B, + 0x30, 0x74, 0x57, 0x1A, 0x95, 0xE7, 0x90, 0x19, 0x8D, 0xAD, 0x8B, 0x4C, 0x4E, 0xC3, 0xE7, + 0x6B, 0x23, 0x86, 0x01, 0xEE, 0x9B, 0xE0, 0x2F, 0x15, 0xA2, 0x2C, 0x4C, 0x39, 0xD3, 0xDF, + 0x9C, 0x39, 0x01, 0xF1, 0x8C, 0x44, 0x4A, 0x15, 0x44, 0xDC, 0x51, 0xF7, 0x22, 0xD7, 0x7F, + 0x41, 0x7F, 0x68, 0xFA, 0xEE, 0x56, 0xE8, 0x05}; // e - const CryptoPP::byte* PublicExponent; + static constexpr CryptoPP::byte PublicExponent[] = {0x00, 0x01, 0x00, 0x01}; // (InverseQ)(q) = 1 mod p - const CryptoPP::byte* Coefficient; + static constexpr CryptoPP::byte Coefficient[] = { + 0xC0, 0x32, 0x43, 0xD3, 0x8C, 0x3D, 0xB4, 0xD2, 0x48, 0x8C, 0x42, 0x41, 0x24, 0x94, 0x6C, + 0x80, 0xC9, 0xC1, 0x79, 0x36, 0x7F, 0xAC, 0xC3, 0xFF, 0x6A, 0x25, 0xEB, 0x2C, 0xFB, 0xD4, + 0x2B, 0xA0, 0xEB, 0xFE, 0x25, 0xE9, 0xC6, 0x77, 0xCE, 0xFE, 0x2D, 0x23, 0xFE, 0xD0, 0xF4, + 0x0F, 0xD9, 0x7E, 0xD5, 0xA5, 0x7D, 0x1F, 0xC0, 0xE8, 0xE8, 0xEC, 0x80, 0x5B, 0xC7, 0xFD, + 0xE2, 0xBD, 0x94, 0xA6, 0x2B, 0xDD, 0x6A, 0x60, 0x45, 0x54, 0xAB, 0xCA, 0x42, 0x9C, 0x6A, + 0x6C, 0xBF, 0x3C, 0x84, 0xF9, 0xA5, 0x0E, 0x63, 0x0C, 0x51, 0x58, 0x62, 0x6D, 0x5A, 0xB7, + 0x3C, 0x3F, 0x49, 0x1A, 0xD0, 0x93, 0xB8, 0x4F, 0x1A, 0x6C, 0x5F, 0xC5, 0xE5, 0xA9, 0x75, + 0xD4, 0x86, 0x9E, 0xDF, 0x87, 0x0F, 0x27, 0xB0, 0x26, 0x78, 0x4E, 0xFB, 0xC1, 0x8A, 0x4A, + 0x24, 0x3F, 0x7F, 0x8F, 0x9A, 0x12, 0x51, 0xCB}; // n = p * q - const CryptoPP::byte* Modulus; + static constexpr CryptoPP::byte Modulus[] = { + 0xC2, 0xD2, 0x44, 0xBC, 0xDD, 0x84, 0x3F, 0xD9, 0xC5, 0x22, 0xAF, 0xF7, 0xFC, 0x88, 0x8A, + 0x33, 0x80, 0xED, 0x8E, 0xE2, 0xCC, 0x81, 0xF7, 0xEC, 0xF8, 0x1C, 0x79, 0xBF, 0x02, 0xBB, + 0x12, 0x8E, 0x61, 0x68, 0x29, 0x1B, 0x15, 0xB6, 0x5E, 0xC6, 0xF8, 0xBF, 0x5A, 0xE0, 0x3B, + 0x6A, 0x6C, 0xD9, 0xD6, 0xF5, 0x75, 0xAB, 0xA0, 0x6F, 0x34, 0x81, 0x34, 0x9A, 0x5B, 0xAD, + 0xED, 0x31, 0xE3, 0xC6, 0xEA, 0x1A, 0xD1, 0x13, 0x22, 0xBB, 0xB3, 0xDA, 0xB3, 0xB2, 0x53, + 0xBD, 0x45, 0x79, 0x87, 0xAD, 0x0A, 0x01, 0x72, 0x18, 0x10, 0x29, 0x49, 0xF4, 0x41, 0x7F, + 0xD6, 0x47, 0x0C, 0x72, 0x92, 0x9E, 0xE9, 0xBB, 0x95, 0xA9, 0x5D, 0x79, 0xEB, 0xE4, 0x30, + 0x76, 0x90, 0x45, 0x4B, 0x9D, 0x9C, 0xCF, 0x92, 0x03, 0x60, 0x8C, 0x4B, 0x6C, 0xB3, 0x7A, + 0x3A, 0x05, 0x39, 0xA0, 0x66, 0xA9, 0x35, 0xCF, 0xB9, 0xFA, 0xAD, 0x9C, 0xAB, 0xEB, 0xE4, + 0x6A, 0x8C, 0xE9, 0x3B, 0xCC, 0x72, 0x12, 0x62, 0x63, 0xBD, 0x80, 0xC4, 0xEE, 0x37, 0x2B, + 0x32, 0x03, 0xA3, 0x09, 0xF7, 0xA0, 0x61, 0x57, 0xAD, 0x0D, 0xCF, 0x15, 0x98, 0x9E, 0x4E, + 0x49, 0xF8, 0xB5, 0xA3, 0x5C, 0x27, 0xEE, 0x45, 0x04, 0xEA, 0xE4, 0x4B, 0xBC, 0x8F, 0x87, + 0xED, 0x19, 0x1E, 0x46, 0x75, 0x63, 0xC4, 0x5B, 0xD5, 0xBC, 0x09, 0x2F, 0x02, 0x73, 0x19, + 0x3C, 0x58, 0x55, 0x49, 0x66, 0x4C, 0x11, 0xEC, 0x0F, 0x09, 0xFA, 0xA5, 0x56, 0x0A, 0x5A, + 0x63, 0x56, 0xAD, 0xA0, 0x0D, 0x86, 0x08, 0xC1, 0xE6, 0xB6, 0x13, 0x22, 0x49, 0x2F, 0x7C, + 0xDB, 0x4C, 0x56, 0x97, 0x0E, 0xC2, 0xD9, 0x2E, 0x87, 0xBC, 0x0E, 0x67, 0xC0, 0x1B, 0x58, + 0xBC, 0x64, 0x2B, 0xC2, 0x6E, 0xE2, 0x93, 0x2E, 0xB5, 0x6B, 0x70, 0xA4, 0x42, 0x9F, 0x64, + 0xC1}; // p - const CryptoPP::byte* Prime1; + static constexpr CryptoPP::byte Prime1[] = { + 0xE5, 0x62, 0xE1, 0x7F, 0x9F, 0x86, 0x08, 0xE2, 0x61, 0xD3, 0xD0, 0x42, 0xE2, 0xC4, 0xB6, + 0xA8, 0x51, 0x09, 0x19, 0x14, 0xA4, 0x3A, 0x11, 0x4C, 0x33, 0xA5, 0x9C, 0x01, 0x5E, 0x34, + 0xB6, 0x3F, 0x02, 0x1A, 0xCA, 0x47, 0xF1, 0x4F, 0x3B, 0x35, 0x2A, 0x07, 0x20, 0xEC, 0xD8, + 0xC1, 0x15, 0xD9, 0xCA, 0x03, 0x4F, 0xB8, 0xE8, 0x09, 0x73, 0x3F, 0x85, 0xB7, 0x41, 0xD5, + 0x51, 0x3E, 0x7B, 0xE3, 0x53, 0x2B, 0x48, 0x8B, 0x8E, 0xCB, 0xBA, 0xF7, 0xE0, 0x60, 0xF5, + 0x35, 0x0E, 0x6F, 0xB0, 0xD9, 0x2A, 0x99, 0xD0, 0xFF, 0x60, 0x14, 0xED, 0x40, 0xEA, 0xF8, + 0xD7, 0x0B, 0xC3, 0x8D, 0x8C, 0xE8, 0x81, 0xB3, 0x75, 0x93, 0x15, 0xB3, 0x7D, 0xF6, 0x39, + 0x60, 0x1A, 0x00, 0xE7, 0xC3, 0x27, 0xAD, 0xA4, 0x33, 0xD5, 0x3E, 0xA4, 0x35, 0x48, 0x6F, + 0x22, 0xEF, 0x5D, 0xDD, 0x7D, 0x7B, 0x61, 0x05}; // q - const CryptoPP::byte* Prime2; - const CryptoPP::byte* PrivateExponent; - - // Constructor - DebugRifKeyset() { - // Initialize PrivateExponent - PrivateExponent = new CryptoPP::byte[0x100]{ - 0x01, 0x61, 0xAD, 0xD8, 0x9C, 0x06, 0x89, 0xD0, 0x60, 0xC8, 0x41, 0xF0, 0xB3, 0x83, - 0x01, 0x5D, 0xE3, 0xA2, 0x6B, 0xA2, 0xBA, 0x9A, 0x0A, 0x58, 0xCD, 0x1A, 0xA0, 0x97, - 0x64, 0xEC, 0xD0, 0x31, 0x1F, 0xCA, 0x36, 0x0E, 0x69, 0xDD, 0x40, 0xF7, 0x4E, 0xC0, - 0xC6, 0xA3, 0x73, 0xF0, 0x69, 0x84, 0xB2, 0xF4, 0x4B, 0x29, 0x14, 0x2A, 0x6D, 0xB8, - 0x23, 0xD8, 0x1B, 0x61, 0xD4, 0x9E, 0x87, 0xB3, 0xBB, 0xA9, 0xC4, 0x85, 0x4A, 0xF8, - 0x03, 0x4A, 0xBF, 0xFE, 0xF9, 0xFE, 0x8B, 0xDD, 0x54, 0x83, 0xBA, 0xE0, 0x2F, 0x3F, - 0xB1, 0xEF, 0xA5, 0x05, 0x5D, 0x28, 0x8B, 0xAB, 0xB5, 0xD0, 0x23, 0x2F, 0x8A, 0xCF, - 0x48, 0x7C, 0xAA, 0xBB, 0xC8, 0x5B, 0x36, 0x27, 0xC5, 0x16, 0xA4, 0xB6, 0x61, 0xAC, - 0x0C, 0x28, 0x47, 0x79, 0x3F, 0x38, 0xAE, 0x5E, 0x25, 0xC6, 0xAF, 0x35, 0xAE, 0xBC, - 0xB0, 0xF3, 0xBC, 0xBD, 0xFD, 0xA4, 0x87, 0x0D, 0x14, 0x3D, 0x90, 0xE4, 0xDE, 0x5D, - 0x1D, 0x46, 0x81, 0xF1, 0x28, 0x6D, 0x2F, 0x2C, 0x5E, 0x97, 0x2D, 0x89, 0x2A, 0x51, - 0x72, 0x3C, 0x20, 0x02, 0x59, 0xB1, 0x98, 0x93, 0x05, 0x1E, 0x3F, 0xA1, 0x8A, 0x69, - 0x30, 0x0E, 0x70, 0x84, 0x8B, 0xAE, 0x97, 0xA1, 0x08, 0x95, 0x63, 0x4C, 0xC7, 0xE8, - 0x5D, 0x59, 0xCA, 0x78, 0x2A, 0x23, 0x87, 0xAC, 0x6F, 0x04, 0x33, 0xB1, 0x61, 0xB9, - 0xF0, 0x95, 0xDA, 0x33, 0xCC, 0xE0, 0x4C, 0x82, 0x68, 0x82, 0x14, 0x51, 0xBE, 0x49, - 0x1C, 0x58, 0xA2, 0x8B, 0x05, 0x4E, 0x98, 0x37, 0xEB, 0x94, 0x0B, 0x01, 0x22, 0xDC, - 0xB3, 0x19, 0xCA, 0x77, 0xA6, 0x6E, 0x97, 0xFF, 0x8A, 0x53, 0x5A, 0xC5, 0x24, 0xE4, - 0xAF, 0x6E, 0xA8, 0x2B, 0x53, 0xA4, 0xBE, 0x96, 0xA5, 0x7B, 0xCE, 0x22, 0x56, 0xA3, - 0xF1, 0xCF, 0x14, 0xA5}; - - // Initialize Exponent1 - Exponent1 = new CryptoPP::byte[0x80]{ - 0xCD, 0x9A, 0x61, 0xB0, 0xB8, 0xD5, 0xB4, 0xE4, 0xE4, 0xF6, 0xAB, 0xF7, 0x27, - 0xB7, 0x56, 0x59, 0x6B, 0xB9, 0x11, 0xE7, 0xF4, 0x83, 0xAF, 0xB9, 0x73, 0x99, - 0x7F, 0x49, 0xA2, 0x9C, 0xF0, 0xB5, 0x6D, 0x37, 0x82, 0x14, 0x15, 0xF1, 0x04, - 0x8A, 0xD4, 0x8E, 0xEB, 0x2E, 0x1F, 0xE2, 0x81, 0xA9, 0x62, 0x6E, 0xB1, 0x68, - 0x75, 0x62, 0xF3, 0x0F, 0xFE, 0xD4, 0x91, 0x87, 0x98, 0x78, 0xBF, 0x26, 0xB5, - 0x07, 0x58, 0xD0, 0xEE, 0x3F, 0x21, 0xE8, 0xC8, 0x0F, 0x5F, 0xFA, 0x1C, 0x64, - 0x74, 0x49, 0x52, 0xEB, 0xE7, 0xEE, 0xDE, 0xBA, 0x23, 0x26, 0x4A, 0xF6, 0x9C, - 0x1A, 0x09, 0x3F, 0xB9, 0x0B, 0x36, 0x26, 0x1A, 0xBE, 0xA9, 0x76, 0xE6, 0xF2, - 0x69, 0xDE, 0xFF, 0xAF, 0xCC, 0x0C, 0x9A, 0x66, 0x03, 0x86, 0x0A, 0x1F, 0x49, - 0xA4, 0x10, 0xB6, 0xBC, 0xC3, 0x7C, 0x88, 0xE8, 0xCE, 0x4B, 0xD9 - - }; - - Exponent2 = new CryptoPP::byte[0x80]{ - 0xB3, 0x73, 0xA3, 0x59, 0xE6, 0x97, 0xC0, 0xAB, 0x3B, 0x68, 0xFC, 0x39, 0xAC, - 0xDB, 0x44, 0xB1, 0xB4, 0x9E, 0x35, 0x4D, 0xBE, 0xC5, 0x36, 0x69, 0x6C, 0x3D, - 0xC5, 0xFC, 0xFE, 0x4B, 0x2F, 0xDC, 0x86, 0x80, 0x46, 0x96, 0x40, 0x1A, 0x0D, - 0x6E, 0xFA, 0x8C, 0xE0, 0x47, 0x91, 0xAC, 0xAD, 0x95, 0x2B, 0x8E, 0x1F, 0xF2, - 0x0A, 0x45, 0xF8, 0x29, 0x95, 0x70, 0xC6, 0x88, 0x5F, 0x71, 0x03, 0x99, 0x79, - 0xBC, 0x84, 0x71, 0xBD, 0xE8, 0x84, 0x8C, 0x0E, 0xD4, 0x7B, 0x30, 0x74, 0x57, - 0x1A, 0x95, 0xE7, 0x90, 0x19, 0x8D, 0xAD, 0x8B, 0x4C, 0x4E, 0xC3, 0xE7, 0x6B, - 0x23, 0x86, 0x01, 0xEE, 0x9B, 0xE0, 0x2F, 0x15, 0xA2, 0x2C, 0x4C, 0x39, 0xD3, - 0xDF, 0x9C, 0x39, 0x01, 0xF1, 0x8C, 0x44, 0x4A, 0x15, 0x44, 0xDC, 0x51, 0xF7, - 0x22, 0xD7, 0x7F, 0x41, 0x7F, 0x68, 0xFA, 0xEE, 0x56, 0xE8, 0x05}; - - PublicExponent = new CryptoPP::byte[4]{0x00, 0x01, 0x00, 0x01}; - - Coefficient = new CryptoPP::byte[0x80]{ - 0xC0, 0x32, 0x43, 0xD3, 0x8C, 0x3D, 0xB4, 0xD2, 0x48, 0x8C, 0x42, 0x41, 0x24, - 0x94, 0x6C, 0x80, 0xC9, 0xC1, 0x79, 0x36, 0x7F, 0xAC, 0xC3, 0xFF, 0x6A, 0x25, - 0xEB, 0x2C, 0xFB, 0xD4, 0x2B, 0xA0, 0xEB, 0xFE, 0x25, 0xE9, 0xC6, 0x77, 0xCE, - 0xFE, 0x2D, 0x23, 0xFE, 0xD0, 0xF4, 0x0F, 0xD9, 0x7E, 0xD5, 0xA5, 0x7D, 0x1F, - 0xC0, 0xE8, 0xE8, 0xEC, 0x80, 0x5B, 0xC7, 0xFD, 0xE2, 0xBD, 0x94, 0xA6, 0x2B, - 0xDD, 0x6A, 0x60, 0x45, 0x54, 0xAB, 0xCA, 0x42, 0x9C, 0x6A, 0x6C, 0xBF, 0x3C, - 0x84, 0xF9, 0xA5, 0x0E, 0x63, 0x0C, 0x51, 0x58, 0x62, 0x6D, 0x5A, 0xB7, 0x3C, - 0x3F, 0x49, 0x1A, 0xD0, 0x93, 0xB8, 0x4F, 0x1A, 0x6C, 0x5F, 0xC5, 0xE5, 0xA9, - 0x75, 0xD4, 0x86, 0x9E, 0xDF, 0x87, 0x0F, 0x27, 0xB0, 0x26, 0x78, 0x4E, 0xFB, - 0xC1, 0x8A, 0x4A, 0x24, 0x3F, 0x7F, 0x8F, 0x9A, 0x12, 0x51, 0xCB}; - - Modulus = new CryptoPP::byte[0x100]{ - 0xC2, 0xD2, 0x44, 0xBC, 0xDD, 0x84, 0x3F, 0xD9, 0xC5, 0x22, 0xAF, 0xF7, 0xFC, 0x88, - 0x8A, 0x33, 0x80, 0xED, 0x8E, 0xE2, 0xCC, 0x81, 0xF7, 0xEC, 0xF8, 0x1C, 0x79, 0xBF, - 0x02, 0xBB, 0x12, 0x8E, 0x61, 0x68, 0x29, 0x1B, 0x15, 0xB6, 0x5E, 0xC6, 0xF8, 0xBF, - 0x5A, 0xE0, 0x3B, 0x6A, 0x6C, 0xD9, 0xD6, 0xF5, 0x75, 0xAB, 0xA0, 0x6F, 0x34, 0x81, - 0x34, 0x9A, 0x5B, 0xAD, 0xED, 0x31, 0xE3, 0xC6, 0xEA, 0x1A, 0xD1, 0x13, 0x22, 0xBB, - 0xB3, 0xDA, 0xB3, 0xB2, 0x53, 0xBD, 0x45, 0x79, 0x87, 0xAD, 0x0A, 0x01, 0x72, 0x18, - 0x10, 0x29, 0x49, 0xF4, 0x41, 0x7F, 0xD6, 0x47, 0x0C, 0x72, 0x92, 0x9E, 0xE9, 0xBB, - 0x95, 0xA9, 0x5D, 0x79, 0xEB, 0xE4, 0x30, 0x76, 0x90, 0x45, 0x4B, 0x9D, 0x9C, 0xCF, - 0x92, 0x03, 0x60, 0x8C, 0x4B, 0x6C, 0xB3, 0x7A, 0x3A, 0x05, 0x39, 0xA0, 0x66, 0xA9, - 0x35, 0xCF, 0xB9, 0xFA, 0xAD, 0x9C, 0xAB, 0xEB, 0xE4, 0x6A, 0x8C, 0xE9, 0x3B, 0xCC, - 0x72, 0x12, 0x62, 0x63, 0xBD, 0x80, 0xC4, 0xEE, 0x37, 0x2B, 0x32, 0x03, 0xA3, 0x09, - 0xF7, 0xA0, 0x61, 0x57, 0xAD, 0x0D, 0xCF, 0x15, 0x98, 0x9E, 0x4E, 0x49, 0xF8, 0xB5, - 0xA3, 0x5C, 0x27, 0xEE, 0x45, 0x04, 0xEA, 0xE4, 0x4B, 0xBC, 0x8F, 0x87, 0xED, 0x19, - 0x1E, 0x46, 0x75, 0x63, 0xC4, 0x5B, 0xD5, 0xBC, 0x09, 0x2F, 0x02, 0x73, 0x19, 0x3C, - 0x58, 0x55, 0x49, 0x66, 0x4C, 0x11, 0xEC, 0x0F, 0x09, 0xFA, 0xA5, 0x56, 0x0A, 0x5A, - 0x63, 0x56, 0xAD, 0xA0, 0x0D, 0x86, 0x08, 0xC1, 0xE6, 0xB6, 0x13, 0x22, 0x49, 0x2F, - 0x7C, 0xDB, 0x4C, 0x56, 0x97, 0x0E, 0xC2, 0xD9, 0x2E, 0x87, 0xBC, 0x0E, 0x67, 0xC0, - 0x1B, 0x58, 0xBC, 0x64, 0x2B, 0xC2, 0x6E, 0xE2, 0x93, 0x2E, 0xB5, 0x6B, 0x70, 0xA4, - 0x42, 0x9F, 0x64, 0xC1}; - - Prime1 = new CryptoPP::byte[0x80]{ - 0xE5, 0x62, 0xE1, 0x7F, 0x9F, 0x86, 0x08, 0xE2, 0x61, 0xD3, 0xD0, 0x42, 0xE2, - 0xC4, 0xB6, 0xA8, 0x51, 0x09, 0x19, 0x14, 0xA4, 0x3A, 0x11, 0x4C, 0x33, 0xA5, - 0x9C, 0x01, 0x5E, 0x34, 0xB6, 0x3F, 0x02, 0x1A, 0xCA, 0x47, 0xF1, 0x4F, 0x3B, - 0x35, 0x2A, 0x07, 0x20, 0xEC, 0xD8, 0xC1, 0x15, 0xD9, 0xCA, 0x03, 0x4F, 0xB8, - 0xE8, 0x09, 0x73, 0x3F, 0x85, 0xB7, 0x41, 0xD5, 0x51, 0x3E, 0x7B, 0xE3, 0x53, - 0x2B, 0x48, 0x8B, 0x8E, 0xCB, 0xBA, 0xF7, 0xE0, 0x60, 0xF5, 0x35, 0x0E, 0x6F, - 0xB0, 0xD9, 0x2A, 0x99, 0xD0, 0xFF, 0x60, 0x14, 0xED, 0x40, 0xEA, 0xF8, 0xD7, - 0x0B, 0xC3, 0x8D, 0x8C, 0xE8, 0x81, 0xB3, 0x75, 0x93, 0x15, 0xB3, 0x7D, 0xF6, - 0x39, 0x60, 0x1A, 0x00, 0xE7, 0xC3, 0x27, 0xAD, 0xA4, 0x33, 0xD5, 0x3E, 0xA4, - 0x35, 0x48, 0x6F, 0x22, 0xEF, 0x5D, 0xDD, 0x7D, 0x7B, 0x61, 0x05}; - - Prime2 = new CryptoPP::byte[0x80]{ - 0xD9, 0x6C, 0xC2, 0x0C, 0xF7, 0xAE, 0xD1, 0xF3, 0x3B, 0x3B, 0x49, 0x1E, 0x9F, - 0x12, 0x9C, 0xA1, 0x78, 0x1F, 0x35, 0x1D, 0x98, 0x26, 0x13, 0x71, 0xF9, 0x09, - 0xFD, 0xF0, 0xAD, 0x38, 0x55, 0xB7, 0xEE, 0x61, 0x04, 0x72, 0x51, 0x87, 0x2E, - 0x05, 0x84, 0xB1, 0x1D, 0x0C, 0x0D, 0xDB, 0xD4, 0x25, 0x3E, 0x26, 0xED, 0xEA, - 0xB8, 0xF7, 0x49, 0xFE, 0xA2, 0x94, 0xE6, 0xF2, 0x08, 0x92, 0xA7, 0x85, 0xF5, - 0x30, 0xB9, 0x84, 0x22, 0xBF, 0xCA, 0xF0, 0x5F, 0xCB, 0x31, 0x20, 0x34, 0x49, - 0x16, 0x76, 0x34, 0xCC, 0x7A, 0xCB, 0x96, 0xFE, 0x78, 0x7A, 0x41, 0xFE, 0x9A, - 0xA2, 0x23, 0xF7, 0x68, 0x80, 0xD6, 0xCE, 0x4A, 0x78, 0xA5, 0xB7, 0x05, 0x77, - 0x81, 0x1F, 0xDE, 0x5E, 0xA8, 0x6E, 0x3E, 0x87, 0xEC, 0x44, 0xD2, 0x69, 0xC6, - 0x54, 0x91, 0x6B, 0x5E, 0x13, 0x8A, 0x03, 0x87, 0x05, 0x31, 0x8D}; - }; + static constexpr CryptoPP::byte Prime2[] = { + 0xD9, 0x6C, 0xC2, 0x0C, 0xF7, 0xAE, 0xD1, 0xF3, 0x3B, 0x3B, 0x49, 0x1E, 0x9F, 0x12, 0x9C, + 0xA1, 0x78, 0x1F, 0x35, 0x1D, 0x98, 0x26, 0x13, 0x71, 0xF9, 0x09, 0xFD, 0xF0, 0xAD, 0x38, + 0x55, 0xB7, 0xEE, 0x61, 0x04, 0x72, 0x51, 0x87, 0x2E, 0x05, 0x84, 0xB1, 0x1D, 0x0C, 0x0D, + 0xDB, 0xD4, 0x25, 0x3E, 0x26, 0xED, 0xEA, 0xB8, 0xF7, 0x49, 0xFE, 0xA2, 0x94, 0xE6, 0xF2, + 0x08, 0x92, 0xA7, 0x85, 0xF5, 0x30, 0xB9, 0x84, 0x22, 0xBF, 0xCA, 0xF0, 0x5F, 0xCB, 0x31, + 0x20, 0x34, 0x49, 0x16, 0x76, 0x34, 0xCC, 0x7A, 0xCB, 0x96, 0xFE, 0x78, 0x7A, 0x41, 0xFE, + 0x9A, 0xA2, 0x23, 0xF7, 0x68, 0x80, 0xD6, 0xCE, 0x4A, 0x78, 0xA5, 0xB7, 0x05, 0x77, 0x81, + 0x1F, 0xDE, 0x5E, 0xA8, 0x6E, 0x3E, 0x87, 0xEC, 0x44, 0xD2, 0x69, 0xC6, 0x54, 0x91, 0x6B, + 0x5E, 0x13, 0x8A, 0x03, 0x87, 0x05, 0x31, 0x8D}; + static constexpr CryptoPP::byte PrivateExponent[] = { + 0x01, 0x61, 0xAD, 0xD8, 0x9C, 0x06, 0x89, 0xD0, 0x60, 0xC8, 0x41, 0xF0, 0xB3, 0x83, 0x01, + 0x5D, 0xE3, 0xA2, 0x6B, 0xA2, 0xBA, 0x9A, 0x0A, 0x58, 0xCD, 0x1A, 0xA0, 0x97, 0x64, 0xEC, + 0xD0, 0x31, 0x1F, 0xCA, 0x36, 0x0E, 0x69, 0xDD, 0x40, 0xF7, 0x4E, 0xC0, 0xC6, 0xA3, 0x73, + 0xF0, 0x69, 0x84, 0xB2, 0xF4, 0x4B, 0x29, 0x14, 0x2A, 0x6D, 0xB8, 0x23, 0xD8, 0x1B, 0x61, + 0xD4, 0x9E, 0x87, 0xB3, 0xBB, 0xA9, 0xC4, 0x85, 0x4A, 0xF8, 0x03, 0x4A, 0xBF, 0xFE, 0xF9, + 0xFE, 0x8B, 0xDD, 0x54, 0x83, 0xBA, 0xE0, 0x2F, 0x3F, 0xB1, 0xEF, 0xA5, 0x05, 0x5D, 0x28, + 0x8B, 0xAB, 0xB5, 0xD0, 0x23, 0x2F, 0x8A, 0xCF, 0x48, 0x7C, 0xAA, 0xBB, 0xC8, 0x5B, 0x36, + 0x27, 0xC5, 0x16, 0xA4, 0xB6, 0x61, 0xAC, 0x0C, 0x28, 0x47, 0x79, 0x3F, 0x38, 0xAE, 0x5E, + 0x25, 0xC6, 0xAF, 0x35, 0xAE, 0xBC, 0xB0, 0xF3, 0xBC, 0xBD, 0xFD, 0xA4, 0x87, 0x0D, 0x14, + 0x3D, 0x90, 0xE4, 0xDE, 0x5D, 0x1D, 0x46, 0x81, 0xF1, 0x28, 0x6D, 0x2F, 0x2C, 0x5E, 0x97, + 0x2D, 0x89, 0x2A, 0x51, 0x72, 0x3C, 0x20, 0x02, 0x59, 0xB1, 0x98, 0x93, 0x05, 0x1E, 0x3F, + 0xA1, 0x8A, 0x69, 0x30, 0x0E, 0x70, 0x84, 0x8B, 0xAE, 0x97, 0xA1, 0x08, 0x95, 0x63, 0x4C, + 0xC7, 0xE8, 0x5D, 0x59, 0xCA, 0x78, 0x2A, 0x23, 0x87, 0xAC, 0x6F, 0x04, 0x33, 0xB1, 0x61, + 0xB9, 0xF0, 0x95, 0xDA, 0x33, 0xCC, 0xE0, 0x4C, 0x82, 0x68, 0x82, 0x14, 0x51, 0xBE, 0x49, + 0x1C, 0x58, 0xA2, 0x8B, 0x05, 0x4E, 0x98, 0x37, 0xEB, 0x94, 0x0B, 0x01, 0x22, 0xDC, 0xB3, + 0x19, 0xCA, 0x77, 0xA6, 0x6E, 0x97, 0xFF, 0x8A, 0x53, 0x5A, 0xC5, 0x24, 0xE4, 0xAF, 0x6E, + 0xA8, 0x2B, 0x53, 0xA4, 0xBE, 0x96, 0xA5, 0x7B, 0xCE, 0x22, 0x56, 0xA3, 0xF1, 0xCF, 0x14, + 0xA5}; }; class PkgDerivedKey3Keyset { public: - // PkgDerivedKey3Keyset(); - //~PkgDerivedKey3Keyset(); - - // Constructor // std::uint8_t* PrivateExponent; - const CryptoPP::byte* Exponent1; + static constexpr CryptoPP::byte Exponent1[] = { + 0x52, 0xCC, 0x2D, 0xA0, 0x9C, 0x9E, 0x75, 0xE7, 0x28, 0xEE, 0x3D, 0xDE, 0xE3, 0x45, 0xD1, + 0x4F, 0x94, 0x1C, 0xCC, 0xC8, 0x87, 0x29, 0x45, 0x3B, 0x8D, 0x6E, 0xAB, 0x6E, 0x2A, 0xA7, + 0xC7, 0x15, 0x43, 0xA3, 0x04, 0x8F, 0x90, 0x5F, 0xEB, 0xF3, 0x38, 0x4A, 0x77, 0xFA, 0x36, + 0xB7, 0x15, 0x76, 0xB6, 0x01, 0x1A, 0x8E, 0x25, 0x87, 0x82, 0xF1, 0x55, 0xD8, 0xC6, 0x43, + 0x2A, 0xC0, 0xE5, 0x98, 0xC9, 0x32, 0xD1, 0x94, 0x6F, 0xD9, 0x01, 0xBA, 0x06, 0x81, 0xE0, + 0x6D, 0x88, 0xF2, 0x24, 0x2A, 0x25, 0x01, 0x64, 0x5C, 0xBF, 0xF2, 0xD9, 0x99, 0x67, 0x3E, + 0xF6, 0x72, 0xEE, 0xE4, 0xE2, 0x33, 0x5C, 0xF8, 0x00, 0x40, 0xE3, 0x2A, 0x9A, 0xF4, 0x3D, + 0x22, 0x86, 0x44, 0x3C, 0xFB, 0x0A, 0xA5, 0x7C, 0x3F, 0xCC, 0xF5, 0xF1, 0x16, 0xC4, 0xAC, + 0x88, 0xB4, 0xDE, 0x62, 0x94, 0x92, 0x6A, 0x13}; // exponent2 = d mod (q - 1) - const CryptoPP::byte* Exponent2; + static constexpr CryptoPP::byte Exponent2[] = { + 0x7C, 0x9D, 0xAD, 0x39, 0xE0, 0xD5, 0x60, 0x14, 0x94, 0x48, 0x19, 0x7F, 0x88, 0x95, 0xD5, + 0x8B, 0x80, 0xAD, 0x85, 0x8A, 0x4B, 0x77, 0x37, 0x85, 0xD0, 0x77, 0xBB, 0xBF, 0x89, 0x71, + 0x4A, 0x72, 0xCB, 0x72, 0x68, 0x38, 0xEC, 0x02, 0xC6, 0x7D, 0xC6, 0x44, 0x06, 0x33, 0x51, + 0x1C, 0xC0, 0xFF, 0x95, 0x8F, 0x0D, 0x75, 0xDC, 0x25, 0xBB, 0x0B, 0x73, 0x91, 0xA9, 0x6D, + 0x42, 0xD8, 0x03, 0xB7, 0x68, 0xD4, 0x1E, 0x75, 0x62, 0xA3, 0x70, 0x35, 0x79, 0x78, 0x00, + 0xC8, 0xF5, 0xEF, 0x15, 0xB9, 0xFC, 0x4E, 0x47, 0x5A, 0xC8, 0x70, 0x70, 0x5B, 0x52, 0x98, + 0xC0, 0xC2, 0x58, 0x4A, 0x70, 0x96, 0xCC, 0xB8, 0x10, 0xE1, 0x2F, 0x78, 0x8B, 0x2B, 0xA1, + 0x7F, 0xF9, 0xAC, 0xDE, 0xF0, 0xBB, 0x2B, 0xE2, 0x66, 0xE3, 0x22, 0x92, 0x31, 0x21, 0x57, + 0x92, 0xC4, 0xB8, 0xF2, 0x3E, 0x76, 0x20, 0x37}; // e - const CryptoPP::byte* PublicExponent; + static constexpr CryptoPP::byte PublicExponent[] = {0, 1, 0, 1}; // (InverseQ)(q) = 1 mod p - const CryptoPP::byte* Coefficient; + static constexpr CryptoPP::byte Coefficient[] = { + 0x45, 0x97, 0x55, 0xD4, 0x22, 0x08, 0x5E, 0xF3, 0x5C, 0xB4, 0x05, 0x7A, 0xFD, 0xAA, 0x42, + 0x42, 0xAD, 0x9A, 0x8C, 0xA0, 0x6C, 0xBB, 0x1D, 0x68, 0x54, 0x54, 0x6E, 0x3E, 0x32, 0xE3, + 0x53, 0x73, 0x76, 0xF1, 0x3E, 0x01, 0xEA, 0xD3, 0xCF, 0xEB, 0xEB, 0x23, 0x3E, 0xC0, 0xBE, + 0xCE, 0xEC, 0x2C, 0x89, 0x5F, 0xA8, 0x27, 0x3A, 0x4C, 0xB7, 0xE6, 0x74, 0xBC, 0x45, 0x4C, + 0x26, 0xC8, 0x25, 0xFF, 0x34, 0x63, 0x25, 0x37, 0xE1, 0x48, 0x10, 0xC1, 0x93, 0xA6, 0xAF, + 0xEB, 0xBA, 0xE3, 0xA2, 0xF1, 0x3D, 0xEF, 0x63, 0xD8, 0xF4, 0xFD, 0xD3, 0xEE, 0xE2, 0x5D, + 0xE9, 0x33, 0xCC, 0xAD, 0xBA, 0x75, 0x5C, 0x85, 0xAF, 0xCE, 0xA9, 0x3D, 0xD1, 0xA2, 0x17, + 0xF3, 0xF6, 0x98, 0xB3, 0x50, 0x8E, 0x5E, 0xF6, 0xEB, 0x02, 0x8E, 0xA1, 0x62, 0xA7, 0xD6, + 0x2C, 0xEC, 0x91, 0xFF, 0x15, 0x40, 0xD2, 0xE3}; // n = p * q - const CryptoPP::byte* Modulus; + static constexpr CryptoPP::byte Modulus[] = { + 0xd2, 0x12, 0xfc, 0x33, 0x5f, 0x6d, 0xdb, 0x83, 0x16, 0x09, 0x62, 0x8b, 0x03, 0x56, 0x27, + 0x37, 0x82, 0xd4, 0x77, 0x85, 0x35, 0x29, 0x39, 0x2d, 0x52, 0x6b, 0x8c, 0x4c, 0x8c, 0xfb, + 0x06, 0xc1, 0x84, 0x5b, 0xe7, 0xd4, 0xf7, 0xbc, 0xd2, 0x4e, 0x62, 0x45, 0xcd, 0x2a, 0xbb, + 0xd7, 0x77, 0x76, 0x45, 0x36, 0x55, 0x27, 0x3f, 0xb3, 0xf5, 0xf9, 0x8e, 0xda, 0x4b, 0xef, + 0xaa, 0x59, 0xae, 0xb3, 0x9b, 0xea, 0x54, 0x98, 0xd2, 0x06, 0x32, 0x6a, 0x58, 0x31, 0x2a, + 0xe0, 0xd4, 0x4f, 0x90, 0xb5, 0x0a, 0x7d, 0xec, 0xf4, 0x3a, 0x9c, 0x52, 0x67, 0x2d, 0x99, + 0x31, 0x8e, 0x0c, 0x43, 0xe6, 0x82, 0xfe, 0x07, 0x46, 0xe1, 0x2e, 0x50, 0xd4, 0x1f, 0x2d, + 0x2f, 0x7e, 0xd9, 0x08, 0xba, 0x06, 0xb3, 0xbf, 0x2e, 0x20, 0x3f, 0x4e, 0x3f, 0xfe, 0x44, + 0xff, 0xaa, 0x50, 0x43, 0x57, 0x91, 0x69, 0x94, 0x49, 0x15, 0x82, 0x82, 0xe4, 0x0f, 0x4c, + 0x8d, 0x9d, 0x2c, 0xc9, 0x5b, 0x1d, 0x64, 0xbf, 0x88, 0x8b, 0xd4, 0xc5, 0x94, 0xe7, 0x65, + 0x47, 0x84, 0x1e, 0xe5, 0x79, 0x10, 0xfb, 0x98, 0x93, 0x47, 0xb9, 0x7d, 0x85, 0x12, 0xa6, + 0x40, 0x98, 0x2c, 0xf7, 0x92, 0xbc, 0x95, 0x19, 0x32, 0xed, 0xe8, 0x90, 0x56, 0x0d, 0x65, + 0xc1, 0xaa, 0x78, 0xc6, 0x2e, 0x54, 0xfd, 0x5f, 0x54, 0xa1, 0xf6, 0x7e, 0xe5, 0xe0, 0x5f, + 0x61, 0xc1, 0x20, 0xb4, 0xb9, 0xb4, 0x33, 0x08, 0x70, 0xe4, 0xdf, 0x89, 0x56, 0xed, 0x01, + 0x29, 0x46, 0x77, 0x5f, 0x8c, 0xb8, 0xa9, 0xf5, 0x1e, 0x2e, 0xb3, 0xb9, 0xbf, 0xe0, 0x09, + 0xb7, 0x8d, 0x28, 0xd4, 0xa6, 0xc3, 0xb8, 0x1e, 0x1f, 0x07, 0xeb, 0xb4, 0x12, 0x0b, 0x95, + 0xb8, 0x85, 0x30, 0xfd, 0xdc, 0x39, 0x13, 0xd0, 0x7c, 0xdc, 0x8f, 0xed, 0xf9, 0xc9, 0xa3, + 0xc1}; // p - const CryptoPP::byte* Prime1; + static constexpr CryptoPP::byte Prime1[] = { + 0xF9, 0x67, 0xAD, 0x99, 0x12, 0x31, 0x0C, 0x56, 0xA2, 0x2E, 0x16, 0x1C, 0x46, 0xB3, 0x4D, + 0x5B, 0x43, 0xBE, 0x42, 0xA2, 0xF6, 0x86, 0x96, 0x80, 0x42, 0xC3, 0xC7, 0x3F, 0xC3, 0x42, + 0xF5, 0x87, 0x49, 0x33, 0x9F, 0x07, 0x5D, 0x6E, 0x2C, 0x04, 0xFD, 0xE3, 0xE1, 0xB2, 0xAE, + 0x0A, 0x0C, 0xF0, 0xC7, 0xA6, 0x1C, 0xA1, 0x63, 0x50, 0xC8, 0x09, 0x9C, 0x51, 0x24, 0x52, + 0x6C, 0x5E, 0x5E, 0xBD, 0x1E, 0x27, 0x06, 0xBB, 0xBC, 0x9E, 0x94, 0xE1, 0x35, 0xD4, 0x6D, + 0xB3, 0xCB, 0x3C, 0x68, 0xDD, 0x68, 0xB3, 0xFE, 0x6C, 0xCB, 0x8D, 0x82, 0x20, 0x76, 0x23, + 0x63, 0xB7, 0xE9, 0x68, 0x10, 0x01, 0x4E, 0xDC, 0xBA, 0x27, 0x5D, 0x01, 0xC1, 0x2D, 0x80, + 0x5E, 0x2B, 0xAF, 0x82, 0x6B, 0xD8, 0x84, 0xB6, 0x10, 0x52, 0x86, 0xA7, 0x89, 0x8E, 0xAE, + 0x9A, 0xE2, 0x89, 0xC6, 0xF7, 0xD5, 0x87, 0xFB}; // q - const CryptoPP::byte* Prime2; - const CryptoPP::byte* PrivateExponent; - - PkgDerivedKey3Keyset() { - - Prime1 = new CryptoPP::byte[0x80]{ - 0xF9, 0x67, 0xAD, 0x99, 0x12, 0x31, 0x0C, 0x56, 0xA2, 0x2E, 0x16, 0x1C, 0x46, - 0xB3, 0x4D, 0x5B, 0x43, 0xBE, 0x42, 0xA2, 0xF6, 0x86, 0x96, 0x80, 0x42, 0xC3, - 0xC7, 0x3F, 0xC3, 0x42, 0xF5, 0x87, 0x49, 0x33, 0x9F, 0x07, 0x5D, 0x6E, 0x2C, - 0x04, 0xFD, 0xE3, 0xE1, 0xB2, 0xAE, 0x0A, 0x0C, 0xF0, 0xC7, 0xA6, 0x1C, 0xA1, - 0x63, 0x50, 0xC8, 0x09, 0x9C, 0x51, 0x24, 0x52, 0x6C, 0x5E, 0x5E, 0xBD, 0x1E, - 0x27, 0x06, 0xBB, 0xBC, 0x9E, 0x94, 0xE1, 0x35, 0xD4, 0x6D, 0xB3, 0xCB, 0x3C, - 0x68, 0xDD, 0x68, 0xB3, 0xFE, 0x6C, 0xCB, 0x8D, 0x82, 0x20, 0x76, 0x23, 0x63, - 0xB7, 0xE9, 0x68, 0x10, 0x01, 0x4E, 0xDC, 0xBA, 0x27, 0x5D, 0x01, 0xC1, 0x2D, - 0x80, 0x5E, 0x2B, 0xAF, 0x82, 0x6B, 0xD8, 0x84, 0xB6, 0x10, 0x52, 0x86, 0xA7, - 0x89, 0x8E, 0xAE, 0x9A, 0xE2, 0x89, 0xC6, 0xF7, 0xD5, 0x87, 0xFB}; - - Prime2 = new CryptoPP::byte[0x80]{ - 0xD7, 0xA1, 0x0F, 0x9A, 0x8B, 0xF2, 0xC9, 0x11, 0x95, 0x32, 0x9A, 0x8C, 0xF0, - 0xD9, 0x40, 0x47, 0xF5, 0x68, 0xA0, 0x0D, 0xBD, 0xC1, 0xFC, 0x43, 0x2F, 0x65, - 0xF9, 0xC3, 0x61, 0x0F, 0x25, 0x77, 0x54, 0xAD, 0xD7, 0x58, 0xAC, 0x84, 0x40, - 0x60, 0x8D, 0x3F, 0xF3, 0x65, 0x89, 0x75, 0xB5, 0xC6, 0x2C, 0x51, 0x1A, 0x2F, - 0x1F, 0x22, 0xE4, 0x43, 0x11, 0x54, 0xBE, 0xC9, 0xB4, 0xC7, 0xB5, 0x1B, 0x05, - 0x0B, 0xBC, 0x56, 0x9A, 0xCD, 0x4A, 0xD9, 0x73, 0x68, 0x5E, 0x5C, 0xFB, 0x92, - 0xB7, 0x8B, 0x0D, 0xFF, 0xF5, 0x07, 0xCA, 0xB4, 0xC8, 0x9B, 0x96, 0x3C, 0x07, - 0x9E, 0x3E, 0x6B, 0x2A, 0x11, 0xF2, 0x8A, 0xB1, 0x8A, 0xD7, 0x2E, 0x1B, 0xA5, - 0x53, 0x24, 0x06, 0xED, 0x50, 0xB8, 0x90, 0x67, 0xB1, 0xE2, 0x41, 0xC6, 0x92, - 0x01, 0xEE, 0x10, 0xF0, 0x61, 0xBB, 0xFB, 0xB2, 0x7D, 0x4A, 0x73}; - PrivateExponent = new CryptoPP::byte[0x100]{ - 0x32, 0xD9, 0x03, 0x90, 0x8F, 0xBD, 0xB0, 0x8F, 0x57, 0x2B, 0x28, 0x5E, 0x0B, 0x8D, - 0xB3, 0xEA, 0x5C, 0xD1, 0x7E, 0xA8, 0x90, 0x88, 0x8C, 0xDD, 0x6A, 0x80, 0xBB, 0xB1, - 0xDF, 0xC1, 0xF7, 0x0D, 0xAA, 0x32, 0xF0, 0xB7, 0x7C, 0xCB, 0x88, 0x80, 0x0E, 0x8B, - 0x64, 0xB0, 0xBE, 0x4C, 0xD6, 0x0E, 0x9B, 0x8C, 0x1E, 0x2A, 0x64, 0xE1, 0xF3, 0x5C, - 0xD7, 0x76, 0x01, 0x41, 0x5E, 0x93, 0x5C, 0x94, 0xFE, 0xDD, 0x46, 0x62, 0xC3, 0x1B, - 0x5A, 0xE2, 0xA0, 0xBC, 0x2D, 0xEB, 0xC3, 0x98, 0x0A, 0xA7, 0xB7, 0x85, 0x69, 0x70, - 0x68, 0x2B, 0x64, 0x4A, 0xB3, 0x1F, 0xCC, 0x7D, 0xDC, 0x7C, 0x26, 0xF4, 0x77, 0xF6, - 0x5C, 0xF2, 0xAE, 0x5A, 0x44, 0x2D, 0xD3, 0xAB, 0x16, 0x62, 0x04, 0x19, 0xBA, 0xFB, - 0x90, 0xFF, 0xE2, 0x30, 0x50, 0x89, 0x6E, 0xCB, 0x56, 0xB2, 0xEB, 0xC0, 0x91, 0x16, - 0x92, 0x5E, 0x30, 0x8E, 0xAE, 0xC7, 0x94, 0x5D, 0xFD, 0x35, 0xE1, 0x20, 0xF8, 0xAD, - 0x3E, 0xBC, 0x08, 0xBF, 0xC0, 0x36, 0x74, 0x9F, 0xD5, 0xBB, 0x52, 0x08, 0xFD, 0x06, - 0x66, 0xF3, 0x7A, 0xB3, 0x04, 0xF4, 0x75, 0x29, 0x5D, 0xE9, 0x5F, 0xAA, 0x10, 0x30, - 0xB2, 0x0F, 0x5A, 0x1A, 0xC1, 0x2A, 0xB3, 0xFE, 0xCB, 0x21, 0xAD, 0x80, 0xEC, 0x8F, - 0x20, 0x09, 0x1C, 0xDB, 0xC5, 0x58, 0x94, 0xC2, 0x9C, 0xC6, 0xCE, 0x82, 0x65, 0x3E, - 0x57, 0x90, 0xBC, 0xA9, 0x8B, 0x06, 0xB4, 0xF0, 0x72, 0xF6, 0x77, 0xDF, 0x98, 0x64, - 0xF1, 0xEC, 0xFE, 0x37, 0x2D, 0xBC, 0xAE, 0x8C, 0x08, 0x81, 0x1F, 0xC3, 0xC9, 0x89, - 0x1A, 0xC7, 0x42, 0x82, 0x4B, 0x2E, 0xDC, 0x8E, 0x8D, 0x73, 0xCE, 0xB1, 0xCC, 0x01, - 0xD9, 0x08, 0x70, 0x87, 0x3C, 0x44, 0x08, 0xEC, 0x49, 0x8F, 0x81, 0x5A, 0xE2, 0x40, - 0xFF, 0x77, 0xFC, 0x0D}; - Exponent1 = new CryptoPP::byte[0x80]{ - 0x52, 0xCC, 0x2D, 0xA0, 0x9C, 0x9E, 0x75, 0xE7, 0x28, 0xEE, 0x3D, 0xDE, 0xE3, - 0x45, 0xD1, 0x4F, 0x94, 0x1C, 0xCC, 0xC8, 0x87, 0x29, 0x45, 0x3B, 0x8D, 0x6E, - 0xAB, 0x6E, 0x2A, 0xA7, 0xC7, 0x15, 0x43, 0xA3, 0x04, 0x8F, 0x90, 0x5F, 0xEB, - 0xF3, 0x38, 0x4A, 0x77, 0xFA, 0x36, 0xB7, 0x15, 0x76, 0xB6, 0x01, 0x1A, 0x8E, - 0x25, 0x87, 0x82, 0xF1, 0x55, 0xD8, 0xC6, 0x43, 0x2A, 0xC0, 0xE5, 0x98, 0xC9, - 0x32, 0xD1, 0x94, 0x6F, 0xD9, 0x01, 0xBA, 0x06, 0x81, 0xE0, 0x6D, 0x88, 0xF2, - 0x24, 0x2A, 0x25, 0x01, 0x64, 0x5C, 0xBF, 0xF2, 0xD9, 0x99, 0x67, 0x3E, 0xF6, - 0x72, 0xEE, 0xE4, 0xE2, 0x33, 0x5C, 0xF8, 0x00, 0x40, 0xE3, 0x2A, 0x9A, 0xF4, - 0x3D, 0x22, 0x86, 0x44, 0x3C, 0xFB, 0x0A, 0xA5, 0x7C, 0x3F, 0xCC, 0xF5, 0xF1, - 0x16, 0xC4, 0xAC, 0x88, 0xB4, 0xDE, 0x62, 0x94, 0x92, 0x6A, 0x13}; - Exponent2 = new CryptoPP::byte[0x80]{ - 0x7C, 0x9D, 0xAD, 0x39, 0xE0, 0xD5, 0x60, 0x14, 0x94, 0x48, 0x19, 0x7F, 0x88, - 0x95, 0xD5, 0x8B, 0x80, 0xAD, 0x85, 0x8A, 0x4B, 0x77, 0x37, 0x85, 0xD0, 0x77, - 0xBB, 0xBF, 0x89, 0x71, 0x4A, 0x72, 0xCB, 0x72, 0x68, 0x38, 0xEC, 0x02, 0xC6, - 0x7D, 0xC6, 0x44, 0x06, 0x33, 0x51, 0x1C, 0xC0, 0xFF, 0x95, 0x8F, 0x0D, 0x75, - 0xDC, 0x25, 0xBB, 0x0B, 0x73, 0x91, 0xA9, 0x6D, 0x42, 0xD8, 0x03, 0xB7, 0x68, - 0xD4, 0x1E, 0x75, 0x62, 0xA3, 0x70, 0x35, 0x79, 0x78, 0x00, 0xC8, 0xF5, 0xEF, - 0x15, 0xB9, 0xFC, 0x4E, 0x47, 0x5A, 0xC8, 0x70, 0x70, 0x5B, 0x52, 0x98, 0xC0, - 0xC2, 0x58, 0x4A, 0x70, 0x96, 0xCC, 0xB8, 0x10, 0xE1, 0x2F, 0x78, 0x8B, 0x2B, - 0xA1, 0x7F, 0xF9, 0xAC, 0xDE, 0xF0, 0xBB, 0x2B, 0xE2, 0x66, 0xE3, 0x22, 0x92, - 0x31, 0x21, 0x57, 0x92, 0xC4, 0xB8, 0xF2, 0x3E, 0x76, 0x20, 0x37}; - Coefficient = new CryptoPP::byte[0x80]{ - 0x45, 0x97, 0x55, 0xD4, 0x22, 0x08, 0x5E, 0xF3, 0x5C, 0xB4, 0x05, 0x7A, 0xFD, - 0xAA, 0x42, 0x42, 0xAD, 0x9A, 0x8C, 0xA0, 0x6C, 0xBB, 0x1D, 0x68, 0x54, 0x54, - 0x6E, 0x3E, 0x32, 0xE3, 0x53, 0x73, 0x76, 0xF1, 0x3E, 0x01, 0xEA, 0xD3, 0xCF, - 0xEB, 0xEB, 0x23, 0x3E, 0xC0, 0xBE, 0xCE, 0xEC, 0x2C, 0x89, 0x5F, 0xA8, 0x27, - 0x3A, 0x4C, 0xB7, 0xE6, 0x74, 0xBC, 0x45, 0x4C, 0x26, 0xC8, 0x25, 0xFF, 0x34, - 0x63, 0x25, 0x37, 0xE1, 0x48, 0x10, 0xC1, 0x93, 0xA6, 0xAF, 0xEB, 0xBA, 0xE3, - 0xA2, 0xF1, 0x3D, 0xEF, 0x63, 0xD8, 0xF4, 0xFD, 0xD3, 0xEE, 0xE2, 0x5D, 0xE9, - 0x33, 0xCC, 0xAD, 0xBA, 0x75, 0x5C, 0x85, 0xAF, 0xCE, 0xA9, 0x3D, 0xD1, 0xA2, - 0x17, 0xF3, 0xF6, 0x98, 0xB3, 0x50, 0x8E, 0x5E, 0xF6, 0xEB, 0x02, 0x8E, 0xA1, - 0x62, 0xA7, 0xD6, 0x2C, 0xEC, 0x91, 0xFF, 0x15, 0x40, 0xD2, 0xE3}; - Modulus = new CryptoPP::byte[0x100]{ - 0xd2, 0x12, 0xfc, 0x33, 0x5f, 0x6d, 0xdb, 0x83, 0x16, 0x09, 0x62, 0x8b, 0x03, 0x56, - 0x27, 0x37, 0x82, 0xd4, 0x77, 0x85, 0x35, 0x29, 0x39, 0x2d, 0x52, 0x6b, 0x8c, 0x4c, - 0x8c, 0xfb, 0x06, 0xc1, 0x84, 0x5b, 0xe7, 0xd4, 0xf7, 0xbc, 0xd2, 0x4e, 0x62, 0x45, - 0xcd, 0x2a, 0xbb, 0xd7, 0x77, 0x76, 0x45, 0x36, 0x55, 0x27, 0x3f, 0xb3, 0xf5, 0xf9, - 0x8e, 0xda, 0x4b, 0xef, 0xaa, 0x59, 0xae, 0xb3, 0x9b, 0xea, 0x54, 0x98, 0xd2, 0x06, - 0x32, 0x6a, 0x58, 0x31, 0x2a, 0xe0, 0xd4, 0x4f, 0x90, 0xb5, 0x0a, 0x7d, 0xec, 0xf4, - 0x3a, 0x9c, 0x52, 0x67, 0x2d, 0x99, 0x31, 0x8e, 0x0c, 0x43, 0xe6, 0x82, 0xfe, 0x07, - 0x46, 0xe1, 0x2e, 0x50, 0xd4, 0x1f, 0x2d, 0x2f, 0x7e, 0xd9, 0x08, 0xba, 0x06, 0xb3, - 0xbf, 0x2e, 0x20, 0x3f, 0x4e, 0x3f, 0xfe, 0x44, 0xff, 0xaa, 0x50, 0x43, 0x57, 0x91, - 0x69, 0x94, 0x49, 0x15, 0x82, 0x82, 0xe4, 0x0f, 0x4c, 0x8d, 0x9d, 0x2c, 0xc9, 0x5b, - 0x1d, 0x64, 0xbf, 0x88, 0x8b, 0xd4, 0xc5, 0x94, 0xe7, 0x65, 0x47, 0x84, 0x1e, 0xe5, - 0x79, 0x10, 0xfb, 0x98, 0x93, 0x47, 0xb9, 0x7d, 0x85, 0x12, 0xa6, 0x40, 0x98, 0x2c, - 0xf7, 0x92, 0xbc, 0x95, 0x19, 0x32, 0xed, 0xe8, 0x90, 0x56, 0x0d, 0x65, 0xc1, 0xaa, - 0x78, 0xc6, 0x2e, 0x54, 0xfd, 0x5f, 0x54, 0xa1, 0xf6, 0x7e, 0xe5, 0xe0, 0x5f, 0x61, - 0xc1, 0x20, 0xb4, 0xb9, 0xb4, 0x33, 0x08, 0x70, 0xe4, 0xdf, 0x89, 0x56, 0xed, 0x01, - 0x29, 0x46, 0x77, 0x5f, 0x8c, 0xb8, 0xa9, 0xf5, 0x1e, 0x2e, 0xb3, 0xb9, 0xbf, 0xe0, - 0x09, 0xb7, 0x8d, 0x28, 0xd4, 0xa6, 0xc3, 0xb8, 0x1e, 0x1f, 0x07, 0xeb, 0xb4, 0x12, - 0x0b, 0x95, 0xb8, 0x85, 0x30, 0xfd, 0xdc, 0x39, 0x13, 0xd0, 0x7c, 0xdc, 0x8f, 0xed, - 0xf9, 0xc9, 0xa3, 0xc1}; - PublicExponent = new CryptoPP::byte[4]{0, 1, 0, 1}; - }; + static constexpr CryptoPP::byte Prime2[] = { + 0xD7, 0xA1, 0x0F, 0x9A, 0x8B, 0xF2, 0xC9, 0x11, 0x95, 0x32, 0x9A, 0x8C, 0xF0, 0xD9, 0x40, + 0x47, 0xF5, 0x68, 0xA0, 0x0D, 0xBD, 0xC1, 0xFC, 0x43, 0x2F, 0x65, 0xF9, 0xC3, 0x61, 0x0F, + 0x25, 0x77, 0x54, 0xAD, 0xD7, 0x58, 0xAC, 0x84, 0x40, 0x60, 0x8D, 0x3F, 0xF3, 0x65, 0x89, + 0x75, 0xB5, 0xC6, 0x2C, 0x51, 0x1A, 0x2F, 0x1F, 0x22, 0xE4, 0x43, 0x11, 0x54, 0xBE, 0xC9, + 0xB4, 0xC7, 0xB5, 0x1B, 0x05, 0x0B, 0xBC, 0x56, 0x9A, 0xCD, 0x4A, 0xD9, 0x73, 0x68, 0x5E, + 0x5C, 0xFB, 0x92, 0xB7, 0x8B, 0x0D, 0xFF, 0xF5, 0x07, 0xCA, 0xB4, 0xC8, 0x9B, 0x96, 0x3C, + 0x07, 0x9E, 0x3E, 0x6B, 0x2A, 0x11, 0xF2, 0x8A, 0xB1, 0x8A, 0xD7, 0x2E, 0x1B, 0xA5, 0x53, + 0x24, 0x06, 0xED, 0x50, 0xB8, 0x90, 0x67, 0xB1, 0xE2, 0x41, 0xC6, 0x92, 0x01, 0xEE, 0x10, + 0xF0, 0x61, 0xBB, 0xFB, 0xB2, 0x7D, 0x4A, 0x73}; + static constexpr CryptoPP::byte PrivateExponent[] = { + 0x32, 0xD9, 0x03, 0x90, 0x8F, 0xBD, 0xB0, 0x8F, 0x57, 0x2B, 0x28, 0x5E, 0x0B, 0x8D, 0xB3, + 0xEA, 0x5C, 0xD1, 0x7E, 0xA8, 0x90, 0x88, 0x8C, 0xDD, 0x6A, 0x80, 0xBB, 0xB1, 0xDF, 0xC1, + 0xF7, 0x0D, 0xAA, 0x32, 0xF0, 0xB7, 0x7C, 0xCB, 0x88, 0x80, 0x0E, 0x8B, 0x64, 0xB0, 0xBE, + 0x4C, 0xD6, 0x0E, 0x9B, 0x8C, 0x1E, 0x2A, 0x64, 0xE1, 0xF3, 0x5C, 0xD7, 0x76, 0x01, 0x41, + 0x5E, 0x93, 0x5C, 0x94, 0xFE, 0xDD, 0x46, 0x62, 0xC3, 0x1B, 0x5A, 0xE2, 0xA0, 0xBC, 0x2D, + 0xEB, 0xC3, 0x98, 0x0A, 0xA7, 0xB7, 0x85, 0x69, 0x70, 0x68, 0x2B, 0x64, 0x4A, 0xB3, 0x1F, + 0xCC, 0x7D, 0xDC, 0x7C, 0x26, 0xF4, 0x77, 0xF6, 0x5C, 0xF2, 0xAE, 0x5A, 0x44, 0x2D, 0xD3, + 0xAB, 0x16, 0x62, 0x04, 0x19, 0xBA, 0xFB, 0x90, 0xFF, 0xE2, 0x30, 0x50, 0x89, 0x6E, 0xCB, + 0x56, 0xB2, 0xEB, 0xC0, 0x91, 0x16, 0x92, 0x5E, 0x30, 0x8E, 0xAE, 0xC7, 0x94, 0x5D, 0xFD, + 0x35, 0xE1, 0x20, 0xF8, 0xAD, 0x3E, 0xBC, 0x08, 0xBF, 0xC0, 0x36, 0x74, 0x9F, 0xD5, 0xBB, + 0x52, 0x08, 0xFD, 0x06, 0x66, 0xF3, 0x7A, 0xB3, 0x04, 0xF4, 0x75, 0x29, 0x5D, 0xE9, 0x5F, + 0xAA, 0x10, 0x30, 0xB2, 0x0F, 0x5A, 0x1A, 0xC1, 0x2A, 0xB3, 0xFE, 0xCB, 0x21, 0xAD, 0x80, + 0xEC, 0x8F, 0x20, 0x09, 0x1C, 0xDB, 0xC5, 0x58, 0x94, 0xC2, 0x9C, 0xC6, 0xCE, 0x82, 0x65, + 0x3E, 0x57, 0x90, 0xBC, 0xA9, 0x8B, 0x06, 0xB4, 0xF0, 0x72, 0xF6, 0x77, 0xDF, 0x98, 0x64, + 0xF1, 0xEC, 0xFE, 0x37, 0x2D, 0xBC, 0xAE, 0x8C, 0x08, 0x81, 0x1F, 0xC3, 0xC9, 0x89, 0x1A, + 0xC7, 0x42, 0x82, 0x4B, 0x2E, 0xDC, 0x8E, 0x8D, 0x73, 0xCE, 0xB1, 0xCC, 0x01, 0xD9, 0x08, + 0x70, 0x87, 0x3C, 0x44, 0x08, 0xEC, 0x49, 0x8F, 0x81, 0x5A, 0xE2, 0x40, 0xFF, 0x77, 0xFC, + 0x0D}; }; \ No newline at end of file diff --git a/src/core/file_format/pkg.cpp b/src/core/file_format/pkg.cpp index 336d81019..d86f3b28d 100644 --- a/src/core/file_format/pkg.cpp +++ b/src/core/file_format/pkg.cpp @@ -67,15 +67,19 @@ bool PKG::Open(const std::filesystem::path& filepath) { file.Seek(0x47); // skip first 7 characters of content_id file.Read(pkgTitleID); - file.Seek(0); - pkg.resize(pkgheader.pkg_promote_size); - file.Read(pkg); - u32 offset = pkgheader.pkg_table_entry_offset; u32 n_files = pkgheader.pkg_table_entry_count; + + file.Seek(offset); for (int i = 0; i < n_files; i++) { - PKGEntry entry; - std::memcpy(&entry, &pkg[offset + i * 0x20], sizeof(entry)); + PKGEntry entry{}; + file.Read(entry.id); + file.Read(entry.filename_offset); + file.Read(entry.flags1); + file.Read(entry.flags2); + file.Read(entry.offset); + file.Read(entry.size); + file.Seek(8, Common::FS::SeekOrigin::CurrentPosition); // Try to figure out the name const auto name = GetEntryNameByType(entry.id); @@ -113,9 +117,6 @@ bool PKG::Extract(const std::filesystem::path& filepath, const std::filesystem:: failreason = "Content size is bigger than pkg size"; return false; } - file.Seek(0); - pkg.resize(pkgheader.pkg_promote_size); - file.Read(pkg); u32 offset = pkgheader.pkg_table_entry_offset; u32 n_files = pkgheader.pkg_table_entry_count; @@ -126,9 +127,18 @@ bool PKG::Extract(const std::filesystem::path& filepath, const std::filesystem:: std::array, 7> key1; std::array imgkeydata; + file.Seek(offset); for (int i = 0; i < n_files; i++) { - PKGEntry entry; - std::memcpy(&entry, &pkg[offset + i * 0x20], sizeof(entry)); + PKGEntry entry{}; + file.Read(entry.id); + file.Read(entry.filename_offset); + file.Read(entry.flags1); + file.Read(entry.flags2); + file.Read(entry.offset); + file.Read(entry.size); + file.Seek(8, Common::FS::SeekOrigin::CurrentPosition); + + auto currentPos = file.Tell(); // Try to figure out the name const auto name = GetEntryNameByType(entry.id); @@ -139,8 +149,15 @@ bool PKG::Extract(const std::filesystem::path& filepath, const std::filesystem:: // Just print with id Common::FS::IOFile out(extract_path / "sce_sys" / std::to_string(entry.id), Common::FS::FileAccessMode::Write); - out.WriteRaw(pkg.data() + entry.offset, entry.size); + file.Seek(entry.offset); + + std::vector data; + data.resize(entry.size); + file.ReadRaw(data.data(), entry.size); + out.WriteRaw(data.data(), entry.size); out.Close(); + + file.Seek(currentPos); continue; } @@ -178,14 +195,25 @@ bool PKG::Extract(const std::filesystem::path& filepath, const std::filesystem:: } Common::FS::IOFile out(extract_path / "sce_sys" / name, Common::FS::FileAccessMode::Write); - out.WriteRaw(pkg.data() + entry.offset, entry.size); + file.Seek(entry.offset); + + std::vector data; + data.resize(entry.size); + file.ReadRaw(data.data(), entry.size); + out.WriteRaw(data.data(), entry.size); out.Close(); // Decrypt Np stuff and overwrite. if (entry.id == 0x400 || entry.id == 0x401 || entry.id == 0x402 || entry.id == 0x403) { // somehow 0x401 is not decrypting decNp.resize(entry.size); - std::span cipherNp(pkg.data() + entry.offset, entry.size); + file.Seek(entry.offset); + + std::vector data; + data.resize(entry.size); + file.ReadRaw(data.data(), entry.size); + + std::span cipherNp(data.data(), entry.size); std::array concatenated_ivkey_dk3_; std::memcpy(concatenated_ivkey_dk3_.data(), &entry, sizeof(entry)); std::memcpy(concatenated_ivkey_dk3_.data() + sizeof(entry), dk3_.data(), sizeof(dk3_)); @@ -197,6 +225,8 @@ bool PKG::Extract(const std::filesystem::path& filepath, const std::filesystem:: out.Write(decNp); out.Close(); } + + file.Seek(currentPos); } // Extract trophy files @@ -214,28 +244,31 @@ bool PKG::Extract(const std::filesystem::path& filepath, const std::filesystem:: PKG::crypto.PfsGenCryptoKey(ekpfsKey, seed, dataKey, tweakKey); const u32 length = pkgheader.pfs_cache_size * 0x2; // Seems to be ok. - // Read encrypted pfs_image - std::vector pfs_encrypted(length); - file.Seek(pkgheader.pfs_image_offset); - file.Read(pfs_encrypted); - file.Close(); - // Decrypt the pfs_image. - std::vector pfs_decrypted(length); - PKG::crypto.decryptPFS(dataKey, tweakKey, pfs_encrypted, pfs_decrypted, 0); - - // Retrieve PFSC from decrypted pfs_image. - pfsc_offset = GetPFSCOffset(pfs_decrypted); + int num_blocks = 0; std::vector pfsc(length); - std::memcpy(pfsc.data(), pfs_decrypted.data() + pfsc_offset, length - pfsc_offset); + if (length != 0) { + // Read encrypted pfs_image + std::vector pfs_encrypted(length); + file.Seek(pkgheader.pfs_image_offset); + file.Read(pfs_encrypted); + file.Close(); + // Decrypt the pfs_image. + std::vector pfs_decrypted(length); + PKG::crypto.decryptPFS(dataKey, tweakKey, pfs_encrypted, pfs_decrypted, 0); - PFSCHdr pfsChdr; - std::memcpy(&pfsChdr, pfsc.data(), sizeof(pfsChdr)); + // Retrieve PFSC from decrypted pfs_image. + pfsc_offset = GetPFSCOffset(pfs_decrypted); + std::memcpy(pfsc.data(), pfs_decrypted.data() + pfsc_offset, length - pfsc_offset); - const int num_blocks = (int)(pfsChdr.data_length / pfsChdr.block_sz2); - sectorMap.resize(num_blocks + 1); // 8 bytes, need extra 1 to get the last offset. + PFSCHdr pfsChdr; + std::memcpy(&pfsChdr, pfsc.data(), sizeof(pfsChdr)); - for (int i = 0; i < num_blocks + 1; i++) { - std::memcpy(§orMap[i], pfsc.data() + pfsChdr.block_offsets + i * 8, 8); + num_blocks = (int)(pfsChdr.data_length / pfsChdr.block_sz2); + sectorMap.resize(num_blocks + 1); // 8 bytes, need extra 1 to get the last offset. + + for (int i = 0; i < num_blocks + 1; i++) { + std::memcpy(§orMap[i], pfsc.data() + pfsChdr.block_offsets + i * 8, 8); + } } u32 ent_size = 0; @@ -279,8 +312,8 @@ bool PKG::Extract(const std::filesystem::path& filepath, const std::filesystem:: } } - // let's deal with the root/uroot enteries here. - // Sometimes it's more than 2 enteries (Tomb Raider Remastered) + // let's deal with the root/uroot entries here. + // Sometimes it's more than 2 entries (Tomb Raider Remastered) const std::string_view flat_path_table(&decompressedData[0x10], 15); if (flat_path_table == "flat_path_table") { uroot_reached = true; @@ -296,7 +329,15 @@ bool PKG::Extract(const std::filesystem::path& filepath, const std::filesystem:: } else { // Set the the folder according to the current inode. // Can be 2 or more (rarely) - extractPaths[ndinode_counter] = extract_path.parent_path() / GetTitleID(); + auto parent_path = extract_path.parent_path(); + auto title_id = GetTitleID(); + + if (parent_path.filename() != title_id) { + extractPaths[ndinode_counter] = parent_path / title_id; + } else { + // DLCs path has different structure + extractPaths[ndinode_counter] = extract_path; + } uroot_reached = false; break; } diff --git a/src/core/file_format/pkg.h b/src/core/file_format/pkg.h index b6b09a191..d30d50b44 100644 --- a/src/core/file_format/pkg.h +++ b/src/core/file_format/pkg.h @@ -149,7 +149,6 @@ public: private: Crypto crypto; TRP trp; - std::vector pkg; u64 pkgSize = 0; char pkgTitleID[9]; PKGHeader pkgheader; diff --git a/src/core/file_format/playgo_chunk.cpp b/src/core/file_format/playgo_chunk.cpp index a430b1ac1..4e2faea45 100644 --- a/src/core/file_format/playgo_chunk.cpp +++ b/src/core/file_format/playgo_chunk.cpp @@ -60,7 +60,7 @@ bool PlaygoFile::LoadChunks(const Common::FS::IOFile& file) { return false; } -bool PlaygoFile::load_chunk_data(const Common::FS::IOFile& file, const chunk_t& chunk, +bool PlaygoFile::load_chunk_data(const Common::FS::IOFile& file, const chunk_t chunk, std::string& data) { if (file.IsOpen()) { if (file.Seek(chunk.offset)) { diff --git a/src/core/file_format/playgo_chunk.h b/src/core/file_format/playgo_chunk.h index 439e27d05..b6d38e0e1 100644 --- a/src/core/file_format/playgo_chunk.h +++ b/src/core/file_format/playgo_chunk.h @@ -123,7 +123,7 @@ public: } private: - bool load_chunk_data(const Common::FS::IOFile& file, const chunk_t& chunk, std::string& data); + bool load_chunk_data(const Common::FS::IOFile& file, const chunk_t chunk, std::string& data); private: PlaygoHeader playgoHeader; diff --git a/src/core/file_format/psf.cpp b/src/core/file_format/psf.cpp index c11dc05c8..3d076acdc 100644 --- a/src/core/file_format/psf.cpp +++ b/src/core/file_format/psf.cpp @@ -9,7 +9,7 @@ PSF::PSF() = default; PSF::~PSF() = default; -bool PSF::open(const std::string& filepath, std::vector psfBuffer) { +bool PSF::open(const std::string& filepath, const std::vector& psfBuffer) { if (!psfBuffer.empty()) { psf.resize(psfBuffer.size()); psf = psfBuffer; diff --git a/src/core/file_format/psf.h b/src/core/file_format/psf.h index 17e515de2..9a162b1d5 100644 --- a/src/core/file_format/psf.h +++ b/src/core/file_format/psf.h @@ -35,7 +35,7 @@ public: PSF(); ~PSF(); - bool open(const std::string& filepath, std::vector psfBuffer); + bool open(const std::string& filepath, const std::vector& psfBuffer); std::string GetString(const std::string& key); u32 GetInteger(const std::string& key); diff --git a/src/core/file_sys/fs.cpp b/src/core/file_sys/fs.cpp index 40d8212bb..199e42a04 100644 --- a/src/core/file_sys/fs.cpp +++ b/src/core/file_sys/fs.cpp @@ -48,7 +48,7 @@ std::filesystem::path MntPoints::GetHostPath(std::string_view guest_directory) { pos = mount->mount.size() + 1; const auto rel_path = std::string_view(corrected_path).substr(pos); const auto host_path = mount->host_path / rel_path; - if (!NeedsCaseInsensiveSearch) { + if (!NeedsCaseInsensitiveSearch) { return host_path; } diff --git a/src/core/file_sys/fs.h b/src/core/file_sys/fs.h index b0fb63242..2c55b0513 100644 --- a/src/core/file_sys/fs.h +++ b/src/core/file_sys/fs.h @@ -14,9 +14,9 @@ namespace Core::FileSys { class MntPoints { #ifdef _WIN64 - static constexpr bool NeedsCaseInsensiveSearch = false; + static constexpr bool NeedsCaseInsensitiveSearch = false; #else - static constexpr bool NeedsCaseInsensiveSearch = true; + static constexpr bool NeedsCaseInsensitiveSearch = true; #endif public: struct MntPair { diff --git a/src/core/libraries/ajm/ajm.cpp b/src/core/libraries/ajm/ajm.cpp index d8d8304ab..441a07f63 100644 --- a/src/core/libraries/ajm/ajm.cpp +++ b/src/core/libraries/ajm/ajm.cpp @@ -1,9 +1,10 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -// Generated By moduleGenerator +#include "ajm.h" +#include "ajm_error.h" + #include "common/logging/log.h" -#include "core/libraries/ajm/ajm.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" diff --git a/src/core/libraries/ajm/ajm_error.h b/src/core/libraries/ajm/ajm_error.h new file mode 100644 index 000000000..64129554f --- /dev/null +++ b/src/core/libraries/ajm/ajm_error.h @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +constexpr int ORBIS_AJM_ERROR_UNKNOWN = 0x80930001; +constexpr int ORBIS_AJM_ERROR_INVALID_CONTEXT = 0x80930002; +constexpr int ORBIS_AJM_ERROR_INVALID_INSTANCE = 0x80930003; +constexpr int ORBIS_AJM_ERROR_INVALID_BATCH = 0x80930004; +constexpr int ORBIS_AJM_ERROR_INVALID_PARAMETER = 0x80930005; +constexpr int ORBIS_AJM_ERROR_OUT_OF_MEMORY = 0x80930006; +constexpr int ORBIS_AJM_ERROR_OUT_OF_RESOURCES = 0x80930007; +constexpr int ORBIS_AJM_ERROR_CODEC_NOT_SUPPORTED = 0x80930008; +constexpr int ORBIS_AJM_ERROR_CODEC_ALREADY_REGISTERED = 0x80930009; +constexpr int ORBIS_AJM_ERROR_CODEC_NOT_REGISTERED = 0x8093000A; +constexpr int ORBIS_AJM_ERROR_WRONG_REVISION_FLAG = 0x8093000B; +constexpr int ORBIS_AJM_ERROR_FLAG_NOT_SUPPORTED = 0x8093000C; +constexpr int ORBIS_AJM_ERROR_BUSY = 0x8093000D; +constexpr int ORBIS_AJM_ERROR_BAD_PRIORITY = 0x8093000E; +constexpr int ORBIS_AJM_ERROR_IN_PROGRESS = 0x8093000F; +constexpr int ORBIS_AJM_ERROR_RETRY = 0x80930010; +constexpr int ORBIS_AJM_ERROR_MALFORMED_BATCH = 0x80930011; +constexpr int ORBIS_AJM_ERROR_JOB_CREATION = 0x80930012; +constexpr int ORBIS_AJM_ERROR_INVALID_OPCODE = 0x80930013; +constexpr int ORBIS_AJM_ERROR_PRIORITY_VIOLATION = 0x80930014; +constexpr int ORBIS_AJM_ERROR_BUFFER_TOO_BIG = 0x80930015; +constexpr int ORBIS_AJM_ERROR_INVALID_ADDRESS = 0x80930016; +constexpr int ORBIS_AJM_ERROR_CANCELLED = 0x80930017; diff --git a/src/core/libraries/app_content/app_content.cpp b/src/core/libraries/app_content/app_content.cpp index 882f99e49..125d19684 100644 --- a/src/core/libraries/app_content/app_content.cpp +++ b/src/core/libraries/app_content/app_content.cpp @@ -1,20 +1,38 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -// Generated By moduleGenerator #include -#include -#include -#include -#include + #include "app_content.h" #include "common/io_file.h" #include "common/logging/log.h" +#include "common/path_util.h" +#include "common/singleton.h" +#include "common/string_util.h" +#include "core/file_format/psf.h" +#include "core/file_sys/fs.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" namespace Libraries::AppContent { +int32_t addcont_count = 0; + +struct AddContInfo { + char entitlementLabel[ORBIS_NP_UNIFIED_ENTITLEMENT_LABEL_SIZE]; + OrbisAppContentAddcontDownloadStatus status; + OrbisAppContentGetEntitlementKey key; +}; + +std::array addcont_info = {{ + {"0000000000000000", + ORBIS_APP_CONTENT_ADDCONT_DOWNLOAD_STATUS_INSTALLED, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00}}, +}}; + +std::string title_id; + int PS4_SYSV_ABI _Z5dummyv() { LOG_ERROR(Lib_AppContent, "(STUBBED) called"); return ORBIS_OK; @@ -35,9 +53,31 @@ int PS4_SYSV_ABI sceAppContentAddcontEnqueueDownloadSp() { return ORBIS_OK; } -int PS4_SYSV_ABI sceAppContentAddcontMount() { - LOG_ERROR(Lib_AppContent, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceAppContentAddcontMount(u32 service_label, + const OrbisNpUnifiedEntitlementLabel* entitlement_label, + OrbisAppContentMountPoint* mount_point) { + LOG_INFO(Lib_AppContent, "called"); + + const auto& mount_dir = Common::FS::GetUserPath(Common::FS::PathType::AddonsDir) / title_id / + entitlement_label->data; + auto* mnt = Common::Singleton::Instance(); + + for (int i = 0; i < addcont_count; i++) { + if (strncmp(entitlement_label->data, addcont_info[i].entitlementLabel, + ORBIS_NP_UNIFIED_ENTITLEMENT_LABEL_SIZE - 1) != 0) { + continue; + } + + if (addcont_info[i].status != ORBIS_APP_CONTENT_ADDCONT_DOWNLOAD_STATUS_INSTALLED) { + return ORBIS_APP_CONTENT_ERROR_NOT_FOUND; + } + + snprintf(mount_point->data, ORBIS_APP_CONTENT_MOUNTPOINT_DATA_MAXSIZE, "/addcont%d", i); + mnt->Mount(mount_dir, mount_point->data); + return ORBIS_OK; + } + + return ORBIS_APP_CONTENT_ERROR_NOT_FOUND; } int PS4_SYSV_ABI sceAppContentAddcontShrink() { @@ -124,22 +164,80 @@ int PS4_SYSV_ABI sceAppContentGetAddcontDownloadProgress() { return ORBIS_OK; } -int PS4_SYSV_ABI sceAppContentGetAddcontInfo() { - LOG_ERROR(Lib_AppContent, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceAppContentGetAddcontInfo(u32 service_label, + const OrbisNpUnifiedEntitlementLabel* entitlementLabel, + OrbisAppContentAddcontInfo* info) { + LOG_INFO(Lib_AppContent, "called"); + + if (entitlementLabel == nullptr || info == nullptr) { + return ORBIS_APP_CONTENT_ERROR_PARAMETER; + } + + for (auto i = 0; i < addcont_count; i++) { + if (strncmp(entitlementLabel->data, addcont_info[i].entitlementLabel, + ORBIS_NP_UNIFIED_ENTITLEMENT_LABEL_SIZE - 1) != 0) { + continue; + } + + LOG_INFO(Lib_AppContent, "found DLC {}", entitlementLabel->data); + + strncpy(info->entitlement_label.data, addcont_info[i].entitlementLabel, + ORBIS_NP_UNIFIED_ENTITLEMENT_LABEL_SIZE); + info->status = addcont_info[i].status; + return ORBIS_OK; + } + + return ORBIS_APP_CONTENT_ERROR_DRM_NO_ENTITLEMENT; } int PS4_SYSV_ABI sceAppContentGetAddcontInfoList(u32 service_label, OrbisAppContentAddcontInfo* list, u32 list_num, u32* hit_num) { - *hit_num = 0; - LOG_ERROR(Lib_AppContent, "(DUMMY) called"); + LOG_INFO(Lib_AppContent, "called"); + + if (list_num == 0 || list == nullptr) { + if (hit_num == nullptr) { + return ORBIS_APP_CONTENT_ERROR_PARAMETER; + } + + *hit_num = addcont_count; + return ORBIS_OK; + } + + int dlcs_to_list = addcont_count < list_num ? addcont_count : list_num; + for (int i = 0; i < dlcs_to_list; i++) { + strncpy(list[i].entitlement_label.data, addcont_info[i].entitlementLabel, + ORBIS_NP_UNIFIED_ENTITLEMENT_LABEL_SIZE); + list[i].status = addcont_info[i].status; + } + + if (hit_num != nullptr) { + *hit_num = dlcs_to_list; + } + return ORBIS_OK; } -int PS4_SYSV_ABI sceAppContentGetEntitlementKey() { - LOG_ERROR(Lib_AppContent, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceAppContentGetEntitlementKey( + u32 service_label, const OrbisNpUnifiedEntitlementLabel* entitlement_label, + OrbisAppContentGetEntitlementKey* key) { + LOG_ERROR(Lib_AppContent, "called"); + + if (entitlement_label == nullptr || key == nullptr) { + return ORBIS_APP_CONTENT_ERROR_PARAMETER; + } + + for (int i = 0; i < addcont_count; i++) { + if (strncmp(entitlement_label->data, addcont_info[i].entitlementLabel, + ORBIS_NP_UNIFIED_ENTITLEMENT_LABEL_SIZE - 1) != 0) { + continue; + } + + memcpy(key->data, addcont_info[i].key.data, ORBIS_APP_CONTENT_ENTITLEMENT_KEY_SIZE); + return ORBIS_OK; + } + + return ORBIS_APP_CONTENT_ERROR_DRM_NO_ENTITLEMENT; } int PS4_SYSV_ABI sceAppContentGetRegion() { @@ -150,7 +248,25 @@ int PS4_SYSV_ABI sceAppContentGetRegion() { int PS4_SYSV_ABI sceAppContentInitialize(const OrbisAppContentInitParam* initParam, OrbisAppContentBootParam* bootParam) { LOG_ERROR(Lib_AppContent, "(DUMMY) called"); - bootParam->attr = 0; // always 0 + auto* param_sfo = Common::Singleton::Instance(); + + const auto addons_dir = Common::FS::GetUserPath(Common::FS::PathType::AddonsDir); + title_id = param_sfo->GetString("TITLE_ID"); + auto addon_path = addons_dir / title_id; + if (std::filesystem::exists(addon_path)) { + for (const auto& entry : std::filesystem::directory_iterator(addon_path)) { + if (entry.is_directory()) { + auto entitlement_label = entry.path().filename().string(); + + AddContInfo info{}; + info.status = ORBIS_APP_CONTENT_ADDCONT_DOWNLOAD_STATUS_INSTALLED; + strcpy(info.entitlementLabel, entitlement_label.c_str()); + + addcont_info[addcont_count++] = info; + } + } + } + return ORBIS_OK; } @@ -221,12 +337,12 @@ int PS4_SYSV_ABI Func_C59A36FF8D7C59DA() { return ORBIS_OK; } -int PS4_SYSV_ABI sceAppContentAddcontEnqueueDownloadByEntitlemetId() { +int PS4_SYSV_ABI sceAppContentAddcontEnqueueDownloadByEntitlementId() { LOG_ERROR(Lib_AppContent, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceAppContentAddcontMountByEntitlemetId() { +int PS4_SYSV_ABI sceAppContentAddcontMountByEntitlementId() { LOG_ERROR(Lib_AppContent, "(STUBBED) called"); return ORBIS_OK; } @@ -313,9 +429,9 @@ void RegisterlibSceAppContent(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("xZo2-418Wdo", "libSceAppContentBundle", 1, "libSceAppContent", 1, 1, Func_C59A36FF8D7C59DA); LIB_FUNCTION("kJmjt81mXKQ", "libSceAppContentIro", 1, "libSceAppContent", 1, 1, - sceAppContentAddcontEnqueueDownloadByEntitlemetId); + sceAppContentAddcontEnqueueDownloadByEntitlementId); LIB_FUNCTION("efX3lrPwdKA", "libSceAppContentIro", 1, "libSceAppContent", 1, 1, - sceAppContentAddcontMountByEntitlemetId); + sceAppContentAddcontMountByEntitlementId); LIB_FUNCTION("z9hgjLd1SGA", "libSceAppContentIro", 1, "libSceAppContent", 1, 1, sceAppContentGetAddcontInfoByEntitlementId); LIB_FUNCTION("3wUaDTGmjcQ", "libSceAppContentIro", 1, "libSceAppContent", 1, 1, @@ -324,4 +440,4 @@ void RegisterlibSceAppContent(Core::Loader::SymbolsResolver* sym) { sceAppContentGetDownloadedStoreCountry); }; -} // namespace Libraries::AppContent \ No newline at end of file +} // namespace Libraries::AppContent diff --git a/src/core/libraries/app_content/app_content.h b/src/core/libraries/app_content/app_content.h index ecf69199e..a16da5b40 100644 --- a/src/core/libraries/app_content/app_content.h +++ b/src/core/libraries/app_content/app_content.h @@ -41,6 +41,16 @@ struct OrbisAppContentMountPoint { constexpr int ORBIS_APP_CONTENT_TEMPORARY_DATA_OPTION_NONE = 0; constexpr int ORBIS_APP_CONTENT_TEMPORARY_DATA_OPTION_FORMAT = (1 << 0); constexpr int ORBIS_NP_UNIFIED_ENTITLEMENT_LABEL_SIZE = 17; +constexpr int ORBIS_APP_CONTENT_ENTITLEMENT_KEY_SIZE = 16; +constexpr int ORBIS_APP_CONTENT_INFO_LIST_MAX_SIZE = 2500; + +enum OrbisAppContentAddcontDownloadStatus : u32 { + ORBIS_APP_CONTENT_ADDCONT_DOWNLOAD_STATUS_NO_EXTRA_DATA = 0, + ORBIS_APP_CONTENT_ADDCONT_DOWNLOAD_STATUS_NO_IN_QUEUE = 1, + ORBIS_APP_CONTENT_ADDCONT_DOWNLOAD_STATUS_DOWNLOADING = 2, + ORBIS_APP_CONTENT_ADDCONT_DOWNLOAD_STATUS_DOWNLOAD_SUSPENDED = 3, + ORBIS_APP_CONTENT_ADDCONT_DOWNLOAD_STATUS_INSTALLED = 4 +}; struct OrbisNpUnifiedEntitlementLabel { char data[ORBIS_NP_UNIFIED_ENTITLEMENT_LABEL_SIZE]; @@ -54,11 +64,17 @@ struct OrbisAppContentAddcontInfo { u32 status; }; +struct OrbisAppContentGetEntitlementKey { + char data[ORBIS_APP_CONTENT_ENTITLEMENT_KEY_SIZE]; +}; + int PS4_SYSV_ABI _Z5dummyv(); int PS4_SYSV_ABI sceAppContentAddcontDelete(); int PS4_SYSV_ABI sceAppContentAddcontEnqueueDownload(); int PS4_SYSV_ABI sceAppContentAddcontEnqueueDownloadSp(); -int PS4_SYSV_ABI sceAppContentAddcontMount(); +int PS4_SYSV_ABI sceAppContentAddcontMount(u32 service_label, + const OrbisNpUnifiedEntitlementLabel* entitlement_label, + OrbisAppContentMountPoint* mount_point); int PS4_SYSV_ABI sceAppContentAddcontShrink(); int PS4_SYSV_ABI sceAppContentAddcontUnmount(); int PS4_SYSV_ABI sceAppContentAppParamGetInt(OrbisAppContentAppParamId paramId, s32* value); @@ -70,11 +86,15 @@ int PS4_SYSV_ABI sceAppContentDownload1Shrink(); int PS4_SYSV_ABI sceAppContentDownloadDataFormat(); int PS4_SYSV_ABI sceAppContentDownloadDataGetAvailableSpaceKb(); int PS4_SYSV_ABI sceAppContentGetAddcontDownloadProgress(); -int PS4_SYSV_ABI sceAppContentGetAddcontInfo(); +int PS4_SYSV_ABI sceAppContentGetAddcontInfo(u32 service_label, + const OrbisNpUnifiedEntitlementLabel* entitlementLabel, + OrbisAppContentAddcontInfo* info); int PS4_SYSV_ABI sceAppContentGetAddcontInfoList(u32 service_label, OrbisAppContentAddcontInfo* list, u32 list_num, u32* hit_num); -int PS4_SYSV_ABI sceAppContentGetEntitlementKey(); +int PS4_SYSV_ABI sceAppContentGetEntitlementKey( + u32 service_label, const OrbisNpUnifiedEntitlementLabel* entitlement_label, + OrbisAppContentGetEntitlementKey* key); int PS4_SYSV_ABI sceAppContentGetRegion(); int PS4_SYSV_ABI sceAppContentInitialize(const OrbisAppContentInitParam* initParam, OrbisAppContentBootParam* bootParam); @@ -92,8 +112,8 @@ int PS4_SYSV_ABI sceAppContentTemporaryDataMount2(OrbisAppContentTemporaryDataOp int PS4_SYSV_ABI sceAppContentTemporaryDataUnmount(); int PS4_SYSV_ABI sceAppContentGetPftFlag(); int PS4_SYSV_ABI Func_C59A36FF8D7C59DA(); -int PS4_SYSV_ABI sceAppContentAddcontEnqueueDownloadByEntitlemetId(); -int PS4_SYSV_ABI sceAppContentAddcontMountByEntitlemetId(); +int PS4_SYSV_ABI sceAppContentAddcontEnqueueDownloadByEntitlementId(); +int PS4_SYSV_ABI sceAppContentAddcontMountByEntitlementId(); int PS4_SYSV_ABI sceAppContentGetAddcontInfoByEntitlementId(); int PS4_SYSV_ABI sceAppContentGetAddcontInfoListByIroTag(); int PS4_SYSV_ABI sceAppContentGetDownloadedStoreCountry(); diff --git a/src/core/libraries/audio/audioout.cpp b/src/core/libraries/audio/audioout.cpp index cb676afc1..54db4c673 100644 --- a/src/core/libraries/audio/audioout.cpp +++ b/src/core/libraries/audio/audioout.cpp @@ -2,9 +2,10 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include -#include #include + #include "audio_core/sdl_audio.h" +#include "common/assert.h" #include "common/logging/log.h" #include "core/libraries/audio/audioout.h" #include "core/libraries/error_codes.h" diff --git a/src/core/libraries/avplayer/avplayer.cpp b/src/core/libraries/avplayer/avplayer.cpp index bd1f6b503..23e1e987a 100644 --- a/src/core/libraries/avplayer/avplayer.cpp +++ b/src/core/libraries/avplayer/avplayer.cpp @@ -40,9 +40,11 @@ int PS4_SYSV_ABI sceAvPlayerChangeStream() { s32 PS4_SYSV_ABI sceAvPlayerClose(SceAvPlayerHandle handle) { LOG_TRACE(Lib_AvPlayer, "called"); if (handle == nullptr) { + LOG_TRACE(Lib_AvPlayer, "returning ORBIS_AVPLAYER_ERROR_INVALID_PARAMS"); return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; } delete handle; + LOG_TRACE(Lib_AvPlayer, "returning ORBIS_OK"); return ORBIS_OK; } @@ -257,6 +259,7 @@ s32 PS4_SYSV_ABI sceAvPlayerStart(SceAvPlayerHandle handle) { s32 PS4_SYSV_ABI sceAvPlayerStop(SceAvPlayerHandle handle) { LOG_TRACE(Lib_AvPlayer, "called"); if (handle == nullptr) { + LOG_TRACE(Lib_AvPlayer, "returning ORBIS_AVPLAYER_ERROR_INVALID_PARAMS"); return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; } const auto res = handle->Stop(); @@ -322,4 +325,4 @@ void RegisterlibSceAvPlayer(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("yN7Jhuv8g24", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0, sceAvPlayerVprintf); }; -} // namespace Libraries::AvPlayer \ No newline at end of file +} // namespace Libraries::AvPlayer diff --git a/src/core/libraries/avplayer/avplayer_data_streamer.h b/src/core/libraries/avplayer/avplayer_data_streamer.h index 04097bb4d..319c88a91 100644 --- a/src/core/libraries/avplayer/avplayer_data_streamer.h +++ b/src/core/libraries/avplayer/avplayer_data_streamer.h @@ -7,6 +7,8 @@ #include "common/types.h" +#include + struct AVIOContext; namespace Libraries::AvPlayer { @@ -14,6 +16,7 @@ namespace Libraries::AvPlayer { class IDataStreamer { public: virtual ~IDataStreamer() = default; + virtual bool Init(std::string_view path) = 0; virtual AVIOContext* GetContext() = 0; }; diff --git a/src/core/libraries/avplayer/avplayer_file_streamer.cpp b/src/core/libraries/avplayer/avplayer_file_streamer.cpp index dc1386a47..c7bd5b5de 100644 --- a/src/core/libraries/avplayer/avplayer_file_streamer.cpp +++ b/src/core/libraries/avplayer/avplayer_file_streamer.cpp @@ -18,19 +18,8 @@ extern "C" { namespace Libraries::AvPlayer { -AvPlayerFileStreamer::AvPlayerFileStreamer(const SceAvPlayerFileReplacement& file_replacement, - std::string_view path) - : m_file_replacement(file_replacement) { - const auto ptr = m_file_replacement.object_ptr; - m_fd = m_file_replacement.open(ptr, path.data()); - ASSERT(m_fd >= 0); - m_file_size = m_file_replacement.size(ptr); - // avio_buffer is deallocated in `avio_context_free` - const auto avio_buffer = reinterpret_cast(av_malloc(AVPLAYER_AVIO_BUFFER_SIZE)); - m_avio_context = - avio_alloc_context(avio_buffer, AVPLAYER_AVIO_BUFFER_SIZE, 0, this, - &AvPlayerFileStreamer::ReadPacket, nullptr, &AvPlayerFileStreamer::Seek); -} +AvPlayerFileStreamer::AvPlayerFileStreamer(const SceAvPlayerFileReplacement& file_replacement) + : m_file_replacement(file_replacement) {} AvPlayerFileStreamer::~AvPlayerFileStreamer() { if (m_avio_context != nullptr) { @@ -43,6 +32,21 @@ AvPlayerFileStreamer::~AvPlayerFileStreamer() { } } +bool AvPlayerFileStreamer::Init(std::string_view path) { + const auto ptr = m_file_replacement.object_ptr; + m_fd = m_file_replacement.open(ptr, path.data()); + if (m_fd < 0) { + return false; + } + m_file_size = m_file_replacement.size(ptr); + // avio_buffer is deallocated in `avio_context_free` + const auto avio_buffer = reinterpret_cast(av_malloc(AVPLAYER_AVIO_BUFFER_SIZE)); + m_avio_context = + avio_alloc_context(avio_buffer, AVPLAYER_AVIO_BUFFER_SIZE, 0, this, + &AvPlayerFileStreamer::ReadPacket, nullptr, &AvPlayerFileStreamer::Seek); + return true; +} + s32 AvPlayerFileStreamer::ReadPacket(void* opaque, u8* buffer, s32 size) { const auto self = reinterpret_cast(opaque); if (self->m_position >= self->m_file_size) { diff --git a/src/core/libraries/avplayer/avplayer_file_streamer.h b/src/core/libraries/avplayer/avplayer_file_streamer.h index 658ce8c1e..034e40dd4 100644 --- a/src/core/libraries/avplayer/avplayer_file_streamer.h +++ b/src/core/libraries/avplayer/avplayer_file_streamer.h @@ -15,9 +15,11 @@ namespace Libraries::AvPlayer { class AvPlayerFileStreamer : public IDataStreamer { public: - AvPlayerFileStreamer(const SceAvPlayerFileReplacement& file_replacement, std::string_view path); + AvPlayerFileStreamer(const SceAvPlayerFileReplacement& file_replacement); ~AvPlayerFileStreamer(); + bool Init(std::string_view path) override; + AVIOContext* GetContext() override { return m_avio_context; } diff --git a/src/core/libraries/avplayer/avplayer_impl.cpp b/src/core/libraries/avplayer/avplayer_impl.cpp index cdfff8277..6de7b4c20 100644 --- a/src/core/libraries/avplayer/avplayer_impl.cpp +++ b/src/core/libraries/avplayer/avplayer_impl.cpp @@ -110,7 +110,7 @@ s32 AvPlayer::AddSource(std::string_view path) { if (path.empty()) { return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS; } - if (AVPLAYER_IS_ERROR(m_state->AddSource(path, GetSourceType(path)))) { + if (!m_state->AddSource(path, GetSourceType(path))) { return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED; } return ORBIS_OK; @@ -128,7 +128,7 @@ s32 AvPlayer::GetStreamCount() { } s32 AvPlayer::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info) { - if (AVPLAYER_IS_ERROR(m_state->GetStreamInfo(stream_index, info))) { + if (!m_state->GetStreamInfo(stream_index, info)) { return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED; } return ORBIS_OK; @@ -145,7 +145,10 @@ s32 AvPlayer::EnableStream(u32 stream_index) { } s32 AvPlayer::Start() { - return m_state->Start(); + if (!m_state->Start()) { + return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED; + } + return ORBIS_OK; } bool AvPlayer::GetVideoData(SceAvPlayerFrameInfo& video_info) { diff --git a/src/core/libraries/avplayer/avplayer_source.cpp b/src/core/libraries/avplayer/avplayer_source.cpp index 776d389f0..99ba2e8b6 100644 --- a/src/core/libraries/avplayer/avplayer_source.cpp +++ b/src/core/libraries/avplayer/avplayer_source.cpp @@ -5,6 +5,7 @@ #include "avplayer_file_streamer.h" +#include "common/alignment.h" #include "common/singleton.h" #include "core/file_sys/fs.h" #include "core/libraries/kernel/time_management.h" @@ -23,31 +24,39 @@ namespace Libraries::AvPlayer { using namespace Kernel; -AvPlayerSource::AvPlayerSource(AvPlayerStateCallback& state, std::string_view path, - const SceAvPlayerInitData& init_data, - SceAvPlayerSourceType source_type) - : m_state(state), m_memory_replacement(init_data.memory_replacement), - m_num_output_video_framebuffers( - std::min(std::max(2, init_data.num_output_video_framebuffers), 16)) { - AVFormatContext* context = avformat_alloc_context(); - if (init_data.file_replacement.open != nullptr) { - m_up_data_streamer = - std::make_unique(init_data.file_replacement, path); - context->pb = m_up_data_streamer->GetContext(); - ASSERT(!AVPLAYER_IS_ERROR(avformat_open_input(&context, nullptr, nullptr, nullptr))); - } else { - const auto mnt = Common::Singleton::Instance(); - const auto filepath = mnt->GetHostPath(path); - ASSERT(!AVPLAYER_IS_ERROR( - avformat_open_input(&context, filepath.string().c_str(), nullptr, nullptr))); - } - m_avformat_context = AVFormatContextPtr(context, &ReleaseAVFormatContext); -} +AvPlayerSource::AvPlayerSource(AvPlayerStateCallback& state) : m_state(state) {} AvPlayerSource::~AvPlayerSource() { Stop(); } +bool AvPlayerSource::Init(const SceAvPlayerInitData& init_data, std::string_view path) { + m_memory_replacement = init_data.memory_replacement, + m_num_output_video_framebuffers = + std::min(std::max(2, init_data.num_output_video_framebuffers), 16); + + AVFormatContext* context = avformat_alloc_context(); + if (init_data.file_replacement.open != nullptr) { + m_up_data_streamer = std::make_unique(init_data.file_replacement); + if (!m_up_data_streamer->Init(path)) { + return false; + } + context->pb = m_up_data_streamer->GetContext(); + if (AVPLAYER_IS_ERROR(avformat_open_input(&context, nullptr, nullptr, nullptr))) { + return false; + } + } else { + const auto mnt = Common::Singleton::Instance(); + const auto filepath = mnt->GetHostPath(path); + if (AVPLAYER_IS_ERROR( + avformat_open_input(&context, filepath.string().c_str(), nullptr, nullptr))) { + return false; + } + } + m_avformat_context = AVFormatContextPtr(context, &ReleaseAVFormatContext); + return true; +} + bool AvPlayerSource::FindStreamInfo() { if (m_avformat_context == nullptr) { LOG_ERROR(Lib_AvPlayer, "Could not find stream info. NULL context."); @@ -82,20 +91,20 @@ static s32 CodecTypeToStreamType(AVMediaType codec_type) { } } -static f32 AVRationalToF32(const AVRational& rational) { +static f32 AVRationalToF32(const AVRational rational) { return f32(rational.num) / rational.den; } -s32 AvPlayerSource::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info) { +bool AvPlayerSource::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info) { info = {}; if (m_avformat_context == nullptr || stream_index >= m_avformat_context->nb_streams) { LOG_ERROR(Lib_AvPlayer, "Could not get stream {} info.", stream_index); - return -1; + return false; } const auto p_stream = m_avformat_context->streams[stream_index]; if (p_stream == nullptr || p_stream->codecpar == nullptr) { LOG_ERROR(Lib_AvPlayer, "Could not get stream {} info. NULL stream.", stream_index); - return -1; + return false; } info.type = CodecTypeToStreamType(p_stream->codecpar->codec_type); info.start_time = p_stream->start_time; @@ -111,8 +120,8 @@ s32 AvPlayerSource::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info) LOG_INFO(Lib_AvPlayer, "Stream {} is a video stream.", stream_index); info.details.video.aspect_ratio = f32(p_stream->codecpar->width) / p_stream->codecpar->height; - info.details.video.width = p_stream->codecpar->width; - info.details.video.height = p_stream->codecpar->height; + info.details.video.width = Common::AlignUp(u32(p_stream->codecpar->width), 16); + info.details.video.height = Common::AlignUp(u32(p_stream->codecpar->height), 16); if (p_lang_node != nullptr) { std::memcpy(info.details.video.language_code, p_lang_node->value, std::min(strlen(p_lang_node->value), size_t(3))); @@ -139,9 +148,9 @@ s32 AvPlayerSource::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info) break; default: LOG_ERROR(Lib_AvPlayer, "Stream {} type is unknown: {}.", stream_index, info.type); - return -1; + return false; } - return 0; + return true; } bool AvPlayerSource::EnableStream(u32 stream_index) { @@ -167,8 +176,9 @@ bool AvPlayerSource::EnableStream(u32 stream_index) { LOG_ERROR(Lib_AvPlayer, "Could not open avcodec for video stream {}.", stream_index); return false; } - const auto width = m_video_codec_context->width; - const auto size = (width * m_video_codec_context->height * 3) / 2; + const auto width = Common::AlignUp(u32(m_video_codec_context->width), 16); + const auto height = Common::AlignUp(u32(m_video_codec_context->height), 16); + const auto size = (width * height * 3) / 2; for (u64 index = 0; index < m_num_output_video_framebuffers; ++index) { m_video_buffers.Push(FrameBuffer(m_memory_replacement, 0x100, size)); } @@ -213,12 +223,12 @@ std::optional AvPlayerSource::HasFrames(u32 num_frames) { return m_video_packets.Size() > num_frames || m_is_eof; } -s32 AvPlayerSource::Start() { +bool AvPlayerSource::Start() { std::unique_lock lock(m_state_mutex); if (m_audio_codec_context == nullptr && m_video_codec_context == nullptr) { LOG_ERROR(Lib_AvPlayer, "Could not start playback. NULL context."); - return -1; + return false; } m_demuxer_thread = std::jthread([this](std::stop_token stop) { this->DemuxerThread(stop); }); m_video_decoder_thread = @@ -226,7 +236,7 @@ s32 AvPlayerSource::Start() { m_audio_decoder_thread = std::jthread([this](std::stop_token stop) { this->AudioDecoderThread(stop); }); m_start_time = std::chrono::high_resolution_clock::now(); - return 0; + return true; } bool AvPlayerSource::Stop() { @@ -284,11 +294,6 @@ bool AvPlayerSource::GetVideoData(SceAvPlayerFrameInfo& video_info) { return true; } -static void CopyNV12Data(u8* dst, const AVFrame& src) { - std::memcpy(dst, src.data[0], src.width * src.height); - std::memcpy(dst + src.width * src.height, src.data[1], (src.width * src.height) / 2); -} - bool AvPlayerSource::GetVideoData(SceAvPlayerFrameInfoEx& video_info) { if (!IsActive()) { return false; @@ -359,12 +364,16 @@ bool AvPlayerSource::GetAudioData(SceAvPlayerFrameInfo& audio_info) { audio_info = {}; audio_info.timestamp = frame->info.timestamp; audio_info.pData = reinterpret_cast(frame->info.pData); + audio_info.details.audio.sample_rate = frame->info.details.audio.sample_rate; audio_info.details.audio.size = frame->info.details.audio.size; audio_info.details.audio.channel_count = frame->info.details.audio.channel_count; return true; } u64 AvPlayerSource::CurrentTime() { + if (!IsActive()) { + return 0; + } using namespace std::chrono; return duration_cast(high_resolution_clock::now() - m_start_time).count(); } @@ -489,13 +498,17 @@ AvPlayerSource::AVFramePtr AvPlayerSource::ConvertVideoFrame(const AVFrame& fram nv12_frame->width = frame.width; nv12_frame->height = frame.height; nv12_frame->sample_aspect_ratio = frame.sample_aspect_ratio; + nv12_frame->crop_top = frame.crop_top; + nv12_frame->crop_bottom = frame.crop_bottom; + nv12_frame->crop_left = frame.crop_left; + nv12_frame->crop_right = frame.crop_right; av_frame_get_buffer(nv12_frame.get(), 0); if (m_sws_context == nullptr) { m_sws_context = SWSContextPtr(sws_getContext(frame.width, frame.height, AVPixelFormat(frame.format), - frame.width, frame.height, AV_PIX_FMT_NV12, + nv12_frame->width, nv12_frame->height, AV_PIX_FMT_NV12, SWS_FAST_BILINEAR, nullptr, nullptr, nullptr), &ReleaseSWSContext); } @@ -508,6 +521,26 @@ AvPlayerSource::AVFramePtr AvPlayerSource::ConvertVideoFrame(const AVFrame& fram return nv12_frame; } +static void CopyNV12Data(u8* dst, const AVFrame& src) { + const auto width = Common::AlignUp(u32(src.width), 16); + const auto height = Common::AlignUp(u32(src.height), 16); + + if (src.width == width) { + std::memcpy(dst, src.data[0], src.width * src.height); + std::memcpy(dst + src.width * height, src.data[1], (src.width * src.height) / 2); + } else { + const auto luma_dst = dst; + for (u32 y = 0; y < src.height; ++y) { + std::memcpy(luma_dst + y * width, src.data[0] + y * src.width, src.width); + } + const auto chroma_dst = dst + width * height; + for (u32 y = 0; y < src.height / 2; ++y) { + std::memcpy(chroma_dst + y * (width / 2), src.data[0] + y * (src.width / 2), + src.width / 2); + } + } +} + Frame AvPlayerSource::PrepareVideoFrame(FrameBuffer buffer, const AVFrame& frame) { ASSERT(frame.format == AV_PIX_FMT_NV12); @@ -521,6 +554,9 @@ Frame AvPlayerSource::PrepareVideoFrame(FrameBuffer buffer, const AVFrame& frame const auto num = time_base.num; const auto timestamp = (num != 0 && den > 1) ? (pkt_dts * num) / den : pkt_dts; + const auto width = Common::AlignUp(u32(frame.width), 16); + const auto height = Common::AlignUp(u32(frame.height), 16); + return Frame{ .buffer = std::move(buffer), .info = @@ -531,9 +567,14 @@ Frame AvPlayerSource::PrepareVideoFrame(FrameBuffer buffer, const AVFrame& frame { .video = { - .width = u32(frame.width), - .height = u32(frame.height), + .width = u32(width), + .height = u32(height), .aspect_ratio = AVRationalToF32(frame.sample_aspect_ratio), + .crop_left_offset = u32(frame.crop_left), + .crop_right_offset = u32(frame.crop_right + (width - frame.width)), + .crop_top_offset = u32(frame.crop_top), + .crop_bottom_offset = + u32(frame.crop_bottom + (height - frame.height)), .pitch = u32(frame.linesize[0]), .luma_bit_depth = 8, .chroma_bit_depth = 8, @@ -655,6 +696,7 @@ Frame AvPlayerSource::PrepareAudioFrame(FrameBuffer buffer, const AVFrame& frame .audio = { .channel_count = u16(frame.ch_layout.nb_channels), + .sample_rate = u32(frame.sample_rate), .size = u32(size), }, }, diff --git a/src/core/libraries/avplayer/avplayer_source.h b/src/core/libraries/avplayer/avplayer_source.h index 7144e7ee4..906122142 100644 --- a/src/core/libraries/avplayer/avplayer_source.h +++ b/src/core/libraries/avplayer/avplayer_source.h @@ -120,17 +120,17 @@ private: class AvPlayerSource { public: - AvPlayerSource(AvPlayerStateCallback& state, std::string_view path, - const SceAvPlayerInitData& init_data, SceAvPlayerSourceType source_type); + AvPlayerSource(AvPlayerStateCallback& state); ~AvPlayerSource(); + bool Init(const SceAvPlayerInitData& init_data, std::string_view path); bool FindStreamInfo(); s32 GetStreamCount(); - s32 GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info); + bool GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info); bool EnableStream(u32 stream_index); void SetLooping(bool is_looping); std::optional HasFrames(u32 num_frames); - s32 Start(); + bool Start(); bool Stop(); bool GetAudioData(SceAvPlayerFrameInfo& audio_info); bool GetVideoData(SceAvPlayerFrameInfo& video_info); diff --git a/src/core/libraries/avplayer/avplayer_state.cpp b/src/core/libraries/avplayer/avplayer_state.cpp index 884cd9408..a512063f2 100644 --- a/src/core/libraries/avplayer/avplayer_state.cpp +++ b/src/core/libraries/avplayer/avplayer_state.cpp @@ -24,6 +24,7 @@ void PS4_SYSV_ABI AvPlayerState::AutoPlayEventCallback(void* opaque, s32 event_i s32 timedtext_stream_index = -1; const s32 stream_count = self->GetStreamCount(); if (AVPLAYER_IS_ERROR(stream_count)) { + self->Stop(); return; } if (stream_count == 0) { @@ -32,7 +33,10 @@ void PS4_SYSV_ABI AvPlayerState::AutoPlayEventCallback(void* opaque, s32 event_i } for (u32 stream_index = 0; stream_index < stream_count; ++stream_index) { SceAvPlayerStreamInfo info{}; - self->GetStreamInfo(stream_index, info); + if (!self->GetStreamInfo(stream_index, info)) { + self->Stop(); + return; + } const std::string_view default_language( reinterpret_cast(self->m_default_language)); @@ -116,23 +120,28 @@ AvPlayerState::~AvPlayerState() { } // Called inside GAME thread -s32 AvPlayerState::AddSource(std::string_view path, SceAvPlayerSourceType source_type) { +bool AvPlayerState::AddSource(std::string_view path, SceAvPlayerSourceType source_type) { if (path.empty()) { LOG_ERROR(Lib_AvPlayer, "File path is empty."); - return -1; + return false; } { std::unique_lock lock(m_source_mutex); if (m_up_source != nullptr) { LOG_ERROR(Lib_AvPlayer, "Only one source is supported."); - return -1; + return false; } - m_up_source = std::make_unique(*this, path, m_init_data, source_type); + m_up_source = std::make_unique(*this); + if (!m_up_source->Init(m_init_data, path)) { + SetState(AvState::Error); + m_up_source.reset(); + return false; + } } AddSourceEvent(); - return 0; + return true; } // Called inside GAME thread @@ -146,25 +155,25 @@ s32 AvPlayerState::GetStreamCount() { } // Called inside GAME thread -s32 AvPlayerState::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info) { +bool AvPlayerState::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info) { std::shared_lock lock(m_source_mutex); if (m_up_source == nullptr) { LOG_ERROR(Lib_AvPlayer, "Could not get stream {} info. No source.", stream_index); - return -1; + return false; } return m_up_source->GetStreamInfo(stream_index, info); } // Called inside GAME thread -s32 AvPlayerState::Start() { +bool AvPlayerState::Start() { std::shared_lock lock(m_source_mutex); - if (m_up_source == nullptr || m_up_source->Start() < 0) { + if (m_up_source == nullptr || !m_up_source->Start()) { LOG_ERROR(Lib_AvPlayer, "Could not start playback."); - return -1; + return false; } SetState(AvState::Play); OnPlaybackStateChanged(AvState::Play); - return 0; + return true; } void AvPlayerState::AvControllerThread(std::stop_token stop) { @@ -219,11 +228,14 @@ bool AvPlayerState::Stop() { if (m_up_source == nullptr || m_current_state == AvState::Stop) { return false; } + if (!m_up_source->Stop()) { + return false; + } if (!SetState(AvState::Stop)) { return false; } OnPlaybackStateChanged(AvState::Stop); - return m_up_source->Stop(); + return true; } bool AvPlayerState::GetVideoData(SceAvPlayerFrameInfo& video_info) { diff --git a/src/core/libraries/avplayer/avplayer_state.h b/src/core/libraries/avplayer/avplayer_state.h index ff80b6cea..a5a3bd689 100644 --- a/src/core/libraries/avplayer/avplayer_state.h +++ b/src/core/libraries/avplayer/avplayer_state.h @@ -24,11 +24,11 @@ public: AvPlayerState(const SceAvPlayerInitData& init_data); ~AvPlayerState(); - s32 AddSource(std::string_view filename, SceAvPlayerSourceType source_type); + bool AddSource(std::string_view filename, SceAvPlayerSourceType source_type); s32 GetStreamCount(); - s32 GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info); + bool GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info); bool EnableStream(u32 stream_index); - s32 Start(); + bool Start(); bool Stop(); bool GetAudioData(SceAvPlayerFrameInfo& audio_info); bool GetVideoData(SceAvPlayerFrameInfo& video_info); diff --git a/src/core/libraries/dialogs/error_dialog.cpp b/src/core/libraries/dialogs/error_dialog.cpp index 02f82c096..7df9b1c83 100644 --- a/src/core/libraries/dialogs/error_dialog.cpp +++ b/src/core/libraries/dialogs/error_dialog.cpp @@ -1,7 +1,6 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -// Generated By moduleGenerator #include "common/logging/log.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" diff --git a/src/core/libraries/dialogs/ime_dialog.cpp b/src/core/libraries/dialogs/ime_dialog.cpp index e73c1881b..ddb1a89f8 100644 --- a/src/core/libraries/dialogs/ime_dialog.cpp +++ b/src/core/libraries/dialogs/ime_dialog.cpp @@ -1,7 +1,6 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -// Generated By moduleGenerator #include "common/logging/log.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" diff --git a/src/core/libraries/disc_map/disc_map.cpp b/src/core/libraries/disc_map/disc_map.cpp index 79f4acb34..bb566a149 100644 --- a/src/core/libraries/disc_map/disc_map.cpp +++ b/src/core/libraries/disc_map/disc_map.cpp @@ -1,7 +1,6 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -// Generated By moduleGenerator #include "common/logging/log.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" diff --git a/src/core/libraries/error_codes.h b/src/core/libraries/error_codes.h index 123edcee8..094ea6603 100644 --- a/src/core/libraries/error_codes.h +++ b/src/core/libraries/error_codes.h @@ -233,9 +233,6 @@ constexpr int SCE_KERNEL_ERROR_ESDKVERSION = 0x80020063; constexpr int SCE_KERNEL_ERROR_ESTART = 0x80020064; constexpr int SCE_KERNEL_ERROR_ESTOP = 0x80020065; -// libSceRandom error codes -constexpr int SCE_RANDOM_ERROR_INVALID = 0x817C0016; - // videoOut constexpr int SCE_VIDEO_OUT_ERROR_INVALID_VALUE = 0x80290001; // invalid argument constexpr int SCE_VIDEO_OUT_ERROR_INVALID_ADDRESS = 0x80290002; // invalid addresses @@ -249,14 +246,6 @@ constexpr int SCE_VIDEO_OUT_ERROR_SLOT_OCCUPIED = 0x80290010; // slot alr constexpr int SCE_VIDEO_OUT_ERROR_FLIP_QUEUE_FULL = 0x80290012; // flip queue is full constexpr int SCE_VIDEO_OUT_ERROR_INVALID_OPTION = 0x8029001A; // Invalid buffer attribute option -// GnmDriver -constexpr int ORBIS_GNM_ERROR_COMPUTEQUEUE_INVALID_PIPE_ID = 0x80D17000; -constexpr int ORBIS_GNM_ERROR_COMPUTEQUEUE_INVALID_QUEUE_ID = 0x80D17001; -constexpr int ORBIS_GNM_ERROR_COMPUTEQUEUE_INVALID_RING_BASE_ADDR = 0x80D17003; -constexpr int ORBIS_GNM_ERROR_COMPUTEQUEUE_INVALID_RING_SIZE = 0x80D17002; -constexpr int ORBIS_GNM_ERROR_COMPUTEQUEUE_INVALID_READ_PTR_ADDR = 0x80D17004; -constexpr int ORBIS_GNM_ERROR_FAILURE = 0x8EEE00FF; - // Generic constexpr int ORBIS_OK = 0x00000000; constexpr int ORBIS_FAIL = 0xFFFFFFFF; @@ -471,4 +460,6 @@ constexpr int ORBIS_AVPLAYER_ERROR_INFO_AES_ENCRY = 0x806A00B5; constexpr int ORBIS_AVPLAYER_ERROR_INFO_OTHER_ENCRY = 0x806A00BF; // AppContent library -constexpr int ORBIS_APP_CONTENT_ERROR_PARAMETER = 0x80D90002; \ No newline at end of file +constexpr int ORBIS_APP_CONTENT_ERROR_PARAMETER = 0x80D90002; +constexpr int ORBIS_APP_CONTENT_ERROR_DRM_NO_ENTITLEMENT = 0x80D90007; +constexpr int ORBIS_APP_CONTENT_ERROR_NOT_FOUND = 0x80D90005; \ No newline at end of file diff --git a/src/core/libraries/gnmdriver/gnm_error.h b/src/core/libraries/gnmdriver/gnm_error.h new file mode 100644 index 000000000..eab684a24 --- /dev/null +++ b/src/core/libraries/gnmdriver/gnm_error.h @@ -0,0 +1,85 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +constexpr int ORBIS_GNM_ERROR_SUBMISSION_FAILED_INVALID_ARGUMENT = 0x80D11000; +constexpr int ORBIS_GNM_ERROR_SUBMISSION_NOT_ENOUGH_RESOURCES = 0x80D11001; +constexpr int ORBIS_GNM_ERROR_SUBMISSION_AND_FLIP_FAILED_INVALID_COMMAND_BUFFER = 0x80D11080; +constexpr int ORBIS_GNM_ERROR_SUBMISSION_AND_FLIP_FAILED_INVALID_QUEUE_FULL = 0x80D11081; +constexpr int ORBIS_GNM_ERROR_SUBMISSION_AND_FLIP_FAILED_REQUEST_FAILED = 0x80D11082; +constexpr int ORBIS_GNM_ERROR_SUBMISSION_FAILED_INTERNAL_ERROR = 0x80D110FF; +constexpr int ORBIS_GNM_ERROR_VALIDATION_WARNING = 0x80D12000; +constexpr int ORBIS_GNM_ERROR_VALIDATION_WARNING_RWBUFFER_ROTYPE = 0x80D12001; +constexpr int ORBIS_GNM_ERROR_VALIDATION_WARNING_BLENDING = 0x80D12002; +constexpr int ORBIS_GNM_ERROR_VALIDATION_WARNING_DRAW_CU_MASK = 0x80D12003; +constexpr int ORBIS_GNM_ERROR_VALIDATION_WARNING_MRT_SETUP = 0x80D12004; +constexpr int ORBIS_GNM_ERROR_VALIDATION_WARNING_DEPTH_RT_SETUP = 0x80D12005; +constexpr int ORBIS_GNM_ERROR_VALIDATION_WARNING_PS_AND_MRT_FORMAT = 0x80D1200F; +constexpr int ORBIS_GNM_ERROR_VALIDATION_ERROR = 0x80D13000; +constexpr int ORBIS_GNM_ERROR_VALIDATION_VSHARP = 0x80D13001; +constexpr int ORBIS_GNM_ERROR_VALIDATION_TSHARP = 0x80D13002; +constexpr int ORBIS_GNM_ERROR_VALIDATION_RESOURCE = 0x80D13003; +constexpr int ORBIS_GNM_ERROR_VALIDATION_TABLE_MEMORY = 0x80D13004; +constexpr int ORBIS_GNM_ERROR_VALIDATION_WRITE_EVENT_OP = 0x80D13005; +constexpr int ORBIS_GNM_ERROR_VALIDATION_INDEX_BUFFER = 0x80D13006; +constexpr int ORBIS_GNM_ERROR_VALIDATION_TESS_FACTOR_BUFFER = 0x80D13007; +constexpr int ORBIS_GNM_ERROR_VALIDATION_SCRATCH_RING = 0x80D13008; +constexpr int ORBIS_GNM_ERROR_VALIDATION_PRIMITIVE_TYPE = 0x80D13009; +constexpr int ORBIS_GNM_ERROR_VALIDATION_INDEX_SIZE = 0x80D1300A; +constexpr int ORBIS_GNM_ERROR_VALIDATION_INLINE_DRAW_SIZE = 0x80D1300B; +constexpr int ORBIS_GNM_ERROR_VALIDATION_NUM_INPUT_PATCHES = 0x80D1300C; +constexpr int ORBIS_GNM_ERROR_VALIDATION_GS_MODE = 0x80D1300D; +constexpr int ORBIS_GNM_ERROR_VALIDATION_SHADER_ADDRESS = 0x80D1300E; +constexpr int ORBIS_GNM_ERROR_VALIDATION_BORDER_COLOR_TABLE = 0x80D1300F; +constexpr int ORBIS_GNM_ERROR_VALIDATION_SSHARP = 0x80D13010; +constexpr int ORBIS_GNM_ERROR_VALIDATION_DISPATCH_DRAW = 0x80D13011; +constexpr int ORBIS_GNM_ERROR_VALIDATION_ACTIVE_SHADER_STAGE = 0x80D13012; +constexpr int ORBIS_GNM_ERROR_VALIDATION_DCB = 0x80D13013; +constexpr int ORBIS_GNM_ERROR_VALIDATION_MISMATCH_SHADER_STAGE = 0x80D13014; +constexpr int ORBIS_GNM_ERROR_VALIDATION_MRT_SETUP = 0x80D13015; +constexpr int ORBIS_GNM_ERROR_VALIDATION_BAD_OP_CODE = 0x80D13016; +constexpr int ORBIS_GNM_ERROR_VALIDATION_DEPTH_RT_SETUP = 0x80D13017; +constexpr int ORBIS_GNM_ERROR_VALIDATION_NUM_INSTANCES = 0x80D13018; +constexpr int ORBIS_GNM_ERROR_VALIDATION_SRT = 0x80D13019; +constexpr int ORBIS_GNM_ERROR_VALIDATION_INVALID_ARGUMENT = 0x80D13FFD; +constexpr int ORBIS_GNM_ERROR_VALIDATION_FAILED_INTERNAL_ERROR = 0x80D13FFE; +constexpr int ORBIS_GNM_ERROR_VALIDATION_NOT_ENABLED = 0x80D13FFF; +constexpr int ORBIS_GNM_ERROR_CAPTURE_FILE_IO = 0x80D15000; +constexpr int ORBIS_GNM_ERROR_CAPTURE_RAZOR_NOT_LOADED = 0x80D15001; +constexpr int ORBIS_GNM_ERROR_CAPTURE_NOTHING_TO_CAPTURE = 0x80D15002; +constexpr int ORBIS_GNM_ERROR_CAPTURE_FAILED_INTERNAL = 0x80D1500F; +constexpr int ORBIS_GNM_ERROR_COMPUTEQUEUE_INVALID_PIPE_ID = 0x80D17000; +constexpr int ORBIS_GNM_ERROR_COMPUTEQUEUE_INVALID_QUEUE_ID = 0x80D17001; +constexpr int ORBIS_GNM_ERROR_COMPUTEQUEUE_INVALID_RING_SIZE = 0x80D17002; +constexpr int ORBIS_GNM_ERROR_COMPUTEQUEUE_INVALID_RING_BASE_ADDR = 0x80D17003; +constexpr int ORBIS_GNM_ERROR_COMPUTEQUEUE_INVALID_READ_PTR_ADDR = 0x80D17004; +constexpr int ORBIS_GNM_ERROR_COMPUTEQUEUE_INVALID_PIPE_PRIORITY = 0x80D17005; +constexpr int ORBIS_GNM_ERROR_COMPUTEQUEUE_INTERNAL = 0x80D170FF; +constexpr int ORBIS_GNM_ERROR_RESOURCE_REGISTRATION_INVALID_ARGUMENT = 0x80D19000; +constexpr int ORBIS_GNM_ERROR_RESOURCE_REGISTRATION_INVALID_SHADER = 0x80D19001; +constexpr int ORBIS_GNM_ERROR_RESOURCE_REGISTRATION_STALE_HANDLE = 0x80D19002; +constexpr int ORBIS_GNM_ERROR_RESOURCE_REGISTRATION_FULL = 0x80D19003; +constexpr int ORBIS_GNM_ERROR_RESOURCE_REGISTRATION_NOT_PERMITTED = 0x80D19004; +constexpr int ORBIS_GNM_ERROR_RESOURCE_REGISTRATION_OUTPUT_ARGUMENT_IS_NULL = 0x80D19005; +constexpr int ORBIS_GNM_ERROR_RESOURCE_REGISTRATION_OWNER_HANDLE_INVALID = 0x80D19006; +constexpr int ORBIS_GNM_ERROR_RESOURCE_REGISTRATION_RESOURCE_HANDLE_INVALID = 0x80D19007; +constexpr int ORBIS_GNM_ERROR_RESOURCE_REGISTRATION_RESOURCE_TYPE_INVALID = 0x80D19008; +constexpr int ORBIS_GNM_ERROR_RESOURCE_REGISTRATION_GDS_RESOURCE_TYPE_INVALID = 0x80D19009; +constexpr int ORBIS_GNM_ERROR_RESOURCE_REGISTRATION_RESOURCE_SIZE_INVALID = 0x80D1900A; +constexpr int ORBIS_GNM_ERROR_RESOURCE_REGISTRATION_RESOURCE_ADDRESS_IS_NULL = 0x80D1900B; +constexpr int ORBIS_GNM_ERROR_RESOURCE_REGISTRATION_GDS_OFFSET_INVALID = 0x80D1900C; +constexpr int ORBIS_GNM_ERROR_RESOURCE_REGISTRATION_RESOURCE_NAME_IS_NULL = 0x80D1900D; +constexpr int ORBIS_GNM_ERROR_RESOURCE_REGISTRATION_OWNER_NAME_IS_NULL = 0x80D1900E; +constexpr int ORBIS_GNM_ERROR_RESOURCE_REGISTRATION_FIND_CALLBACK_IS_NULL = 0x80D1900F; +constexpr int ORBIS_GNM_ERROR_RESOURCE_REGISTRATION_RESOURCE_IS_NOT_SHADER = 0x80D19010; +constexpr int ORBIS_GNM_ERROR_RESOURCE_REGISTRATION_USER_MEMORY_PARAM_IS_NULL = 0x80D19011; +constexpr int ORBIS_GNM_ERROR_RESOURCE_REGISTRATION_USER_MEMORY_PARAM_NOT_ALIGNED = 0x80D19012; +constexpr int ORBIS_GNM_ERROR_RESOURCE_REGISTRATION_INVALID_NAME_LENGTH_PARAM = 0x80D19013; +constexpr int ORBIS_GNM_ERROR_RESOURCE_REGISTRATION_INVALID_SIZE_PARAM = 0x80D19014; +constexpr int ORBIS_GNM_ERROR_RESOURCE_REGISTRATION_INVALID_NUM_RESOURCES_PARAM = 0x80D19015; +constexpr int ORBIS_GNM_ERROR_RESOURCE_REGISTRATION_INTERNAL = 0x80D19FFF; +constexpr int ORBIS_GNM_ERROR_GET_GPU_INFO_PARAMETER_NULL = 0x80D1B000; +constexpr int ORBIS_GNM_ERROR_GET_GPU_INFO_FAILED = 0x80D1B001; +constexpr int ORBIS_GNM_ERROR_GET_GPU_INFO_PARAMETER_INVALID = 0x80D1B002; +constexpr int ORBIS_GNM_ERROR_FAILURE = 0x8EEE00FF; diff --git a/src/core/libraries/gnmdriver/gnmdriver.cpp b/src/core/libraries/gnmdriver/gnmdriver.cpp index c2ee6d592..a2ef94037 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.cpp +++ b/src/core/libraries/gnmdriver/gnmdriver.cpp @@ -1,6 +1,9 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "gnm_error.h" +#include "gnmdriver.h" + #include "common/assert.h" #include "common/config.h" #include "common/debug.h" @@ -9,7 +12,6 @@ #include "common/slot_vector.h" #include "core/address_space.h" #include "core/libraries/error_codes.h" -#include "core/libraries/gnmdriver/gnmdriver.h" #include "core/libraries/kernel/libkernel.h" #include "core/libraries/libs.h" #include "core/libraries/videoout/video_out.h" @@ -55,6 +57,10 @@ static constexpr auto HwInitPacketSize = 0x100u; // clang-format off static constexpr std::array InitSequence{ + // A fake preamble to mimic context reset sent by FW + 0xc0001200u, 0u, // IT_CLEAR_STATE + + // Actual init state sequence 0xc0017600u, 0x216u, 0xffffffffu, 0xc0017600u, 0x217u, 0xffffffffu, 0xc0017600u, 0x215u, 0u, @@ -94,9 +100,13 @@ static constexpr std::array InitSequence{ 0xc0036900u, 0x295u, 0x100u, 0x100u, 4u, 0xc0017900u, 0x200u, 0xe0000000u, }; -static_assert(InitSequence.size() == 0x73); +static_assert(InitSequence.size() == 0x73 + 2); static constexpr std::array InitSequence175{ + // A fake preamble to mimic context reset sent by FW + 0xc0001200u, 0u, // IT_CLEAR_STATE + + // Actual init state sequence 0xc0017600u, 0x216u, 0xffffffffu, 0xc0017600u, 0x217u, 0xffffffffu, 0xc0017600u, 0x215u, 0u, @@ -136,9 +146,13 @@ static constexpr std::array InitSequence175{ 0xc0036900u, 0x295u, 0x100u, 0x100u, 4u, 0xc0017900u, 0x200u, 0xe0000000u, }; -static_assert(InitSequence175.size() == 0x73); +static_assert(InitSequence175.size() == 0x73 + 2); static constexpr std::array InitSequence200{ + // A fake preamble to mimic context reset sent by FW + 0xc0001200u, 0u, // IT_CLEAR_STATE + + // Actual init state sequence 0xc0017600u, 0x216u, 0xffffffffu, 0xc0017600u, 0x217u, 0xffffffffu, 0xc0017600u, 0x215u, 0u, @@ -179,9 +193,13 @@ static constexpr std::array InitSequence200{ 0xc0036900u, 0x295u, 0x100u, 0x100u, 4u, 0xc0017900u, 0x200u, 0xe0000000u, }; -static_assert(InitSequence200.size() == 0x76); +static_assert(InitSequence200.size() == 0x76 + 2); static constexpr std::array InitSequence350{ + // A fake preamble to mimic context reset sent by FW + 0xc0001200u, 0u, // IT_CLEAR_STATE + + // Actual init state sequence 0xc0017600u, 0x216u, 0xffffffffu, 0xc0017600u, 0x217u, 0xffffffffu, 0xc0017600u, 0x215u, 0u, @@ -224,7 +242,7 @@ static constexpr std::array InitSequence350{ 0xc0017900u, 0x200u, 0xe0000000u, 0xc0016900u, 0x2aau, 0xffu, }; -static_assert(InitSequence350.size() == 0x7c); +static_assert(InitSequence350.size() == 0x7c + 2); static constexpr std::array CtxInitSequence{ 0xc0012800u, 0x80000000u, 0x80000000u, @@ -632,12 +650,12 @@ s32 PS4_SYSV_ABI sceGnmDrawIndexAuto(u32* cmdbuf, u32 size, u32 index_count, u32 } s32 PS4_SYSV_ABI sceGnmDrawIndexIndirect(u32* cmdbuf, u32 size, u32 data_offset, u32 shader_stage, - u32 vertex_sgpr_offset, u32 instance_vgpr_offset, + u32 vertex_sgpr_offset, u32 instance_sgpr_offset, u32 flags) { LOG_TRACE(Lib_GnmDriver, "called"); if (cmdbuf && (size == 9) && (shader_stage < ShaderStages::Max) && - (vertex_sgpr_offset < 0x10u) && (instance_vgpr_offset < 0x10u)) { + (vertex_sgpr_offset < 0x10u) && (instance_sgpr_offset < 0x10u)) { const auto predicate = flags & 1 ? PM4Predicate::PredEnable : PM4Predicate::PredDisable; cmdbuf = WriteHeader( @@ -647,7 +665,7 @@ s32 PS4_SYSV_ABI sceGnmDrawIndexIndirect(u32* cmdbuf, u32 size, u32 data_offset, cmdbuf[0] = data_offset; cmdbuf[1] = vertex_sgpr_offset == 0 ? 0 : (vertex_sgpr_offset & 0xffffu) + sgpr_offset; - cmdbuf[2] = instance_vgpr_offset == 0 ? 0 : (instance_vgpr_offset & 0xffffu) + sgpr_offset; + cmdbuf[2] = instance_sgpr_offset == 0 ? 0 : (instance_sgpr_offset & 0xffffu) + sgpr_offset; cmdbuf[3] = 0; cmdbuf += 4; @@ -689,11 +707,11 @@ s32 PS4_SYSV_ABI sceGnmDrawIndexOffset(u32* cmdbuf, u32 size, u32 index_offset, } s32 PS4_SYSV_ABI sceGnmDrawIndirect(u32* cmdbuf, u32 size, u32 data_offset, u32 shader_stage, - u32 vertex_sgpr_offset, u32 instance_vgpr_offset, u32 flags) { + u32 vertex_sgpr_offset, u32 instance_sgpr_offset, u32 flags) { LOG_TRACE(Lib_GnmDriver, "called"); if (cmdbuf && (size == 9) && (shader_stage < ShaderStages::Max) && - (vertex_sgpr_offset < 0x10u) && (instance_vgpr_offset < 0x10u)) { + (vertex_sgpr_offset < 0x10u) && (instance_sgpr_offset < 0x10u)) { const auto predicate = flags & 1 ? PM4Predicate::PredEnable : PM4Predicate::PredDisable; cmdbuf = WriteHeader(cmdbuf, 4, PM4ShaderType::ShaderGraphics, @@ -703,7 +721,7 @@ s32 PS4_SYSV_ABI sceGnmDrawIndirect(u32* cmdbuf, u32 size, u32 data_offset, u32 cmdbuf[0] = data_offset; cmdbuf[1] = vertex_sgpr_offset == 0 ? 0 : (vertex_sgpr_offset & 0xffffu) + sgpr_offset; - cmdbuf[2] = instance_vgpr_offset == 0 ? 0 : (instance_vgpr_offset & 0xffffu) + sgpr_offset; + cmdbuf[2] = instance_sgpr_offset == 0 ? 0 : (instance_sgpr_offset & 0xffffu) + sgpr_offset; cmdbuf[3] = 2; // auto index cmdbuf += 4; @@ -735,11 +753,11 @@ u32 PS4_SYSV_ABI sceGnmDrawInitDefaultHardwareState(u32* cmdbuf, u32 size) { cmdbuf = ClearContextState(cmdbuf); } - std::memcpy(cmdbuf, InitSequence.data(), InitSequence.size() * 4); - cmdbuf += InitSequence.size(); + std::memcpy(cmdbuf, &InitSequence[2], (InitSequence.size() - 2) * 4); + cmdbuf += InitSequence.size() - 2; const auto cmdbuf_left = - HwInitPacketSize - InitSequence.size() - (clear_state ? 0xc : 0) - 1; + HwInitPacketSize - (InitSequence.size() - 2) - (clear_state ? 0xc : 0) - 1; cmdbuf = WriteHeader(cmdbuf, cmdbuf_left); cmdbuf = WriteBody(cmdbuf, 0u); @@ -757,10 +775,10 @@ u32 PS4_SYSV_ABI sceGnmDrawInitDefaultHardwareState175(u32* cmdbuf, u32 size) { } cmdbuf = ClearContextState(cmdbuf); - std::memcpy(cmdbuf, InitSequence175.data(), InitSequence175.size() * 4); - cmdbuf += InitSequence175.size(); + std::memcpy(cmdbuf, &InitSequence175[2], (InitSequence175.size() - 2) * 4); + cmdbuf += InitSequence175.size() - 2; - constexpr auto cmdbuf_left = HwInitPacketSize - InitSequence175.size() - 0xc - 1; + constexpr auto cmdbuf_left = HwInitPacketSize - (InitSequence175.size() - 2) - 0xc - 1; WriteTrailingNop(cmdbuf); return HwInitPacketSize; @@ -778,11 +796,11 @@ u32 PS4_SYSV_ABI sceGnmDrawInitDefaultHardwareState200(u32* cmdbuf, u32 size) { cmdbuf = ClearContextState(cmdbuf); } - std::memcpy(cmdbuf, InitSequence200.data(), InitSequence200.size() * 4); - cmdbuf += InitSequence200.size(); + std::memcpy(cmdbuf, &InitSequence200[2], (InitSequence200.size() - 2) * 4); + cmdbuf += InitSequence200.size() - 2; const auto cmdbuf_left = - HwInitPacketSize - InitSequence200.size() - (clear_state ? 0xc : 0) - 1; + HwInitPacketSize - (InitSequence200.size() - 2) - (clear_state ? 0xc : 0) - 1; cmdbuf = WriteHeader(cmdbuf, cmdbuf_left); cmdbuf = WriteBody(cmdbuf, 0u); @@ -804,11 +822,11 @@ u32 PS4_SYSV_ABI sceGnmDrawInitDefaultHardwareState350(u32* cmdbuf, u32 size) { cmdbuf = ClearContextState(cmdbuf); } - std::memcpy(cmdbuf, InitSequence350.data(), InitSequence350.size() * 4); - cmdbuf += InitSequence350.size(); + std::memcpy(cmdbuf, &InitSequence350[2], (InitSequence350.size() - 2) * 4); + cmdbuf += InitSequence350.size() - 2; const auto cmdbuf_left = - HwInitPacketSize - InitSequence350.size() - (clear_state ? 0xc : 0) - 1; + HwInitPacketSize - (InitSequence350.size() - 2) - (clear_state ? 0xc : 0) - 1; cmdbuf = WriteHeader(cmdbuf, cmdbuf_left); cmdbuf = WriteBody(cmdbuf, 0u); @@ -1743,7 +1761,7 @@ s32 PS4_SYSV_ABI sceGnmSetVsShader(u32* cmdbuf, u32 size, const u32* vs_regs, u3 return -1; } - const u32 var = shader_modifier == 0 ? vs_regs[2] : (vs_regs[2] & 0xfcfffc3f | shader_modifier); + const u32 var = shader_modifier == 0 ? vs_regs[2] : (vs_regs[2] & 0xfcfffc3f) | shader_modifier; cmdbuf = PM4CmdSetData::SetShReg(cmdbuf, 0x48u, vs_regs[0], 0u); // SPI_SHADER_PGM_LO_VS cmdbuf = PM4CmdSetData::SetShReg(cmdbuf, 0x4au, var, vs_regs[3]); // SPI_SHADER_PGM_RSRC1_VS cmdbuf = PM4CmdSetData::SetContextReg(cmdbuf, 0x207u, vs_regs[6]); // PA_CL_VS_OUT_CNTL @@ -2328,9 +2346,9 @@ s32 PS4_SYSV_ABI sceGnmUpdateVsShader(u32* cmdbuf, u32 size, const u32* vs_regs, return ORBIS_OK; } -int PS4_SYSV_ABI sceGnmValidateCommandBuffers() { - LOG_ERROR(Lib_GnmDriver, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceGnmValidateCommandBuffers() { + LOG_TRACE(Lib_GnmDriver, "called"); + return ORBIS_GNM_ERROR_VALIDATION_NOT_ENABLED; // not available in retail FW; } int PS4_SYSV_ABI sceGnmValidateDisableDiagnostics() { diff --git a/src/core/libraries/gnmdriver/gnmdriver.h b/src/core/libraries/gnmdriver/gnmdriver.h index 84872297e..754d488f8 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.h +++ b/src/core/libraries/gnmdriver/gnmdriver.h @@ -45,7 +45,7 @@ s32 PS4_SYSV_ABI sceGnmDrawIndex(u32* cmdbuf, u32 size, u32 index_count, uintptr u32 flags, u32 type); s32 PS4_SYSV_ABI sceGnmDrawIndexAuto(u32* cmdbuf, u32 size, u32 index_count, u32 flags); s32 PS4_SYSV_ABI sceGnmDrawIndexIndirect(u32* cmdbuf, u32 size, u32 data_offset, u32 shader_stage, - u32 vertex_sgpr_offset, u32 instance_vgpr_offset, + u32 vertex_sgpr_offset, u32 instance_sgpr_offset, u32 flags); int PS4_SYSV_ABI sceGnmDrawIndexIndirectCountMulti(); int PS4_SYSV_ABI sceGnmDrawIndexIndirectMulti(); @@ -53,7 +53,7 @@ int PS4_SYSV_ABI sceGnmDrawIndexMultiInstanced(); s32 PS4_SYSV_ABI sceGnmDrawIndexOffset(u32* cmdbuf, u32 size, u32 index_offset, u32 index_count, u32 flags); s32 PS4_SYSV_ABI sceGnmDrawIndirect(u32* cmdbuf, u32 size, u32 data_offset, u32 shader_stage, - u32 vertex_sgpr_offset, u32 instance_vgpr_offset, u32 flags); + u32 vertex_sgpr_offset, u32 instance_sgpr_offset, u32 flags); int PS4_SYSV_ABI sceGnmDrawIndirectCountMulti(); int PS4_SYSV_ABI sceGnmDrawIndirectMulti(); u32 PS4_SYSV_ABI sceGnmDrawInitDefaultHardwareState(u32* cmdbuf, u32 size); @@ -223,7 +223,7 @@ s32 PS4_SYSV_ABI sceGnmUpdatePsShader(u32* cmdbuf, u32 size, const u32* ps_regs) s32 PS4_SYSV_ABI sceGnmUpdatePsShader350(u32* cmdbuf, u32 size, const u32* ps_regs); s32 PS4_SYSV_ABI sceGnmUpdateVsShader(u32* cmdbuf, u32 size, const u32* vs_regs, u32 shader_modifier); -int PS4_SYSV_ABI sceGnmValidateCommandBuffers(); +s32 PS4_SYSV_ABI sceGnmValidateCommandBuffers(); int PS4_SYSV_ABI sceGnmValidateDisableDiagnostics(); int PS4_SYSV_ABI sceGnmValidateDisableDiagnostics2(); int PS4_SYSV_ABI sceGnmValidateDispatchCommandBuffers(); diff --git a/src/core/libraries/kernel/event_flag/event_flag.cpp b/src/core/libraries/kernel/event_flag/event_flag.cpp index 8afd139c0..0fd0c3bb7 100644 --- a/src/core/libraries/kernel/event_flag/event_flag.cpp +++ b/src/core/libraries/kernel/event_flag/event_flag.cpp @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include +#include "common/assert.h" #include "common/logging/log.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" diff --git a/src/core/libraries/kernel/event_flag/event_flag_obj.cpp b/src/core/libraries/kernel/event_flag/event_flag_obj.cpp index ed01d7cc0..6d6dcf7a9 100644 --- a/src/core/libraries/kernel/event_flag/event_flag_obj.cpp +++ b/src/core/libraries/kernel/event_flag/event_flag_obj.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include + #include "core/libraries/error_codes.h" #include "event_flag_obj.h" diff --git a/src/core/libraries/kernel/event_flag/event_flag_obj.h b/src/core/libraries/kernel/event_flag/event_flag_obj.h index fe50be697..8d1624e2b 100644 --- a/src/core/libraries/kernel/event_flag/event_flag_obj.h +++ b/src/core/libraries/kernel/event_flag/event_flag_obj.h @@ -2,8 +2,10 @@ // SPDX-License-Identifier: GPL-2.0-or-later #pragma once + #include #include + #include "common/types.h" namespace Libraries::Kernel { diff --git a/src/core/libraries/kernel/event_queue.cpp b/src/core/libraries/kernel/event_queue.cpp index 3555fddc9..88918bf54 100644 --- a/src/core/libraries/kernel/event_queue.cpp +++ b/src/core/libraries/kernel/event_queue.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include + #include "common/assert.h" #include "core/libraries/kernel/event_queue.h" diff --git a/src/core/libraries/kernel/file_system.cpp b/src/core/libraries/kernel/file_system.cpp index 990b11d69..f7f58df59 100644 --- a/src/core/libraries/kernel/file_system.cpp +++ b/src/core/libraries/kernel/file_system.cpp @@ -538,7 +538,7 @@ void fileSystemSymbolsRegister(Core::Loader::SymbolsResolver* sym) { // openOrbis (to check if it is valid out of OpenOrbis LIB_FUNCTION("6c3rCVE-fTU", "libkernel", 1, "libkernel", 1, 1, - posix_open); // _open shoudld be equal to open function + posix_open); // _open should be equal to open function } } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/libkernel.h b/src/core/libraries/kernel/libkernel.h index 5b7f1e722..c28a548ff 100644 --- a/src/core/libraries/kernel/libkernel.h +++ b/src/core/libraries/kernel/libkernel.h @@ -4,6 +4,7 @@ #pragma once #include + #include "common/types.h" namespace Core::Loader { diff --git a/src/core/libraries/kernel/memory_management.cpp b/src/core/libraries/kernel/memory_management.cpp index fa3e1bc48..69e19a97e 100644 --- a/src/core/libraries/kernel/memory_management.cpp +++ b/src/core/libraries/kernel/memory_management.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include + #include "common/alignment.h" #include "common/assert.h" #include "common/logging/log.h" @@ -16,7 +17,8 @@ namespace Libraries::Kernel { u64 PS4_SYSV_ABI sceKernelGetDirectMemorySize() { LOG_WARNING(Kernel_Vmm, "called"); - return SCE_KERNEL_MAIN_DMEM_SIZE; + const auto* memory = Core::Memory::Instance(); + return memory->GetTotalDirectSize(); } int PS4_SYSV_ABI sceKernelAllocateDirectMemory(s64 searchStart, s64 searchEnd, u64 len, @@ -53,8 +55,8 @@ int PS4_SYSV_ABI sceKernelAllocateDirectMemory(s64 searchStart, s64 searchEnd, u s32 PS4_SYSV_ABI sceKernelAllocateMainDirectMemory(size_t len, size_t alignment, int memoryType, s64* physAddrOut) { - return sceKernelAllocateDirectMemory(0, SCE_KERNEL_MAIN_DMEM_SIZE, len, alignment, memoryType, - physAddrOut); + const auto searchEnd = static_cast(sceKernelGetDirectMemorySize()); + return sceKernelAllocateDirectMemory(0, searchEnd, len, alignment, memoryType, physAddrOut); } s32 PS4_SYSV_ABI sceKernelCheckedReleaseDirectMemory(u64 start, size_t len) { @@ -76,19 +78,28 @@ s32 PS4_SYSV_ABI sceKernelAvailableDirectMemorySize(u64 searchStart, u64 searchE LOG_WARNING(Kernel_Vmm, "called searchStart = {:#x}, searchEnd = {:#x}, alignment = {:#x}", searchStart, searchEnd, alignment); - if (searchEnd <= searchStart) { + if (physAddrOut == nullptr || sizeOut == nullptr) { return ORBIS_KERNEL_ERROR_EINVAL; } - if (searchEnd > SCE_KERNEL_MAIN_DMEM_SIZE) { + if (searchEnd > sceKernelGetDirectMemorySize()) { return ORBIS_KERNEL_ERROR_EINVAL; } + if (searchEnd <= searchStart) { + return ORBIS_KERNEL_ERROR_ENOMEM; + } auto* memory = Core::Memory::Instance(); - PAddr physAddr; - s32 result = - memory->DirectQueryAvailable(searchStart, searchEnd, alignment, &physAddr, sizeOut); + PAddr physAddr{}; + size_t size{}; + s32 result = memory->DirectQueryAvailable(searchStart, searchEnd, alignment, &physAddr, &size); + + if (size == 0) { + return ORBIS_KERNEL_ERROR_ENOMEM; + } + *physAddrOut = static_cast(physAddr); + *sizeOut = size; return result; } @@ -259,6 +270,7 @@ int PS4_SYSV_ABI sceKernelMunmap(void* addr, size_t len); s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEntries, int* numEntriesOut, int flags) { + int result = ORBIS_OK; int processed = 0; int result = 0; for (int i = 0; i < numEntries; i++) { @@ -267,20 +279,19 @@ s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEn break; // break and assign a value to numEntriesOut. } - if (entries[i].operation == MemoryOpTypes::ORBIS_KERNEL_MAP_OP_MAP_DIRECT) { + switch (entries[i].operation) { + case MemoryOpTypes::ORBIS_KERNEL_MAP_OP_MAP_DIRECT: { result = sceKernelMapNamedDirectMemory(&entries[i].start, entries[i].length, entries[i].protection, flags, static_cast(entries[i].offset), 0, ""); - LOG_INFO( - Kernel_Vmm, - "BatchMap: entry = {}, operation = {}, len = {:#x}, offset = {:#x}, type = {}, " - "result = {}", - i, entries[i].operation, entries[i].length, entries[i].offset, (u8)entries[i].type, - result); - - if (result == 0) - processed++; - } else if (entries[i].operation == MemoryOpTypes::ORBIS_KERNEL_MAP_OP_UNMAP) { + LOG_INFO(Kernel_Vmm, + "entry = {}, operation = {}, len = {:#x}, offset = {:#x}, type = {}, " + "result = {}", + i, entries[i].operation, entries[i].length, entries[i].offset, + (u8)entries[i].type, result); + break; + } + case MemoryOpTypes::ORBIS_KERNEL_MAP_OP_UNMAP: { result = sceKernelMunmap(entries[i].start, entries[i].length); LOG_INFO(Kernel_Vmm, "BatchMap: entry = {}, operation = {}, len = {:#x}, result = {}", i, entries[i].operation, entries[i].length, result); @@ -314,14 +325,26 @@ s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEn result = sceKernelMapNamedFlexibleMemory(&entries[i].start, entries[i].length, entries[i].protection, flags, ""); LOG_INFO(Kernel_Vmm, - "BatchMap: entry = {}, operation = {}, len = {:#x}, type = {}, " + "entry = {}, operation = {}, len = {:#x}, type = {}, " "result = {}", i, entries[i].operation, entries[i].length, (u8)entries[i].type, result); + break; + } + case MemoryOpTypes::ORBIS_KERNEL_MAP_OP_TYPE_PROTECT: { + // By now, ignore protection and log it instead + LOG_WARNING(Kernel_Vmm, + "entry = {}, operation = {}, len = {:#x}, type = {} " + "is UNSUPPORTED and skipped", + i, entries[i].operation, entries[i].length, (u8)entries[i].type); + break; + } + default: { + UNREACHABLE(); + } + } - if (result == 0) - processed++; - } else { - UNREACHABLE_MSG("called: Unimplemented Operation = {}", entries[i].operation); + if (result != ORBIS_OK) { + break; } } LOG_INFO(Kernel_Vmm, "sceKernelBatchMap2 finished: processed = {}, result = {}", processed, diff --git a/src/core/libraries/kernel/memory_management.h b/src/core/libraries/kernel/memory_management.h index 97018e43f..b48990bfb 100644 --- a/src/core/libraries/kernel/memory_management.h +++ b/src/core/libraries/kernel/memory_management.h @@ -6,7 +6,7 @@ #include "common/bit_field.h" #include "common/types.h" -constexpr u64 SCE_KERNEL_MAIN_DMEM_SIZE = 6_GB; // ~ 6GB +constexpr u64 SCE_KERNEL_MAIN_DMEM_SIZE = 5056_MB; // ~ 5GB namespace Libraries::Kernel { diff --git a/src/core/libraries/kernel/thread_management.cpp b/src/core/libraries/kernel/thread_management.cpp index 689532693..11d472a49 100644 --- a/src/core/libraries/kernel/thread_management.cpp +++ b/src/core/libraries/kernel/thread_management.cpp @@ -2,20 +2,23 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include -#include + #include "common/alignment.h" #include "common/assert.h" #include "common/error.h" #include "common/logging/log.h" #include "common/singleton.h" #include "common/thread.h" +#include "core/cpu_patches.h" #include "core/libraries/error_codes.h" #include "core/libraries/kernel/libkernel.h" #include "core/libraries/kernel/thread_management.h" #include "core/libraries/kernel/threads/threads.h" #include "core/libraries/libs.h" #include "core/linker.h" +#include "core/tls.h" #ifdef _WIN64 #include #else @@ -510,23 +513,24 @@ int PS4_SYSV_ABI scePthreadMutexattrInit(ScePthreadMutexattr* attr) { int PS4_SYSV_ABI scePthreadMutexattrSettype(ScePthreadMutexattr* attr, int type) { int ptype = PTHREAD_MUTEX_DEFAULT; switch (type) { - case 1: + case ORBIS_PTHREAD_MUTEX_ERRORCHECK: ptype = PTHREAD_MUTEX_ERRORCHECK; break; - case 2: + case ORBIS_PTHREAD_MUTEX_RECURSIVE: ptype = PTHREAD_MUTEX_RECURSIVE; break; - case 3: - case 4: + case ORBIS_PTHREAD_MUTEX_NORMAL: + case ORBIS_PTHREAD_MUTEX_ADAPTIVE: ptype = PTHREAD_MUTEX_NORMAL; break; default: - UNREACHABLE_MSG("Invalid type: {}", type); + return SCE_KERNEL_ERROR_EINVAL; } int result = pthread_mutexattr_settype(&(*attr)->pth_mutex_attr, ptype); + ASSERT(result == 0); - return result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL; + return SCE_OK; } int PS4_SYSV_ABI scePthreadMutexattrSetprotocol(ScePthreadMutexattr* attr, int protocol) { @@ -984,12 +988,14 @@ static void cleanup_thread(void* arg) { destructor(value); } } + Core::SetTcbBase(nullptr); thread->is_almost_done = true; } static void* run_thread(void* arg) { auto* thread = static_cast(arg); Common::SetCurrentThreadName(thread->name.c_str()); + Core::InitializeThreadPatchStack(); auto* linker = Common::Singleton::Instance(); linker->InitTlsForThread(false); void* ret = nullptr; @@ -1123,7 +1129,6 @@ int PS4_SYSV_ABI posix_pthread_join(ScePthread thread, void** res) { } int PS4_SYSV_ABI scePthreadDetach(ScePthread thread) { - LOG_INFO(Kernel_Pthread, "thread create name = {}", thread->name); thread->is_detached = true; return ORBIS_OK; } @@ -1374,90 +1379,97 @@ int PS4_SYSV_ABI posix_pthread_detach(ScePthread thread) { return pthread_detach(thread->pth); } -int PS4_SYSV_ABI posix_sem_init(sem_t* sem, int pshared, unsigned int value) { - int result = sem_init(sem, pshared, value); - if (result == -1) { - SetPosixErrno(errno); +int PS4_SYSV_ABI posix_sem_init(PthreadSemInternal** sem, int pshared, unsigned int value) { + if (value > ORBIS_KERNEL_SEM_VALUE_MAX) { + SetPosixErrno(EINVAL); + return -1; } - return result; + if (sem != nullptr) { + *sem = new PthreadSemInternal{ + .semaphore = std::counting_semaphore{value}, + .value = {static_cast(value)}, + }; + } + return 0; } -int PS4_SYSV_ABI posix_sem_wait(sem_t* sem) { - int result = sem_wait(sem); - if (result == -1) { - SetPosixErrno(errno); +int PS4_SYSV_ABI posix_sem_wait(PthreadSemInternal** sem) { + if (sem == nullptr || *sem == nullptr) { + SetPosixErrno(EINVAL); + return -1; } - return result; + (*sem)->semaphore.acquire(); + --(*sem)->value; + return 0; } -int PS4_SYSV_ABI posix_sem_trywait(sem_t* sem) { - int result = sem_trywait(sem); - if (result == -1) { - SetPosixErrno(errno); +int PS4_SYSV_ABI posix_sem_trywait(PthreadSemInternal** sem) { + if (sem == nullptr || *sem == nullptr) { + SetPosixErrno(EINVAL); + return -1; } - return result; + if (!(*sem)->semaphore.try_acquire()) { + SetPosixErrno(EAGAIN); + return -1; + } + --(*sem)->value; + return 0; } -#ifndef HAVE_SEM_TIMEDWAIT -int sem_timedwait(sem_t* sem, const struct timespec* abstime) { - int rc; - while ((rc = sem_trywait(sem)) == EAGAIN) { - struct timespec curr_time; - clock_gettime(CLOCK_REALTIME, &curr_time); - - s64 remaining_ns = 0; - remaining_ns += - (static_cast(abstime->tv_sec) - static_cast(curr_time.tv_sec)) * 1000000000L; - remaining_ns += static_cast(abstime->tv_nsec) - static_cast(curr_time.tv_nsec); - - if (remaining_ns <= 0) { - return ETIMEDOUT; - } - - struct timespec sleep_time; - sleep_time.tv_sec = 0; - if (remaining_ns < 5000000L) { - sleep_time.tv_nsec = remaining_ns; - } else { - sleep_time.tv_nsec = 5000000; - } - - nanosleep(&sleep_time, nullptr); +int PS4_SYSV_ABI posix_sem_timedwait(PthreadSemInternal** sem, const timespec* t) { + if (sem == nullptr || *sem == nullptr) { + SetPosixErrno(EINVAL); + return -1; } - return rc; -} -#endif -int PS4_SYSV_ABI posix_sem_timedwait(sem_t* sem, const timespec* t) { - int result = sem_timedwait(sem, t); - if (result == -1) { - SetPosixErrno(errno); + using std::chrono::duration_cast; + using std::chrono::nanoseconds; + using std::chrono::seconds; + using std::chrono::system_clock; + + const system_clock::time_point time{ + duration_cast(seconds{t->tv_sec} + nanoseconds{t->tv_nsec})}; + if (!(*sem)->semaphore.try_acquire_until(time)) { + SetPosixErrno(ETIMEDOUT); + return -1; } - return result; + --(*sem)->value; + return 0; } -int PS4_SYSV_ABI posix_sem_post(sem_t* sem) { - int result = sem_post(sem); - if (result == -1) { - SetPosixErrno(errno); +int PS4_SYSV_ABI posix_sem_post(PthreadSemInternal** sem) { + if (sem == nullptr || *sem == nullptr) { + SetPosixErrno(EINVAL); + return -1; } - return result; + if ((*sem)->value == ORBIS_KERNEL_SEM_VALUE_MAX) { + SetPosixErrno(EOVERFLOW); + return -1; + } + ++(*sem)->value; + (*sem)->semaphore.release(); + return 0; } -int PS4_SYSV_ABI posix_sem_destroy(sem_t* sem) { - int result = sem_destroy(sem); - if (result == -1) { - SetPosixErrno(errno); +int PS4_SYSV_ABI posix_sem_destroy(PthreadSemInternal** sem) { + if (sem == nullptr || *sem == nullptr) { + SetPosixErrno(EINVAL); + return -1; } - return result; + delete *sem; + *sem = nullptr; + return 0; } -int PS4_SYSV_ABI posix_sem_getvalue(sem_t* sem, int* sval) { - int result = sem_getvalue(sem, sval); - if (result == -1) { - SetPosixErrno(errno); +int PS4_SYSV_ABI posix_sem_getvalue(PthreadSemInternal** sem, int* sval) { + if (sem == nullptr || *sem == nullptr) { + SetPosixErrno(EINVAL); + return -1; } - return result; + if (sval) { + *sval = (*sem)->value; + } + return 0; } int PS4_SYSV_ABI posix_pthread_attr_getstacksize(const pthread_attr_t* attr, size_t* size) { diff --git a/src/core/libraries/kernel/thread_management.h b/src/core/libraries/kernel/thread_management.h index c5935275f..a2b2f6fea 100644 --- a/src/core/libraries/kernel/thread_management.h +++ b/src/core/libraries/kernel/thread_management.h @@ -5,10 +5,12 @@ #include #include +#include #include #include #include #include + #include "common/types.h" namespace Core::Loader { @@ -19,6 +21,12 @@ namespace Libraries::Kernel { constexpr int ORBIS_KERNEL_PRIO_FIFO_DEFAULT = 700; constexpr int ORBIS_KERNEL_PRIO_FIFO_HIGHEST = 256; constexpr int ORBIS_KERNEL_PRIO_FIFO_LOWEST = 767; +constexpr int ORBIS_KERNEL_SEM_VALUE_MAX = 0x7FFFFFFF; + +constexpr int ORBIS_PTHREAD_MUTEX_ERRORCHECK = 1; +constexpr int ORBIS_PTHREAD_MUTEX_RECURSIVE = 2; +constexpr int ORBIS_PTHREAD_MUTEX_NORMAL = 3; +constexpr int ORBIS_PTHREAD_MUTEX_ADAPTIVE = 4; struct PthreadInternal; struct PthreadAttrInternal; @@ -104,6 +112,11 @@ struct PthreadRwInternal { std::string name; }; +struct PthreadSemInternal { + std::counting_semaphore semaphore; + std::atomic value; +}; + class PThreadPool { public: ScePthread Create(); diff --git a/src/core/libraries/kernel/threads/semaphore.cpp b/src/core/libraries/kernel/threads/semaphore.cpp index 5304dc57b..63ca25338 100644 --- a/src/core/libraries/kernel/threads/semaphore.cpp +++ b/src/core/libraries/kernel/threads/semaphore.cpp @@ -2,10 +2,10 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include -#include -#include #include + #include "common/assert.h" #include "common/logging/log.h" #include "core/libraries/error_codes.h" @@ -13,9 +13,6 @@ namespace Libraries::Kernel { -using ListBaseHook = - boost::intrusive::list_base_hook>; - class Semaphore { public: Semaphore(s32 init_count, s32 max_count, std::string_view name, bool is_fifo) @@ -37,7 +34,7 @@ public: // Create waiting thread object and add it into the list of waiters. WaitingThread waiter{need_count, is_fifo}; - AddWaiter(waiter); + AddWaiter(&waiter); // Perform the wait. return waiter.Wait(lk, timeout); @@ -52,14 +49,14 @@ public: // Wake up threads in order of priority. for (auto it = wait_list.begin(); it != wait_list.end();) { - auto& waiter = *it; - if (waiter.need_count > token_count) { + auto* waiter = *it; + if (waiter->need_count > token_count) { it++; continue; } it = wait_list.erase(it); - token_count -= waiter.need_count; - waiter.cv.notify_one(); + token_count -= waiter->need_count; + waiter->cv.notify_one(); } return true; @@ -70,9 +67,9 @@ public: if (num_waiters) { *num_waiters = wait_list.size(); } - for (auto& waiter : wait_list) { - waiter.was_cancled = true; - waiter.cv.notify_one(); + for (auto* waiter : wait_list) { + waiter->was_cancled = true; + waiter->cv.notify_one(); } wait_list.clear(); token_count = set_count < 0 ? init_count : set_count; @@ -80,7 +77,7 @@ public: } public: - struct WaitingThread : public ListBaseHook { + struct WaitingThread { std::condition_variable cv; u32 priority; s32 need_count; @@ -132,7 +129,7 @@ public: } }; - void AddWaiter(WaitingThread& waiter) { + void AddWaiter(WaitingThread* waiter) { // Insert at the end of the list for FIFO order. if (is_fifo) { wait_list.push_back(waiter); @@ -140,16 +137,13 @@ public: } // Find the first with priority less then us and insert right before it. auto it = wait_list.begin(); - while (it != wait_list.end() && it->priority > waiter.priority) { + while (it != wait_list.end() && (*it)->priority > waiter->priority) { it++; } wait_list.insert(it, waiter); } - using WaitingThreads = - boost::intrusive::list, - boost::intrusive::constant_time_size>; - WaitingThreads wait_list; + std::list wait_list; std::string name; std::atomic token_count; std::mutex mutex; diff --git a/src/core/libraries/kernel/time_management.cpp b/src/core/libraries/kernel/time_management.cpp index c4854937b..7a6ba4f62 100644 --- a/src/core/libraries/kernel/time_management.cpp +++ b/src/core/libraries/kernel/time_management.cpp @@ -2,7 +2,9 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include + #include "common/assert.h" +#include "common/debug.h" #include "common/native_clock.h" #include "core/libraries/error_codes.h" #include "core/libraries/kernel/time_management.h" @@ -30,7 +32,8 @@ u64 PS4_SYSV_ABI sceKernelGetTscFrequency() { } u64 PS4_SYSV_ABI sceKernelGetProcessTime() { - return clock->GetProcessTimeUS(); + // TODO: this timer should support suspends, so initial ptc needs to be updated on wake up + return clock->GetTimeUS(initial_ptc); } u64 PS4_SYSV_ABI sceKernelGetProcessTimeCounter() { @@ -143,6 +146,7 @@ int PS4_SYSV_ABI sceKernelGettimeofday(OrbisKernelTimeval* tp) { return ORBIS_KERNEL_ERROR_EFAULT; } +#ifdef _WIN64 auto now = std::chrono::system_clock::now(); auto duration = now.time_since_epoch(); auto seconds = std::chrono::duration_cast(duration); @@ -150,6 +154,12 @@ int PS4_SYSV_ABI sceKernelGettimeofday(OrbisKernelTimeval* tp) { tp->tv_sec = seconds.count(); tp->tv_usec = microsecs.count(); +#else + timeval tv; + gettimeofday(&tv, nullptr); + tp->tv_sec = tv.tv_sec; + tp->tv_usec = tv.tv_usec; +#endif return ORBIS_OK; } diff --git a/src/core/libraries/libc/libc.cpp b/src/core/libraries/libc/libc.cpp deleted file mode 100644 index dafb16bed..000000000 --- a/src/core/libraries/libc/libc.cpp +++ /dev/null @@ -1,496 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include "common/debug.h" -#include "common/logging/log.h" -#include "common/singleton.h" -#include "core/libraries/libc/libc.h" -#include "core/libraries/libc/libc_cxa.h" -#include "core/libraries/libc/libc_math.h" -#include "core/libraries/libc/libc_stdio.h" -#include "core/libraries/libc/libc_stdlib.h" -#include "core/libraries/libc/libc_string.h" -#include "core/libraries/libs.h" - -namespace Libraries::LibC { - -constexpr bool log_file_libc = true; // disable it to disable logging -static u32 g_need_sceLibc = 1; - -using cxa_destructor_func_t = void (*)(void*); - -struct CxaDestructor { - cxa_destructor_func_t destructor_func; - void* destructor_object; - void* module_id; -}; - -struct CContext { - std::vector cxa; -}; - -static PS4_SYSV_ABI int ps4___cxa_atexit(void (*func)(void*), void* arg, void* dso_handle) { - auto* cc = Common::Singleton::Instance(); - CxaDestructor c{}; - c.destructor_func = func; - c.destructor_object = arg; - c.module_id = dso_handle; - cc->cxa.push_back(c); - return 0; -} - -void PS4_SYSV_ABI ps4___cxa_finalize(void* d) { - BREAKPOINT(); -} - -void PS4_SYSV_ABI ps4___cxa_pure_virtual() { - BREAKPOINT(); -} - -static PS4_SYSV_ABI void ps4_init_env() { - LOG_INFO(Lib_LibC, "called"); -} - -static PS4_SYSV_ABI void ps4_catchReturnFromMain(int status) { - LOG_INFO(Lib_LibC, "returned = {}", status); -} - -static PS4_SYSV_ABI void ps4__Assert() { - LOG_INFO(Lib_LibC, "called"); - BREAKPOINT(); -} - -PS4_SYSV_ABI void ps4__ZdlPv(void* ptr) { - std::free(ptr); -} - -PS4_SYSV_ABI void ps4__ZSt11_Xbad_allocv() { - LOG_INFO(Lib_LibC, "called"); - BREAKPOINT(); -} - -PS4_SYSV_ABI void ps4__ZSt14_Xlength_errorPKc() { - LOG_INFO(Lib_LibC, "called"); - BREAKPOINT(); -} - -PS4_SYSV_ABI void* ps4__Znwm(u64 count) { - if (count == 0) { - LOG_INFO(Lib_LibC, "_Znwm count ={}", count); - BREAKPOINT(); - } - void* ptr = std::malloc(count); - return ptr; -} - -static constexpr u16 lowercaseTable[256] = { - 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, - 0x000C, 0x000D, 0x000E, 0x000F, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, - 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, 0x0020, 0x0021, 0x0022, 0x0023, - 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, - 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, - 0x003C, 0x003D, 0x003E, 0x003F, 0x0040, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, - 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x0073, - 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, - 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, - 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, - 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, 0x0080, 0x0081, 0x0082, 0x0083, - 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F, - 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, - 0x009C, 0x009D, 0x009E, 0x009F, 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, - 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, 0x00B0, 0x00B1, 0x00B2, 0x00B3, - 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, - 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, - 0x00CC, 0x00CD, 0x00CE, 0x00CF, 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, - 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, 0x00E0, 0x00E1, 0x00E2, 0x00E3, - 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, - 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, - 0x00FC, 0x00FD, 0x00FE, 0x00FF, -}; - -const PS4_SYSV_ABI u16* ps4__Getptolower() { - return &lowercaseTable[0]; -} - -static constexpr u16 uppercaseTable[256] = { - 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, - 0x000C, 0x000D, 0x000E, 0x000F, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, - 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, 0x0020, 0x0021, 0x0022, 0x0023, - 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, - 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, - 0x003C, 0x003D, 0x003E, 0x003F, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, - 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x0053, - 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, - 0x0060, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, - 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, - 0x0058, 0x0059, 0x005A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, 0x0080, 0x0081, 0x0082, 0x0083, - 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F, - 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, - 0x009C, 0x009D, 0x009E, 0x009F, 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, - 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, 0x00B0, 0x00B1, 0x00B2, 0x00B3, - 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, - 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, - 0x00CC, 0x00CD, 0x00CE, 0x00CF, 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, - 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, 0x00E0, 0x00E1, 0x00E2, 0x00E3, - 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, - 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, - 0x00FC, 0x00FD, 0x00FE, 0x00FF, -}; - -const PS4_SYSV_ABI u16* ps4__Getptoupper() { - return &uppercaseTable[0]; -} - -namespace CharacterType { -enum : u16 { - HexDigit = 0x1, // '0'-'9', 'A'-'F', 'a'-'f' - Uppercase = 0x2, // 'A'-'Z' - Space = 0x4, - Punctuation = 0x08, - Lowercase = 0x10, // 'a'-'z' - DecimalDigit = 0x20, // '0'-'9' - Control = 0x40, // CR, FF, HT, NL, VT - Control2 = 0x80, // BEL, BS, etc - ExtraSpace = 0x100, - ExtraAlphabetic = 0x200, - ExtraBlank = 0x400 -}; -} - -static constexpr u16 characterTypeTable[256] = { - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control | CharacterType::Control2 | CharacterType::ExtraBlank, - CharacterType::Control | CharacterType::Control2, - CharacterType::Control | CharacterType::Control2, - CharacterType::Control | CharacterType::Control2, - CharacterType::Control | CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Control2, - CharacterType::Space, // - CharacterType::Punctuation, // ! - CharacterType::Punctuation, // " - CharacterType::Punctuation, // # - CharacterType::Punctuation, // $ - CharacterType::Punctuation, // % - CharacterType::Punctuation, // & - CharacterType::Punctuation, // ' - CharacterType::Punctuation, // ( - CharacterType::Punctuation, // ) - CharacterType::Punctuation, // * - CharacterType::Punctuation, // + - CharacterType::Punctuation, // , - CharacterType::Punctuation, // - - CharacterType::Punctuation, // . - CharacterType::Punctuation, // / - CharacterType::HexDigit | CharacterType::DecimalDigit, // 0 - CharacterType::HexDigit | CharacterType::DecimalDigit, // 1 - CharacterType::HexDigit | CharacterType::DecimalDigit, // 2 - CharacterType::HexDigit | CharacterType::DecimalDigit, // 3 - CharacterType::HexDigit | CharacterType::DecimalDigit, // 4 - CharacterType::HexDigit | CharacterType::DecimalDigit, // 5 - CharacterType::HexDigit | CharacterType::DecimalDigit, // 6 - CharacterType::HexDigit | CharacterType::DecimalDigit, // 7 - CharacterType::HexDigit | CharacterType::DecimalDigit, // 8 - CharacterType::HexDigit | CharacterType::DecimalDigit, // 9 - CharacterType::Punctuation, // : - CharacterType::Punctuation, // ; - CharacterType::Punctuation, // < - CharacterType::Punctuation, // = - CharacterType::Punctuation, // > - CharacterType::Punctuation, // ? - CharacterType::Punctuation, // @ - CharacterType::HexDigit | CharacterType::Uppercase, // A - CharacterType::HexDigit | CharacterType::Uppercase, // B - CharacterType::HexDigit | CharacterType::Uppercase, // C - CharacterType::HexDigit | CharacterType::Uppercase, // D - CharacterType::HexDigit | CharacterType::Uppercase, // E - CharacterType::HexDigit | CharacterType::Uppercase, // F - CharacterType::Uppercase, // G - CharacterType::Uppercase, // H - CharacterType::Uppercase, // I - CharacterType::Uppercase, // J - CharacterType::Uppercase, // K - CharacterType::Uppercase, // L - CharacterType::Uppercase, // M - CharacterType::Uppercase, // N - CharacterType::Uppercase, // O - CharacterType::Uppercase, // P - CharacterType::Uppercase, // Q - CharacterType::Uppercase, // R - CharacterType::Uppercase, // S - CharacterType::Uppercase, // T - CharacterType::Uppercase, // U - CharacterType::Uppercase, // V - CharacterType::Uppercase, // W - CharacterType::Uppercase, // X - CharacterType::Uppercase, // Y - CharacterType::Uppercase, // Z - CharacterType::Punctuation, // [ - CharacterType::Punctuation, // - CharacterType::Punctuation, // ] - CharacterType::Punctuation, // ^ - CharacterType::Punctuation, // _ - CharacterType::Punctuation, // ` - CharacterType::HexDigit | CharacterType::Lowercase, // a - CharacterType::HexDigit | CharacterType::Lowercase, // b - CharacterType::HexDigit | CharacterType::Lowercase, // c - CharacterType::HexDigit | CharacterType::Lowercase, // d - CharacterType::HexDigit | CharacterType::Lowercase, // e - CharacterType::HexDigit | CharacterType::Lowercase, // f - CharacterType::Lowercase, // g - CharacterType::Lowercase, // h - CharacterType::Lowercase, // i - CharacterType::Lowercase, // j - CharacterType::Lowercase, // k - CharacterType::Lowercase, // l - CharacterType::Lowercase, // m - CharacterType::Lowercase, // n - CharacterType::Lowercase, // o - CharacterType::Lowercase, // p - CharacterType::Lowercase, // q - CharacterType::Lowercase, // r - CharacterType::Lowercase, // s - CharacterType::Lowercase, // t - CharacterType::Lowercase, // u - CharacterType::Lowercase, // v - CharacterType::Lowercase, // w - CharacterType::Lowercase, // x - CharacterType::Lowercase, // y - CharacterType::Lowercase, // z - CharacterType::Punctuation, // { - CharacterType::Punctuation, // | - CharacterType::Punctuation, // } - CharacterType::Punctuation, // ~ - CharacterType::Control2, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, -}; - -const PS4_SYSV_ABI u16* ps4__Getpctype() { - return &characterTypeTable[0]; -} - -void libcSymbolsRegister(Core::Loader::SymbolsResolver* sym) { - // cxa functions - LIB_FUNCTION("3GPpjQdAMTw", "libc", 1, "libc", 1, 1, ps4___cxa_guard_acquire); - LIB_FUNCTION("9rAeANT2tyE", "libc", 1, "libc", 1, 1, ps4___cxa_guard_release); - LIB_FUNCTION("2emaaluWzUw", "libc", 1, "libc", 1, 1, ps4___cxa_guard_abort); - - // stdlib functions - LIB_FUNCTION("uMei1W9uyNo", "libc", 1, "libc", 1, 1, ps4_exit); - LIB_FUNCTION("8G2LB+A3rzg", "libc", 1, "libc", 1, 1, ps4_atexit); - LIB_FUNCTION("gQX+4GDQjpM", "libc", 1, "libc", 1, 1, ps4_malloc); - LIB_FUNCTION("tIhsqj0qsFE", "libc", 1, "libc", 1, 1, ps4_free); - LIB_FUNCTION("cpCOXWMgha0", "libc", 1, "libc", 1, 1, ps4_rand); - LIB_FUNCTION("AEJdIVZTEmo", "libc", 1, "libc", 1, 1, ps4_qsort); - - // math functions - LIB_FUNCTION("EH-x713A99c", "libc", 1, "libc", 1, 1, ps4_atan2f); - LIB_FUNCTION("QI-x0SL8jhw", "libc", 1, "libc", 1, 1, ps4_acosf); - LIB_FUNCTION("ZE6RNL+eLbk", "libc", 1, "libc", 1, 1, ps4_tanf); - LIB_FUNCTION("GZWjF-YIFFk", "libc", 1, "libc", 1, 1, ps4_asinf); - LIB_FUNCTION("9LCjpWyQ5Zc", "libc", 1, "libc", 1, 1, ps4_pow); - LIB_FUNCTION("cCXjU72Z0Ow", "libc", 1, "libc", 1, 1, ps4__Sin); - LIB_FUNCTION("ZtjspkJQ+vw", "libc", 1, "libc", 1, 1, ps4__Fsin); - LIB_FUNCTION("dnaeGXbjP6E", "libc", 1, "libc", 1, 1, ps4_exp2); - LIB_FUNCTION("1D0H2KNjshE", "libc", 1, "libc", 1, 1, ps4_powf); - LIB_FUNCTION("DDHG1a6+3q0", "libc", 1, "libc", 1, 1, ps4_roundf); - - // string functions - LIB_FUNCTION("Ovb2dSJOAuE", "libc", 1, "libc", 1, 1, ps4_strcmp); - LIB_FUNCTION("j4ViWNHEgww", "libc", 1, "libc", 1, 1, ps4_strlen); - LIB_FUNCTION("6sJWiWSRuqk", "libc", 1, "libc", 1, 1, ps4_strncpy); - LIB_FUNCTION("+P6FRGH4LfA", "libc", 1, "libc", 1, 1, ps4_memmove); - LIB_FUNCTION("kiZSXIWd9vg", "libc", 1, "libc", 1, 1, ps4_strcpy); - LIB_FUNCTION("Ls4tzzhimqQ", "libc", 1, "libc", 1, 1, ps4_strcat); - LIB_FUNCTION("DfivPArhucg", "libc", 1, "libc", 1, 1, ps4_memcmp); - LIB_FUNCTION("Q3VBxCXhUHs", "libc", 1, "libc", 1, 1, ps4_memcpy); - LIB_FUNCTION("8zTFvBIAIN8", "libc", 1, "libc", 1, 1, ps4_memset); - LIB_FUNCTION("9yDWMxEFdJU", "libc", 1, "libc", 1, 1, ps4_strrchr); - LIB_FUNCTION("aesyjrHVWy4", "libc", 1, "libc", 1, 1, ps4_strncmp); - LIB_FUNCTION("g7zzzLDYGw0", "libc", 1, "libc", 1, 1, ps4_strdup); - - // stdio functions - LIB_FUNCTION("xeYO4u7uyJ0", "libc", 1, "libc", 1, 1, ps4_fopen); - // LIB_FUNCTION("hcuQgD53UxM", "libc", 1, "libc", 1, 1, ps4_printf); - LIB_FUNCTION("Q2V+iqvjgC0", "libc", 1, "libc", 1, 1, ps4_vsnprintf); - LIB_FUNCTION("YQ0navp+YIc", "libc", 1, "libc", 1, 1, ps4_puts); - // LIB_FUNCTION("fffwELXNVFA", "libc", 1, "libc", 1, 1, ps4_fprintf); - LIB_FUNCTION("QMFyLoqNxIg", "libc", 1, "libc", 1, 1, ps4_setvbuf); - LIB_FUNCTION("uodLYyUip20", "libc", 1, "libc", 1, 1, ps4_fclose); - LIB_FUNCTION("rQFVBXp-Cxg", "libc", 1, "libc", 1, 1, ps4_fseek); - LIB_FUNCTION("SHlt7EhOtqA", "libc", 1, "libc", 1, 1, ps4_fgetpos); - LIB_FUNCTION("lbB+UlZqVG0", "libc", 1, "libc", 1, 1, ps4_fread); - LIB_FUNCTION("Qazy8LmXTvw", "libc", 1, "libc", 1, 1, ps4_ftell); - - // misc - LIB_OBJ("P330P3dFF68", "libc", 1, "libc", 1, 1, &g_need_sceLibc); - LIB_OBJ("2sWzhYqFH4E", "libc", 1, "libc", 1, 1, stdout); - LIB_OBJ("H8AprKeZtNg", "libc", 1, "libc", 1, 1, stderr); - LIB_FUNCTION("bzQExy189ZI", "libc", 1, "libc", 1, 1, ps4_init_env); - LIB_FUNCTION("XKRegsFpEpk", "libc", 1, "libc", 1, 1, ps4_catchReturnFromMain); - LIB_FUNCTION("-QgqOT5u2Vk", "libc", 1, "libc", 1, 1, ps4__Assert); - LIB_FUNCTION("z+P+xCnWLBk", "libc", 1, "libc", 1, 1, ps4__ZdlPv); - LIB_FUNCTION("eT2UsmTewbU", "libc", 1, "libc", 1, 1, ps4__ZSt11_Xbad_allocv); - LIB_FUNCTION("tQIo+GIPklo", "libc", 1, "libc", 1, 1, ps4__ZSt14_Xlength_errorPKc); - LIB_FUNCTION("fJnpuVVBbKk", "libc", 1, "libc", 1, 1, ps4__Znwm); - LIB_FUNCTION("tsvEmnenz48", "libc", 1, "libc", 1, 1, ps4___cxa_atexit); - LIB_FUNCTION("H2e8t5ScQGc", "libc", 1, "libc", 1, 1, ps4___cxa_finalize); - LIB_FUNCTION("zr094EQ39Ww", "libc", 1, "libc", 1, 1, ps4___cxa_pure_virtual); - LIB_FUNCTION("1uJgoVq3bQU", "libc", 1, "libc", 1, 1, ps4__Getptolower); - LIB_FUNCTION("rcQCUr0EaRU", "libc", 1, "libc", 1, 1, ps4__Getptoupper); - LIB_FUNCTION("sUP1hBaouOw", "libc", 1, "libc", 1, 1, ps4__Getpctype); -} - -}; // namespace Libraries::LibC diff --git a/src/core/libraries/libc/libc.h b/src/core/libraries/libc/libc.h deleted file mode 100644 index 6504b8d71..000000000 --- a/src/core/libraries/libc/libc.h +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -namespace Core::Loader { -class SymbolsResolver; -} - -namespace Libraries::LibC { - -void libcSymbolsRegister(Core::Loader::SymbolsResolver* sym); - -} // namespace Libraries::LibC diff --git a/src/core/libraries/libc/libc_cxa.cpp b/src/core/libraries/libc/libc_cxa.cpp deleted file mode 100644 index ff10d3f08..000000000 --- a/src/core/libraries/libc/libc_cxa.cpp +++ /dev/null @@ -1,161 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/logging/log.h" -#include "core/libraries/libc/libc_cxa.h" - -// adapted from -// https://opensource.apple.com/source/libcppabi/libcppabi-14/src/cxa_guard.cxx.auto.html - -namespace Libraries::LibC { - -// This file implements the __cxa_guard_* functions as defined at: -// http://www.codesourcery.com/public/cxx-abi/abi.html -// -// The goal of these functions is to support thread-safe, one-time -// initialization of function scope variables. The compiler will generate -// code like the following: -// -// if ( obj_guard.first_byte == 0 ) { -// if ( __cxa_guard_acquire(&obj_guard) ) { -// try { -// ... initialize the object ...; -// } -// catch (...) { -// __cxa_guard_abort(&obj_guard); -// throw; -// } -// ... queue object destructor with __cxa_atexit() ...; -// __cxa_guard_release(&obj_guard); -// } -// } -// -// Notes: -// ojb_guard is a 64-bytes in size and statically initialized to zero. -// -// Section 6.7 of the C++ Spec says "If control re-enters the declaration -// recursively while the object is being initialized, the behavior is -// undefined". This implementation calls abort(). -// - -// Note don't use function local statics to avoid use of cxa functions... -static pthread_mutex_t __guard_mutex; -static pthread_once_t __once_control = PTHREAD_ONCE_INIT; - -static void makeRecusiveMutex() { - pthread_mutexattr_t recursiveMutexAttr; - pthread_mutexattr_init(&recursiveMutexAttr); - pthread_mutexattr_settype(&recursiveMutexAttr, PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init(&__guard_mutex, &recursiveMutexAttr); -} - -__attribute__((noinline)) static pthread_mutex_t* guard_mutex() { - pthread_once(&__once_control, &makeRecusiveMutex); - return &__guard_mutex; -} - -// helper functions for getting/setting flags in guard_object -static bool initializerHasRun(u64* guard_object) { - return (*((u8*)guard_object) != 0); -} - -static void setInitializerHasRun(u64* guard_object) { - *((u8*)guard_object) = 1; -} - -static bool inUse(u64* guard_object) { - return (((u8*)guard_object)[1] != 0); -} - -static void setInUse(u64* guard_object) { - ((u8*)guard_object)[1] = 1; -} - -static void setNotInUse(u64* guard_object) { - ((u8*)guard_object)[1] = 0; -} - -// -// Returns 1 if the caller needs to run the initializer and then either -// call __cxa_guard_release() or __cxa_guard_abort(). If zero is returned, -// then the initializer has already been run. This function blocks -// if another thread is currently running the initializer. This function -// aborts if called again on the same guard object without an intervening -// call to __cxa_guard_release() or __cxa_guard_abort(). -// -int PS4_SYSV_ABI ps4___cxa_guard_acquire(u64* guard_object) { - // Double check that the initializer has not already been run - if (initializerHasRun(guard_object)) - return 0; - - // We now need to acquire a lock that allows only one thread - // to run the initializer. If a different thread calls - // __cxa_guard_acquire() with the same guard object, we want - // that thread to block until this thread is done running the - // initializer and calls __cxa_guard_release(). But if the same - // thread calls __cxa_guard_acquire() with the same guard object, - // we want to abort. - // To implement this we have one global pthread recursive mutex - // shared by all guard objects, but only one at a time. - - int result = ::pthread_mutex_lock(guard_mutex()); - if (result != 0) { - LOG_ERROR(Lib_LibC, "pthread_mutex_lock failed with {}", result); - } - // At this point all other threads will block in __cxa_guard_acquire() - - // Check if another thread has completed initializer run - if (initializerHasRun(guard_object)) { - int result = ::pthread_mutex_unlock(guard_mutex()); - if (result != 0) { - LOG_ERROR(Lib_LibC, "pthread_mutex_lock failed with {}", result); - } - return 0; - } - - // The pthread mutex is recursive to allow other lazy initialized - // function locals to be evaluated during evaluation of this one. - // But if the same thread can call __cxa_guard_acquire() on the - // *same* guard object again, we call abort(); - if (inUse(guard_object)) { - LOG_ERROR(Lib_LibC, - "initializer for function local static variable called enclosing function"); - } - - // mark this guard object as being in use - setInUse(guard_object); - - // return non-zero to tell caller to run initializer - return 1; -} - -// -// Sets the first byte of the guard_object to a non-zero value. -// Releases any locks acquired by __cxa_guard_acquire(). -// -void PS4_SYSV_ABI ps4___cxa_guard_release(u64* guard_object) { - // first mark initalizer as having been run, so - // other threads won't try to re-run it. - setInitializerHasRun(guard_object); - - // release global mutex - int result = ::pthread_mutex_unlock(guard_mutex()); - if (result != 0) { - LOG_ERROR(Lib_LibC, "pthread_mutex_unlock failed with {}", result); - } -} - -// -// Releases any locks acquired by __cxa_guard_acquire(). -// -void PS4_SYSV_ABI ps4___cxa_guard_abort(u64* guard_object) { - int result = ::pthread_mutex_unlock(guard_mutex()); - if (result != 0) { - LOG_ERROR(Lib_LibC, "pthread_mutex_unlock failed with {}", result); - } - - // now reset state, so possible to try to initialize again - setNotInUse(guard_object); -} - -} // namespace Libraries::LibC diff --git a/src/core/libraries/libc/libc_cxa.h b/src/core/libraries/libc/libc_cxa.h deleted file mode 100644 index 2594493eb..000000000 --- a/src/core/libraries/libc/libc_cxa.h +++ /dev/null @@ -1,15 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include "common/types.h" - -namespace Libraries::LibC { - -int PS4_SYSV_ABI ps4___cxa_guard_acquire(u64* guard_object); -void PS4_SYSV_ABI ps4___cxa_guard_release(u64* guard_object); -void PS4_SYSV_ABI ps4___cxa_guard_abort(u64* guard_object); - -} // namespace Libraries::LibC diff --git a/src/core/libraries/libc/libc_math.cpp b/src/core/libraries/libc/libc_math.cpp deleted file mode 100644 index 3b51c80e2..000000000 --- a/src/core/libraries/libc/libc_math.cpp +++ /dev/null @@ -1,55 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include "common/assert.h" -#include "core/libraries/libc/libc_math.h" - -namespace Libraries::LibC { - -float PS4_SYSV_ABI ps4_atan2f(float y, float x) { - return atan2f(y, x); -} - -float PS4_SYSV_ABI ps4_acosf(float num) { - return acosf(num); -} - -float PS4_SYSV_ABI ps4_tanf(float num) { - return tanf(num); -} - -float PS4_SYSV_ABI ps4_asinf(float num) { - return asinf(num); -} - -double PS4_SYSV_ABI ps4_pow(double base, double exponent) { - return pow(base, exponent); -} - -float PS4_SYSV_ABI ps4_powf(float x, float y) { - return powf(x, y); -} - -float PS4_SYSV_ABI ps4_roundf(float arg) { - return roundf(arg); -} - -double PS4_SYSV_ABI ps4__Sin(double x) { - return sin(x); -} - -float PS4_SYSV_ABI ps4__Fsin(float arg, unsigned int m, int n) { - ASSERT(n == 0); - if (m != 0) { - return cosf(arg); - } else { - return sinf(arg); - } -} - -double PS4_SYSV_ABI ps4_exp2(double arg) { - return exp2(arg); -} - -} // namespace Libraries::LibC diff --git a/src/core/libraries/libc/libc_math.h b/src/core/libraries/libc/libc_math.h deleted file mode 100644 index f73a33f14..000000000 --- a/src/core/libraries/libc/libc_math.h +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "common/types.h" - -namespace Libraries::LibC { - -float PS4_SYSV_ABI ps4_atan2f(float y, float x); -float PS4_SYSV_ABI ps4_acosf(float num); -float PS4_SYSV_ABI ps4_tanf(float num); -float PS4_SYSV_ABI ps4_asinf(float num); -double PS4_SYSV_ABI ps4_pow(double base, double exponent); -double PS4_SYSV_ABI ps4__Sin(double x); -float PS4_SYSV_ABI ps4__Fsin(float arg, unsigned int, int); -double PS4_SYSV_ABI ps4_exp2(double arg); -float PS4_SYSV_ABI ps4_powf(float x, float y); -float PS4_SYSV_ABI ps4_roundf(float arg); - -} // namespace Libraries::LibC diff --git a/src/core/libraries/libc/libc_stdio.cpp b/src/core/libraries/libc/libc_stdio.cpp deleted file mode 100644 index 2b15bd369..000000000 --- a/src/core/libraries/libc/libc_stdio.cpp +++ /dev/null @@ -1,78 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/logging/log.h" -#include "common/singleton.h" -#include "core/file_sys/fs.h" -#include "core/libraries/libc/libc_stdio.h" - -namespace Libraries::LibC { - -std::FILE* PS4_SYSV_ABI ps4_fopen(const char* filename, const char* mode) { - auto* mnt = Common::Singleton::Instance(); - const auto host_path = mnt->GetHostPath(filename).string(); - FILE* f = std::fopen(host_path.c_str(), mode); - if (f != nullptr) { - LOG_INFO(Lib_LibC, "fopen = {}", host_path); - } else { - LOG_INFO(Lib_LibC, "fopen can't open = {}", host_path); - } - return f; -} - -int PS4_SYSV_ABI ps4_fclose(FILE* stream) { - LOG_INFO(Lib_LibC, "callled"); - int ret = 0; - if (stream != nullptr) { - ret = fclose(stream); - } - return ret; -} - -int PS4_SYSV_ABI ps4_setvbuf(FILE* stream, char* buf, int mode, size_t size) { - return setvbuf(stream, buf, mode, size); -} - -int PS4_SYSV_ABI ps4_fseek(FILE* stream, long offset, int whence) { - return fseek(stream, offset, whence); -} - -int PS4_SYSV_ABI ps4_fgetpos(FILE* stream, fpos_t* pos) { - return fgetpos(stream, pos); -} - -std::size_t PS4_SYSV_ABI ps4_fread(void* ptr, size_t size, size_t nmemb, FILE* stream) { - return fread(ptr, size, nmemb, stream); -} - -int PS4_SYSV_ABI ps4_printf(VA_ARGS) { - VA_CTX(ctx); - return printf_ctx(&ctx); -} - -int PS4_SYSV_ABI ps4_fprintf(FILE* file, VA_ARGS) { - int fd = fileno(file); - if (fd == 1 || fd == 2) { // output stdout and stderr to console - VA_CTX(ctx); - return printf_ctx(&ctx); - } else { - VA_CTX(ctx); - char buf[256]; - fprintf_ctx(&ctx, buf); - return fprintf(file, "%s", buf); - } -} - -int PS4_SYSV_ABI ps4_vsnprintf(char* s, size_t n, const char* format, VaList* arg) { - return vsnprintf_ctx(s, n, format, arg); -} - -int PS4_SYSV_ABI ps4_puts(const char* s) { - return std::puts(s); -} - -long PS4_SYSV_ABI ps4_ftell(FILE* stream) { - return ftell(stream); -} - -} // namespace Libraries::LibC diff --git a/src/core/libraries/libc/libc_stdio.h b/src/core/libraries/libc/libc_stdio.h deleted file mode 100644 index 806c17c28..000000000 --- a/src/core/libraries/libc/libc_stdio.h +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "common/types.h" -#include "core/libraries/libc/printf.h" - -namespace Libraries::LibC { - -std::FILE* PS4_SYSV_ABI ps4_fopen(const char* filename, const char* mode); -int PS4_SYSV_ABI ps4_printf(VA_ARGS); -int PS4_SYSV_ABI ps4_vsnprintf(char* s, size_t n, const char* format, VaList* arg); -int PS4_SYSV_ABI ps4_puts(const char* s); -int PS4_SYSV_ABI ps4_fprintf(FILE* file, VA_ARGS); -int PS4_SYSV_ABI ps4_setvbuf(FILE* stream, char* buf, int mode, size_t size); -int PS4_SYSV_ABI ps4_fclose(FILE* stream); -int PS4_SYSV_ABI ps4_fseek(FILE* stream, long offset, int whence); -int PS4_SYSV_ABI ps4_fgetpos(FILE* stream, fpos_t* pos); -std::size_t PS4_SYSV_ABI ps4_fread(void* ptr, size_t size, size_t nmemb, FILE* stream); -long PS4_SYSV_ABI ps4_ftell(FILE* stream); - -} // namespace Libraries::LibC diff --git a/src/core/libraries/libc/libc_stdlib.cpp b/src/core/libraries/libc/libc_stdlib.cpp deleted file mode 100644 index e71838780..000000000 --- a/src/core/libraries/libc/libc_stdlib.cpp +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include "common/assert.h" -#include "core/libraries/libc/libc_stdlib.h" - -namespace Libraries::LibC { - -void PS4_SYSV_ABI ps4_exit(int code) { - std::exit(code); -} - -int PS4_SYSV_ABI ps4_atexit(void (*func)()) { - int rt = std::atexit(func); - ASSERT_MSG(rt == 0, "atexit returned {}", rt); - return rt; -} - -void* PS4_SYSV_ABI ps4_malloc(size_t size) { - return std::malloc(size); -} - -void PS4_SYSV_ABI ps4_free(void* ptr) { - std::free(ptr); -} - -typedef int(PS4_SYSV_ABI* pfunc_QsortCmp)(const void*, const void*); -thread_local static pfunc_QsortCmp compair_ps4; - -int qsort_compair(const void* arg1, const void* arg2) { - return compair_ps4(arg1, arg2); -} - -void PS4_SYSV_ABI ps4_qsort(void* ptr, size_t count, size_t size, - int(PS4_SYSV_ABI* comp)(const void*, const void*)) { - compair_ps4 = comp; - std::qsort(ptr, count, size, qsort_compair); -} - -int PS4_SYSV_ABI ps4_rand() { - return std::rand(); -} - -} // namespace Libraries::LibC diff --git a/src/core/libraries/libc/libc_stdlib.h b/src/core/libraries/libc/libc_stdlib.h deleted file mode 100644 index 4bb5f6c13..000000000 --- a/src/core/libraries/libc/libc_stdlib.h +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include "common/types.h" - -namespace Libraries::LibC { - -void PS4_SYSV_ABI ps4_exit(int code); -int PS4_SYSV_ABI ps4_atexit(void (*func)()); -void* PS4_SYSV_ABI ps4_malloc(size_t size); -void PS4_SYSV_ABI ps4_free(void* ptr); -void PS4_SYSV_ABI ps4_qsort(void* ptr, size_t count, size_t size, - int(PS4_SYSV_ABI* comp)(const void*, const void*)); -int PS4_SYSV_ABI ps4_rand(); - -} // namespace Libraries::LibC diff --git a/src/core/libraries/libc/libc_string.cpp b/src/core/libraries/libc/libc_string.cpp deleted file mode 100644 index af7524e6e..000000000 --- a/src/core/libraries/libc/libc_string.cpp +++ /dev/null @@ -1,61 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include -#include "core/libraries/libc/libc_string.h" - -namespace Libraries::LibC { - -int PS4_SYSV_ABI ps4_memcmp(const void* s1, const void* s2, size_t n) { - return std::memcmp(s1, s2, n); -} - -void* PS4_SYSV_ABI ps4_memcpy(void* dest, const void* src, size_t n) { - return std::memcpy(dest, src, n); -} - -void* PS4_SYSV_ABI ps4_memset(void* s, int c, size_t n) { - return std::memset(s, c, n); -} - -int PS4_SYSV_ABI ps4_strcmp(const char* str1, const char* str2) { - return std::strcmp(str1, str2); -} - -char* PS4_SYSV_ABI ps4_strncpy(char* dest, const char* src, size_t count) { - return std::strncpy(dest, src, count); -} - -void* PS4_SYSV_ABI ps4_memmove(void* dest, const void* src, std::size_t count) { - return std::memmove(dest, src, count); -} - -char* PS4_SYSV_ABI ps4_strcpy(char* dest, const char* src) { - return std::strcpy(dest, src); -} - -char* PS4_SYSV_ABI ps4_strcat(char* dest, const char* src) { - return std::strcat(dest, src); -} - -size_t PS4_SYSV_ABI ps4_strlen(const char* str) { - return std::strlen(str); -} - -char* PS4_SYSV_ABI ps4_strrchr(const char* s, int c) { - return (char*)strrchr(s, c); -} - -int PS4_SYSV_ABI ps4_strncmp(const char* s1, const char* s2, size_t n) { - return strncmp(s1, s2, n); -} - -char* PS4_SYSV_ABI ps4_strdup(const char* str1) { - char* dup = (char*)std::malloc(std::strlen(str1) + 1); - if (dup != NULL) - strcpy(dup, str1); - return dup; -} - -} // namespace Libraries::LibC diff --git a/src/core/libraries/libc/libc_string.h b/src/core/libraries/libc/libc_string.h deleted file mode 100644 index 3e575fa30..000000000 --- a/src/core/libraries/libc/libc_string.h +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include "common/types.h" - -namespace Libraries::LibC { - -int PS4_SYSV_ABI ps4_memcmp(const void* s1, const void* s2, size_t n); -void* PS4_SYSV_ABI ps4_memcpy(void* dest, const void* src, size_t n); -void* PS4_SYSV_ABI ps4_memset(void* s, int c, size_t n); -int PS4_SYSV_ABI ps4_strcmp(const char* str1, const char* str2); -char* PS4_SYSV_ABI ps4_strncpy(char* dest, const char* src, size_t count); -void* PS4_SYSV_ABI ps4_memmove(void* dest, const void* src, std::size_t count); -char* PS4_SYSV_ABI ps4_strcpy(char* destination, const char* source); -char* PS4_SYSV_ABI ps4_strcat(char* dest, const char* src); -size_t PS4_SYSV_ABI ps4_strlen(const char* str); -char* PS4_SYSV_ABI ps4_strrchr(const char* s, int c); -int PS4_SYSV_ABI ps4_strncmp(const char* s1, const char* s2, size_t n); -char* PS4_SYSV_ABI ps4_strdup(const char* str1); - -} // namespace Libraries::LibC diff --git a/src/core/libraries/libc/printf.h b/src/core/libraries/libc/printf.h deleted file mode 100644 index e84654274..000000000 --- a/src/core/libraries/libc/printf.h +++ /dev/null @@ -1,753 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2014-2018 Marco Paland (info@paland.com) -// SPDX-License-Identifier: MIT - -/////////////////////////////////////////////////////////////////////////////// -// \author (c) Marco Paland (info@paland.com) -// 2014-2018, PALANDesign Hannover, Germany -// -// \license The MIT License (MIT) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// -// \brief Tiny printf, sprintf and snprintf implementation, optimized for speed on -// embedded systems with a very limited resources. -// Use this instead of bloated standard/newlib printf. -// These routines are thread safe and reentrant! -// -/////////////////////////////////////////////////////////////////////////////// -// Vita3K emulator project -// Copyright (C) 2023 Vita3K team -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License along -// with this program; if not, write to the Free Software Foundation, Inc., -// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -// copied from Vita3k project at 6/10/2023 (latest update 30/06/2023) -// modifications for adapting va_args parameters - -#pragma once - -#include - -#include -#include -#include -#include -#include -#include - -#include "va_ctx.h" - -namespace Libraries::LibC { -// ntoa conversion buffer size, this must be big enough to hold -// one converted numeric number including padded zeros (dynamically created on stack) -// 32 byte is a good default -#define PRINTF_NTOA_BUFFER_SIZE 32U - -// ftoa conversion buffer size, this must be big enough to hold -// one converted float number including padded zeros (dynamically created on stack) -// 32 byte is a good default -#define PRINTF_FTOA_BUFFER_SIZE 32U - -// define this to support floating point (%f) -#define PRINTF_SUPPORT_FLOAT - -// define this to support long long types (%llu or %p) -#define PRINTF_SUPPORT_LONG_LONG - -// define this to support the ptrdiff_t type (%t) -// ptrdiff_t is normally defined in as long or long long type -#define PRINTF_SUPPORT_PTRDIFF_T - -/////////////////////////////////////////////////////////////////////////////// - -// internal flag definitions -#define FLAGS_ZEROPAD (1U << 0U) -#define FLAGS_LEFT (1U << 1U) -#define FLAGS_PLUS (1U << 2U) -#define FLAGS_SPACE (1U << 3U) -#define FLAGS_HASH (1U << 4U) -#define FLAGS_UPPERCASE (1U << 5U) -#define FLAGS_CHAR (1U << 6U) -#define FLAGS_SHORT (1U << 7U) -#define FLAGS_LONG (1U << 8U) -#define FLAGS_LONG_LONG (1U << 9U) -#define FLAGS_PRECISION (1U << 10U) -#define FLAGS_WIDTH (1U << 11U) - -// output function type -typedef void (*out_fct_type)(char character, void* buffer, size_t idx, size_t maxlen); - -// wrapper (used as buffer) for output function type -typedef struct { - void (*fct)(char character, void* arg); - void* arg; -} out_fct_wrap_type; - -// internal buffer output -static inline void _out_buffer(char character, void* buffer, size_t idx, size_t maxlen) { - if (idx < maxlen) { - ((char*)buffer)[idx] = character; - } -} - -// internal null output -static inline void _out_null(char character, void* buffer, size_t idx, size_t maxlen) { - (void)character; - (void)buffer; - (void)idx; - (void)maxlen; -} - -// internal output function wrapper -static inline void _out_fct(char character, void* buffer, size_t idx, size_t maxlen) { - (void)idx; - (void)maxlen; - // buffer is the output fct pointer - ((out_fct_wrap_type*)buffer)->fct(character, ((out_fct_wrap_type*)buffer)->arg); -} - -// internal strlen -// \return The length of the string (excluding the terminating 0) -static inline unsigned int _strlen(const char* str) { - const char* s; - for (s = str; *s; ++s) - ; - return (unsigned int)(s - str); -} - -// internal test if char is a digit (0-9) -// \return true if char is a digit -static inline bool _is_digit(char ch) { - return (ch >= '0') && (ch <= '9'); -} - -// internal ASCII string to unsigned int conversion -static inline unsigned int _atoi(const char** str) { - unsigned int i = 0U; - while (_is_digit(**str)) { - i = i * 10U + (unsigned int)(*((*str)++) - '0'); - } - return i; -} - -// internal itoa format -static inline size_t _ntoa_format(out_fct_type out, char* buffer, size_t idx, size_t maxlen, - char* buf, size_t len, bool negative, unsigned int base, - unsigned int prec, unsigned int width, unsigned int flags) { - const size_t start_idx = idx; - - // pad leading zeros - while (!(flags & FLAGS_LEFT) && (len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) { - buf[len++] = '0'; - } - while (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD) && (len < width) && - (len < PRINTF_NTOA_BUFFER_SIZE)) { - buf[len++] = '0'; - } - - // handle hash - if (flags & FLAGS_HASH) { - if (((len == prec) || (len == width)) && (len > 0U)) { - len--; - if ((base == 16U) && (len > 0U)) { - len--; - } - } - if ((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) { - buf[len++] = 'x'; - } - if ((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) { - buf[len++] = 'X'; - } - if (len < PRINTF_NTOA_BUFFER_SIZE) { - buf[len++] = '0'; - } - } - - // handle sign - if ((len == width) && (negative || (flags & FLAGS_PLUS) || (flags & FLAGS_SPACE))) { - len--; - } - if (len < PRINTF_NTOA_BUFFER_SIZE) { - if (negative) { - buf[len++] = '-'; - } else if (flags & FLAGS_PLUS) { - buf[len++] = '+'; // ignore the space if the '+' exists - } else if (flags & FLAGS_SPACE) { - buf[len++] = ' '; - } - } - - // pad spaces up to given width - if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) { - for (size_t i = len; i < width; i++) { - out(' ', buffer, idx++, maxlen); - } - } - - // reverse string - for (size_t i = 0U; i < len; i++) { - out(buf[len - i - 1U], buffer, idx++, maxlen); - } - - // append pad spaces up to given width - if (flags & FLAGS_LEFT) { - while (idx - start_idx < width) { - out(' ', buffer, idx++, maxlen); - } - } - - return idx; -} - -// internal itoa for 'long' type -static inline size_t _ntoa_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, - unsigned long value, bool negative, unsigned long base, - unsigned int prec, unsigned int width, unsigned int flags) { - char buf[PRINTF_NTOA_BUFFER_SIZE]; - size_t len = 0U; - - // write if precision != 0 and value is != 0 - if (!(flags & FLAGS_PRECISION) || value) { - do { - const char digit = (char)(value % base); - buf[len++] = - digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; - value /= base; - } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); - } - - return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, - width, flags); -} - -// internal itoa for 'long long' type -#if defined(PRINTF_SUPPORT_LONG_LONG) -static inline size_t _ntoa_long_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, - unsigned long long value, bool negative, - unsigned long long base, unsigned int prec, unsigned int width, - unsigned int flags) { - char buf[PRINTF_NTOA_BUFFER_SIZE]; - size_t len = 0U; - - // write if precision != 0 and value is != 0 - if (!(flags & FLAGS_PRECISION) || value) { - do { - const char digit = (char)(value % base); - buf[len++] = - digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; - value /= base; - } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); - } - - return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, - width, flags); -} -#endif // PRINTF_SUPPORT_LONG_LONG - -#if defined(PRINTF_SUPPORT_FLOAT) -static inline size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, - unsigned int prec, unsigned int width, unsigned int flags) { - char buf[PRINTF_FTOA_BUFFER_SIZE]; - size_t len = 0U; - double diff = 0.0; - - // if input is larger than thres_max, revert to exponential - const double thres_max = (double)0x7FFFFFFF; - - // powers of 10 - static const double pow10[] = {1, 10, 100, 1000, 10000, - 100000, 1000000, 10000000, 100000000, 1000000000}; - - // test for negative - bool negative = false; - if (value < 0) { - negative = true; - value = 0 - value; - } - - // set default precision to 6, if not set explicitly - if (!(flags & FLAGS_PRECISION)) { - prec = 6U; - } - // limit precision to 9, cause a prec >= 10 can lead to overflow errors - while ((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) { - buf[len++] = '0'; - prec--; - } - - int whole = (int)value; - double tmp = (value - whole) * pow10[prec]; - unsigned long frac = (unsigned long)tmp; - diff = tmp - frac; - - if (diff > 0.5) { - ++frac; - // handle rollover, e.g. case 0.99 with prec 1 is 1.0 - if (frac >= pow10[prec]) { - frac = 0; - ++whole; - } - } else if ((diff == 0.5) && ((frac == 0U) || (frac & 1U))) { - // if halfway, round up if odd, OR if last digit is 0 - ++frac; - } - - // TBD: for very large numbers switch back to native sprintf for exponentials. Anyone want to - // write code to replace this? Normal printf behavior is to print EVERY whole number digit which - // can be 100s of characters overflowing your buffers == bad - if (value > thres_max) { - return 0U; - } - - if (prec == 0U) { - diff = value - (double)whole; - if (diff > 0.5) { - // greater than 0.5, round up, e.g. 1.6 -> 2 - ++whole; - } else if ((diff == 0.5) && (whole & 1)) { - // exactly 0.5 and ODD, then round up - // 1.5 -> 2, but 2.5 -> 2 - ++whole; - } - } else { - unsigned int count = prec; - // now do fractional part, as an unsigned number - while (len < PRINTF_FTOA_BUFFER_SIZE) { - --count; - buf[len++] = (char)(48U + (frac % 10U)); - if (!(frac /= 10U)) { - break; - } - } - // add extra 0s - while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) { - buf[len++] = '0'; - } - if (len < PRINTF_FTOA_BUFFER_SIZE) { - // add decimal - buf[len++] = '.'; - } - } - - // do whole part, number is reversed - while (len < PRINTF_FTOA_BUFFER_SIZE) { - buf[len++] = (char)(48 + (whole % 10)); - if (!(whole /= 10)) { - break; - } - } - - // pad leading zeros - while (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD) && (len < width) && - (len < PRINTF_FTOA_BUFFER_SIZE)) { - buf[len++] = '0'; - } - - // handle sign - if ((len == width) && (negative || (flags & FLAGS_PLUS) || (flags & FLAGS_SPACE))) { - len--; - } - if (len < PRINTF_FTOA_BUFFER_SIZE) { - if (negative) { - buf[len++] = '-'; - } else if (flags & FLAGS_PLUS) { - buf[len++] = '+'; // ignore the space if the '+' exists - } else if (flags & FLAGS_SPACE) { - buf[len++] = ' '; - } - } - - // pad spaces up to given width - if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) { - for (size_t i = len; i < width; i++) { - out(' ', buffer, idx++, maxlen); - } - } - - // reverse string - for (size_t i = 0U; i < len; i++) { - out(buf[len - i - 1U], buffer, idx++, maxlen); - } - - // append pad spaces up to given width - if (flags & FLAGS_LEFT) { - while (idx < width) { - out(' ', buffer, idx++, maxlen); - } - } - - return idx; -} -#endif // PRINTF_SUPPORT_FLOAT - -// internal vsnprintf -static inline int _vsnprintf(out_fct_type out, char* buffer, const char* format, VaList* va_list) { - unsigned int flags, width, precision, n; - size_t idx = 0U; - auto maxlen = static_cast(-1); - - if (!buffer) { - // use null output function - out = _out_null; - } - - while (*format) { - // format specifier? %[flags][width][.precision][length] - if (*format != '%') { - // no - out(*format, buffer, idx++, maxlen); - format++; - continue; - } else { - // yes, evaluate it - format++; - } - - // evaluate flags - flags = 0U; - do { - switch (*format) { - case '0': - flags |= FLAGS_ZEROPAD; - format++; - n = 1U; - break; - case '-': - flags |= FLAGS_LEFT; - format++; - n = 1U; - break; - case '+': - flags |= FLAGS_PLUS; - format++; - n = 1U; - break; - case ' ': - flags |= FLAGS_SPACE; - format++; - n = 1U; - break; - case '#': - flags |= FLAGS_HASH; - format++; - n = 1U; - break; - default: - n = 0U; - break; - } - } while (n); - - // evaluate width field - width = 0U; - if (_is_digit(*format)) { - width = _atoi(&format); - } else if (*format == '*') { - const int w = vaArgInteger(va_list); // const int w = va.next(cpu, mem); - - if (w < 0) { - flags |= FLAGS_LEFT; // reverse padding - width = (unsigned int)-w; - } else { - width = (unsigned int)w; - } - format++; - } - - // evaluate precision field - precision = 0U; - if (*format == '.') { - flags |= FLAGS_PRECISION; - format++; - if (_is_digit(*format)) { - precision = _atoi(&format); - } else if (*format == '*') { - precision = - vaArgInteger(va_list); // precision = (unsigned int)va.next(cpu, mem); - format++; - } - } - - // evaluate length field - switch (*format) { - case 'l': - flags |= FLAGS_LONG; - format++; - if (*format == 'l') { - flags |= FLAGS_LONG_LONG; - format++; - } - break; - case 'h': - flags |= FLAGS_SHORT; - format++; - if (*format == 'h') { - flags |= FLAGS_CHAR; - format++; - } - break; -#if defined(PRINTF_SUPPORT_PTRDIFF_T) - case 't': - flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); - format++; - break; -#endif - case 'j': - flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); - format++; - break; - case 'z': - flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); - format++; - break; - default: - break; - } - - // evaluate specifier - switch (*format) { - case 'd': - case 'i': - case 'u': - case 'x': - case 'X': - case 'o': - case 'b': { - // set the base - unsigned int base; - if (*format == 'x' || *format == 'X') { - base = 16U; - } else if (*format == 'o') { - base = 8U; - } else if (*format == 'b') { - base = 2U; - flags &= ~FLAGS_HASH; // no hash for bin format - } else { - base = 10U; - flags &= ~FLAGS_HASH; // no hash for dec format - } - // uppercase - if (*format == 'X') { - flags |= FLAGS_UPPERCASE; - } - - // no plus or space flag for u, x, X, o, b - if ((*format != 'i') && (*format != 'd')) { - flags &= ~(FLAGS_PLUS | FLAGS_SPACE); - } - - // convert the integer - if ((*format == 'i') || (*format == 'd')) { - // signed - if (flags & FLAGS_LONG_LONG) { -#if defined(PRINTF_SUPPORT_LONG_LONG) - auto value = vaArgLongLong( - va_list); // const long long value = va.next(cpu, mem); - idx = _ntoa_long_long(out, buffer, idx, maxlen, - (unsigned long long)(value > 0 ? value : 0 - value), - value < 0, base, precision, width, flags); -#endif - } else if (flags & FLAGS_LONG) { - auto value = vaArgLong(va_list); // const long value = va.next(cpu, mem); - idx = _ntoa_long(out, buffer, idx, maxlen, - (unsigned long)(value > 0 ? value : 0 - value), value < 0, - base, precision, width, flags); - } else { - // const int value = (flags & FLAGS_CHAR) ? (char)va.next(cpu, mem) : - // (flags & FLAGS_SHORT) ? (short int)va.next(cpu, mem): va.next(cpu, - // mem); - const int value = - (flags & FLAGS_CHAR) ? static_cast(vaArgInteger(va_list)) - : (flags & FLAGS_SHORT) ? static_cast(vaArgInteger(va_list)) - : vaArgInteger(va_list); - idx = _ntoa_long(out, buffer, idx, maxlen, - (unsigned int)(value > 0 ? value : 0 - value), value < 0, base, - precision, width, flags); - } - } else { - // unsigned - if (flags & FLAGS_LONG_LONG) { -#if defined(PRINTF_SUPPORT_LONG_LONG) - // idx = _ntoa_long_long(out, buffer, idx, maxlen, va.next(cpu, mem), false, base, precision, width, flags); - idx = _ntoa_long_long(out, buffer, idx, maxlen, - static_cast(vaArgLongLong(va_list)), false, base, - precision, width, flags); -#endif - } else if (flags & FLAGS_LONG) { - // idx = _ntoa_long(out, buffer, idx, maxlen, va.next(cpu, mem), - // false, base, precision, width, flags); - idx = _ntoa_long(out, buffer, idx, maxlen, static_cast(vaArgLong(va_list)), - false, base, precision, width, flags); - } else { - // const unsigned int value = (flags & FLAGS_CHAR) ? (unsigned - // char)va.next(cpu, mem) : (flags & FLAGS_SHORT) ? - // (unsigned short int)va.next(cpu, mem) : va.next(cpu, mem); - const unsigned int value = - (flags & FLAGS_CHAR) ? static_cast(vaArgInteger(va_list)) - : (flags & FLAGS_SHORT) ? static_cast(vaArgInteger(va_list)) - : static_cast(vaArgInteger(va_list)); - idx = _ntoa_long(out, buffer, idx, maxlen, value, false, base, precision, width, - flags); - } - } - format++; - break; - } -#if defined(PRINTF_SUPPORT_FLOAT) - case 'f': - case 'F': - // idx = _ftoa(out, buffer, idx, maxlen, va.next(cpu, mem), precision, width, - // flags); - idx = _ftoa(out, buffer, idx, maxlen, vaArgDouble(va_list), precision, width, flags); - format++; - break; -#endif // PRINTF_SUPPORT_FLOAT - case 'c': { - unsigned int l = 1U; - // pre padding - if (!(flags & FLAGS_LEFT)) { - while (l++ < width) { - out(' ', buffer, idx++, maxlen); - } - } - // char output - // out((char)va.next(cpu, mem), buffer, idx++, maxlen); - out(static_cast(vaArgInteger(va_list)), buffer, idx++, maxlen); - // post padding - if (flags & FLAGS_LEFT) { - while (l++ < width) { - out(' ', buffer, idx++, maxlen); - } - } - format++; - break; - } - - case 's': { - const char* p = vaArgPtr( - va_list); // const char *p = va.next>(cpu, mem).get(mem); - p = p != nullptr ? p : "(null)"; - unsigned int l = _strlen(p); - // pre padding - if (flags & FLAGS_PRECISION) { - l = (l < precision ? l : precision); - } - if (!(flags & FLAGS_LEFT)) { - while (l++ < width) { - out(' ', buffer, idx++, maxlen); - } - } - // string output - while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) { - out(*(p++), buffer, idx++, maxlen); - } - // post padding - if (flags & FLAGS_LEFT) { - while (l++ < width) { - out(' ', buffer, idx++, maxlen); - } - } - format++; - break; - } - - case 'p': { - width = sizeof(void*) * 2U; - flags |= FLAGS_ZEROPAD | FLAGS_UPPERCASE; -#if defined(PRINTF_SUPPORT_LONG_LONG) - const bool is_ll = sizeof(uintptr_t) == sizeof(long long); - if (is_ll) { - // idx = _ntoa_long_long(out, buffer, idx, maxlen, - // (uintptr_t)va.next>(cpu, mem).address(), false, 16U, precision, width, - // flags); - idx = _ntoa_long_long(out, buffer, idx, maxlen, - reinterpret_cast(vaArgPtr(va_list)), false, - 16U, precision, width, flags); - } else { -#endif - // idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned - // long)((uintptr_t)va.next>(cpu, mem).address()), false, 16U, precision, - // width, flags); - idx = _ntoa_long( - out, buffer, idx, maxlen, - static_cast(reinterpret_cast(vaArgPtr(va_list))), - false, 16U, precision, width, flags); -#if defined(PRINTF_SUPPORT_LONG_LONG) - } -#endif - format++; - break; - } - - case '%': - out('%', buffer, idx++, maxlen); - format++; - break; - - default: - out(*format, buffer, idx++, maxlen); - format++; - break; - } - } - - // termination - out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen); - - // return written chars without terminating \0 - return (int)idx; -} - -static int printf_ctx(VaCtx* ctx) { - const char* format = vaArgPtr(&ctx->va_list); - char buffer[256]; - int result = _vsnprintf(_out_buffer, buffer, format, &ctx->va_list); - printf("%s", buffer); - return result; -} - -static int fprintf_ctx(VaCtx* ctx, char* buf) { - const char* format = vaArgPtr(&ctx->va_list); - char buffer[256]; - int result = _vsnprintf(_out_buffer, buffer, format, &ctx->va_list); - std::strcpy(buf, buffer); - return result; -} - -static int vsnprintf_ctx(char* s, size_t n, const char* format, VaList* arg) { - char buffer[n]; - int result = _vsnprintf(_out_buffer, buffer, format, arg); - std::strcpy(s, buffer); - return result; -} -} // namespace Libraries::LibC diff --git a/src/core/libraries/libc/va_ctx.h b/src/core/libraries/libc/va_ctx.h deleted file mode 100644 index 1c0314959..000000000 --- a/src/core/libraries/libc/va_ctx.h +++ /dev/null @@ -1,110 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include "common/types.h" - -#define VA_ARGS \ - uint64_t rdi, uint64_t rsi, uint64_t rdx, uint64_t rcx, uint64_t r8, uint64_t r9, \ - uint64_t overflow_arg_area, __m128 xmm0, __m128 xmm1, __m128 xmm2, __m128 xmm3, \ - __m128 xmm4, __m128 xmm5, __m128 xmm6, __m128 xmm7, ... - -#define VA_CTX(ctx) \ - alignas(16) VaCtx ctx; \ - (ctx).reg_save_area.gp[0] = rdi; \ - (ctx).reg_save_area.gp[1] = rsi; \ - (ctx).reg_save_area.gp[2] = rdx; \ - (ctx).reg_save_area.gp[3] = rcx; \ - (ctx).reg_save_area.gp[4] = r8; \ - (ctx).reg_save_area.gp[5] = r9; \ - (ctx).reg_save_area.fp[0] = xmm0; \ - (ctx).reg_save_area.fp[1] = xmm1; \ - (ctx).reg_save_area.fp[2] = xmm2; \ - (ctx).reg_save_area.fp[3] = xmm3; \ - (ctx).reg_save_area.fp[4] = xmm4; \ - (ctx).reg_save_area.fp[5] = xmm5; \ - (ctx).reg_save_area.fp[6] = xmm6; \ - (ctx).reg_save_area.fp[7] = xmm7; \ - (ctx).va_list.reg_save_area = &(ctx).reg_save_area; \ - (ctx).va_list.gp_offset = offsetof(VaRegSave, gp); \ - (ctx).va_list.fp_offset = offsetof(VaRegSave, fp); \ - (ctx).va_list.overflow_arg_area = &overflow_arg_area; - -namespace Libraries::LibC { - -// https://stackoverflow.com/questions/4958384/what-is-the-format-of-the-x86-64-va-list-structure - -struct VaList { - u32 gp_offset; - u32 fp_offset; - void* overflow_arg_area; - void* reg_save_area; -}; - -struct VaRegSave { - u64 gp[6]; - __m128 fp[8]; -}; - -struct VaCtx { - VaRegSave reg_save_area; - VaList va_list; -}; - -template -T vaArgRegSaveAreaGp(VaList* l) { - auto* addr = reinterpret_cast(static_cast(l->reg_save_area) + l->gp_offset); - l->gp_offset += Size; - return *addr; -} -template -T vaArgOverflowArgArea(VaList* l) { - auto ptr = ((reinterpret_cast(l->overflow_arg_area) + (Align - 1)) & ~(Align - 1)); - auto* addr = reinterpret_cast(ptr); - l->overflow_arg_area = reinterpret_cast(ptr + Size); - return *addr; -} - -template -T vaArgRegSaveAreaFp(VaList* l) { - auto* addr = reinterpret_cast(static_cast(l->reg_save_area) + l->fp_offset); - l->fp_offset += Size; - return *addr; -} - -inline int vaArgInteger(VaList* l) { - if (l->gp_offset <= 40) { - return vaArgRegSaveAreaGp(l); - } - return vaArgOverflowArgArea(l); -} - -inline long long vaArgLongLong(VaList* l) { - if (l->gp_offset <= 40) { - return vaArgRegSaveAreaGp(l); - } - return vaArgOverflowArgArea(l); -} -inline long vaArgLong(VaList* l) { - if (l->gp_offset <= 40) { - return vaArgRegSaveAreaGp(l); - } - return vaArgOverflowArgArea(l); -} - -inline double vaArgDouble(VaList* l) { - if (l->fp_offset <= 160) { - return vaArgRegSaveAreaFp(l); - } - return vaArgOverflowArgArea(l); -} - -template -T* vaArgPtr(VaList* l) { - if (l->gp_offset <= 40) { - return vaArgRegSaveAreaGp(l); - } - return vaArgOverflowArgArea(l); -} - -} // namespace Libraries::LibC diff --git a/src/core/libraries/libc_internal/libc_internal.cpp b/src/core/libraries/libc_internal/libc_internal.cpp index 0607e93b7..8eea41eb3 100644 --- a/src/core/libraries/libc_internal/libc_internal.cpp +++ b/src/core/libraries/libc_internal/libc_internal.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include + #include "common/logging/log.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" diff --git a/src/core/libraries/libs.cpp b/src/core/libraries/libs.cpp index 20efd3c0e..e91a51e68 100644 --- a/src/core/libraries/libs.cpp +++ b/src/core/libraries/libs.cpp @@ -2,14 +2,18 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "common/config.h" +#include "core/libraries/ajm/ajm.h" #include "core/libraries/app_content/app_content.h" #include "core/libraries/audio/audioin.h" #include "core/libraries/audio/audioout.h" +#include "core/libraries/avplayer/avplayer.h" +#include "core/libraries/dialogs/error_dialog.h" +#include "core/libraries/dialogs/ime_dialog.h" #include "core/libraries/disc_map/disc_map.h" #include "core/libraries/gnmdriver/gnmdriver.h" #include "core/libraries/kernel/libkernel.h" -#include "core/libraries/libc/libc.h" #include "core/libraries/libc_internal/libc_internal.h" +#include "core/libraries/libpng/pngdec.h" #include "core/libraries/libs.h" #include "core/libraries/network/http.h" #include "core/libraries/network/net.h" @@ -33,11 +37,6 @@ #include "core/libraries/system/userservice.h" #include "core/libraries/usbd/usbd.h" #include "core/libraries/videoout/video_out.h" -#include "src/core/libraries/ajm/ajm.h" -#include "src/core/libraries/avplayer/avplayer.h" -#include "src/core/libraries/dialogs/error_dialog.h" -#include "src/core/libraries/dialogs/ime_dialog.h" -#include "src/core/libraries/libpng/pngdec.h" namespace Libraries { @@ -46,9 +45,6 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) { Libraries::Kernel::LibKernel_Register(sym); Libraries::GnmDriver::RegisterlibSceGnmDriver(sym); Libraries::VideoOut::RegisterLib(sym); - if (!Config::isLleLibc()) { - Libraries::LibC::libcSymbolsRegister(sym); - } // New libraries folder from autogen Libraries::UserService::RegisterlibSceUserService(sym); diff --git a/src/core/libraries/libs.h b/src/core/libraries/libs.h index 7cad7f8ba..ea928101e 100644 --- a/src/core/libraries/libs.h +++ b/src/core/libraries/libs.h @@ -4,6 +4,7 @@ #pragma once #include + #include "common/logging/log.h" #include "core/loader/elf.h" #include "core/loader/symbols_resolver.h" diff --git a/src/core/libraries/ngs2/ngs2_error.h b/src/core/libraries/ngs2/ngs2_error.h index 254ae26e6..2773168e2 100644 --- a/src/core/libraries/ngs2/ngs2_error.h +++ b/src/core/libraries/ngs2/ngs2_error.h @@ -3,54 +3,116 @@ #pragma once -constexpr int ORBIS_NGS2_ERROR_INVALID_PARAMETERS = 0x804A0001; -constexpr int ORBIS_NGS2_ERROR_INVALID_MAXIMUM_GRAIN_SAMPLES = 0x804A0050; -constexpr int ORBIS_NGS2_ERROR_INVALID_GRAIN_SAMPLES = 0x804A0051; -constexpr int ORBIS_NGS2_ERROR_INVALID_CHANNELS = 0x804A0052; -constexpr int ORBIS_NGS2_ERROR_INVALD_ADDRESS = 0x804A0053; -constexpr int ORBIS_NGS2_ERROR_INVALD_SIZE = 0x804A0054; +constexpr int ORBIS_NGS2_ERROR_FAIL = 0x804A0001; +constexpr int ORBIS_NGS2_ERROR_INVALID_MAX_GRAIN_SAMPLES = 0x804A0050; +constexpr int ORBIS_NGS2_ERROR_INVALID_NUM_GRAIN_SAMPLES = 0x804A0051; +constexpr int ORBIS_NGS2_ERROR_INVALID_NUM_CHANNELS = 0x804A0052; +constexpr int ORBIS_NGS2_ERROR_INVALID_OUT_ADDRESS = 0x804A0053; +constexpr int ORBIS_NGS2_ERROR_INVALID_OUT_SIZE = 0x804A0054; +constexpr int ORBIS_NGS2_ERROR_INVALID_OPTION_ADDRESS = 0x804A0080; constexpr int ORBIS_NGS2_ERROR_INVALID_OPTION_SIZE = 0x804A0081; -constexpr int ORBIS_NGS2_ERROR_INVALID_RACK_OPTION_MAX_MATRICES = 0x804A0100; -constexpr int ORBIS_NGS2_ERROR_INVALID_RACK_OPTION_MAX_PORTS = 0x804A0101; -constexpr int ORBIS_NGS2_ERROR_INVALID_RACK_OPTION_MAX_INPUT_DELAY_BLOCKS = 0x804A0102; -constexpr int ORBIS_NGS2_ERROR_INVALID_MATRIX_LEVELS = 0x804A0150; -constexpr int ORBIS_NGS2_ERROR_SAMPLER_WAVEFORM_TERMINATED = 0x804A0151; -constexpr int ORBIS_NGS2_ERROR_INVALID_ENVELOPE_POINTS = 0x804A0152; +constexpr int ORBIS_NGS2_ERROR_INVALID_OPTION_FLAG = 0x804A0082; +constexpr int ORBIS_NGS2_ERROR_INVALID_MAX_MATRICES = 0x804A0100; +constexpr int ORBIS_NGS2_ERROR_INVALID_MAX_PORTS = 0x804A0101; +constexpr int ORBIS_NGS2_ERROR_INVALID_MAX_INPUT_DELAY_BLOCKS = 0x804A0102; +constexpr int ORBIS_NGS2_ERROR_INVALID_MAX_VOICES = 0x804A0103; +constexpr int ORBIS_NGS2_ERROR_INVALID_MAX_CHANNELS = 0x804A0104; +constexpr int ORBIS_NGS2_ERROR_INVALID_MAX_CHANNEL_WORKS = 0x804A0105; +constexpr int ORBIS_NGS2_ERROR_INVALID_MAX_WAVEFORM_BLOCKS = 0x804A0106; +constexpr int ORBIS_NGS2_ERROR_INVALID_MAX_ENVELOPE_POINTS = 0x804A0107; +constexpr int ORBIS_NGS2_ERROR_INVALID_MAX_FILTERS = 0x804A0108; +constexpr int ORBIS_NGS2_ERROR_INVALID_MAX_CODEC_CACHES = 0x804A0109; +constexpr int ORBIS_NGS2_ERROR_INVALID_MAX_ATRAC9_DECODERS = 0x804A010A; +constexpr int ORBIS_NGS2_ERROR_INVALID_MAX_ATRAC9_CHANNEL_WORKS = 0x804A010B; +constexpr int ORBIS_NGS2_ERROR_INVALID_MAX_INPUTS = 0x804A010C; +constexpr int ORBIS_NGS2_ERROR_INVALID_NUM_MATRIX_LEVELS = 0x804A0150; +constexpr int ORBIS_NGS2_ERROR_INVALID_NUM_WAVEFORM_BLOCKS = 0x804A0151; +constexpr int ORBIS_NGS2_ERROR_INVALID_NUM_ENVELOPE_POINTS = 0x804A0152; constexpr int ORBIS_NGS2_ERROR_INVALID_MATRIX_LEVEL_ADDRESS = 0x804A0153; -constexpr int ORBIS_NGS2_ERROR_INVALID_SAMPLER_WAVEFORM_BLOCK_ADDRESS = 0x804A0154; +constexpr int ORBIS_NGS2_ERROR_INVALID_WAVEFORM_BLOCK_ADDRESS = 0x804A0154; constexpr int ORBIS_NGS2_ERROR_INVALID_ENVELOPE_POINT_ADDRESS = 0x804A0155; +constexpr int ORBIS_NGS2_ERROR_INVALID_NUM_PEAKMETER_BLOCKS = 0x804A0156; constexpr int ORBIS_NGS2_ERROR_INVALID_HANDLE = 0x804A0200; constexpr int ORBIS_NGS2_ERROR_INVALID_SAMPLE_RATE = 0x804A0201; +constexpr int ORBIS_NGS2_ERROR_INVALID_REPORT_TYPE = 0x804A0202; +constexpr int ORBIS_NGS2_ERROR_INVALID_REPORT_HANDLER = 0x804A0203; constexpr int ORBIS_NGS2_ERROR_INVALID_REPORT_HANDLE = 0x804A0204; +constexpr int ORBIS_NGS2_ERROR_EMPTY_REPORT_HANDLE = 0x804A0205; constexpr int ORBIS_NGS2_ERROR_INVALID_BUFFER_INFO = 0x804A0206; constexpr int ORBIS_NGS2_ERROR_INVALID_BUFFER_ADDRESS = 0x804A0207; -constexpr int ORBIS_NGS2_ERROR_INVALID_BUFFER_ALIGNMENT = 0x804A0208; +constexpr int ORBIS_NGS2_ERROR_INVALID_BUFFER_ALIGN = 0x804A0208; constexpr int ORBIS_NGS2_ERROR_INVALID_BUFFER_SIZE = 0x804A0209; constexpr int ORBIS_NGS2_ERROR_INVALID_BUFFER_ALLOCATOR = 0x804A020A; -constexpr int ORBIS_NGS2_ERROR_BUFFER_VERIFY_FAILED = 0x804A020B; -constexpr int ORBIS_NGS2_ERROR_MODULE_PLAYER_DATA_EMPTY = 0x804A020C; +constexpr int ORBIS_NGS2_ERROR_BUFFER_BROKEN = 0x804A020B; +constexpr int ORBIS_NGS2_ERROR_EMPTY_BUFFER = 0x804A020C; constexpr int ORBIS_NGS2_ERROR_INVALID_SYSTEM_HANDLE = 0x804A0230; constexpr int ORBIS_NGS2_ERROR_INVALID_RACK_ID = 0x804A0260; constexpr int ORBIS_NGS2_ERROR_INVALID_RACK_HANDLE = 0x804A0261; constexpr int ORBIS_NGS2_ERROR_INVALID_VOICE_HANDLE = 0x804A0300; +constexpr int ORBIS_NGS2_ERROR_UNINIT_VOICE = 0x804A0301; constexpr int ORBIS_NGS2_ERROR_INVALID_VOICE_INDEX = 0x804A0302; -constexpr int ORBIS_NGS2_ERROR_INVALID_VOICE_EVENT = 0x804A0303; -constexpr int ORBIS_NGS2_ERROR_INVALID_VOICE_PORT_INDEX = 0x804A0304; -constexpr int ORBIS_NGS2_ERROR_INVALID_VOICE_INPUT_OR_RACK_OCCUPIED = 0x804A0305; -constexpr int ORBIS_NGS2_ERROR_INVALID_CONTROL_ID = 0x804A0308; -constexpr int ORBIS_NGS2_ERROR_INVALID_VOICE_CONTROL_PARAMETER = 0x804A0309; -constexpr int ORBIS_NGS2_ERROR_INVALID_PARAMETER_SIZE = 0x804A030A; +constexpr int ORBIS_NGS2_ERROR_INVALID_EVENT_TYPE = 0x804A0303; +constexpr int ORBIS_NGS2_ERROR_INVALID_PORT_INDEX = 0x804A0304; +constexpr int ORBIS_NGS2_ERROR_INVALID_PATCH = 0x804A0305; +constexpr int ORBIS_NGS2_ERROR_EMPTY_CHANNEL_WORK = 0x804A0306; +constexpr int ORBIS_NGS2_ERROR_EMPTY_CODEC_DECODER = 0x804A0307; +constexpr int ORBIS_NGS2_ERROR_INVALID_VOICE_CONTROL_ID = 0x804A0308; +constexpr int ORBIS_NGS2_ERROR_INVALID_VOICE_CONTROL_ADDRESS = 0x804A0309; +constexpr int ORBIS_NGS2_ERROR_INVALID_VOICE_CONTROL_SIZE = 0x804A030A; constexpr int ORBIS_NGS2_ERROR_DETECTED_CIRCULAR_VOICE_CONTROL = 0x804A030B; -constexpr int ORBIS_NGS2_ERROR_INVALID_SAMPLER_WAVEFORM_DATA = 0x804A0400; -constexpr int ORBIS_NGS2_ERROR_INVALID_SAMPLER_WAVEFORM_FORMAT = 0x804A0401; -constexpr int ORBIS_NGS2_ERROR_INVALID_SAMPLER_WAVEFORM_TYPE_NO_ATRAC9_DECODERS = 0x804A0402; -constexpr int ORBIS_NGS2_ERROR_INVALID_SAMPLER_ATRAC9_CONFIG_DATA = 0x804A0403; -constexpr int ORBIS_NGS2_ERROR_INVALID_SAMPLER_WAVEFORM_SAMPLE_RATE = 0x804A0404; -constexpr int ORBIS_NGS2_ERROR_INVALID_SAMPLER_WAVEFORM_FRAME = 0x804A0405; -constexpr int ORBIS_NGS2_ERROR_INVALID_SAMPLER_WAVEFORM_ADDRESS = 0x804A0406; +constexpr int ORBIS_NGS2_ERROR_UNABLE_CALLBACK = 0x804A030C; +constexpr int ORBIS_NGS2_ERROR_INVALID_CALLBACK_FLAG = 0x804A030D; +constexpr int ORBIS_NGS2_ERROR_INVALID_CALLBACK_HANDLER = 0x804A030E; +constexpr int ORBIS_NGS2_ERROR_INVALID_OPERATION = 0x804A030F; +constexpr int ORBIS_NGS2_ERROR_INVALID_WAVEFORM_FORMAT = 0x804A0400; +constexpr int ORBIS_NGS2_ERROR_UNKNOWN_WAVEFORM_FORMAT = 0x804A0401; +constexpr int ORBIS_NGS2_ERROR_INVALID_WAVEFORM_TYPE = 0x804A0402; +constexpr int ORBIS_NGS2_ERROR_INVALID_WAVEFORM_CONFIG = 0x804A0403; +constexpr int ORBIS_NGS2_ERROR_INVALID_WAVEFORM_SAMPLE_RATE = 0x804A0404; +constexpr int ORBIS_NGS2_ERROR_INVALID_WAVEFORM_FRAME = 0x804A0405; +constexpr int ORBIS_NGS2_ERROR_INVALID_WAVEFORM_ADDRESS = 0x804A0406; +constexpr int ORBIS_NGS2_ERROR_INVALID_WAVEFORM_SIZE = 0x804A0407; +constexpr int ORBIS_NGS2_ERROR_INVALID_WAVEFORM_DATA = 0x804A0408; +constexpr int ORBIS_NGS2_ERROR_INVALID_WAVEFORM_BLOCK_NUM_REPEATS = 0x804A0409; constexpr int ORBIS_NGS2_ERROR_INVALID_ENVELOPE_CURVE = 0x804A0500; +constexpr int ORBIS_NGS2_ERROR_INVALID_PEAKMETER_FLAG = 0x804A0510; +constexpr int ORBIS_NGS2_ERROR_INVALID_MAX_CHORUS_PHASES = 0x804A0520; +constexpr int ORBIS_NGS2_ERROR_INVALID_NUM_CHORUS_PHASES = 0x804A0521; +constexpr int ORBIS_NGS2_ERROR_INVALID_MAX_DELAY_LENGTH = 0x804A0530; +constexpr int ORBIS_NGS2_ERROR_INVALID_MAX_DELAY_TAPS = 0x804A0531; +constexpr int ORBIS_NGS2_ERROR_INVALID_DELAY_TYPE = 0x804A0532; +constexpr int ORBIS_NGS2_ERROR_INVALID_NUM_DELAY_TAPS = 0x804A0533; +constexpr int ORBIS_NGS2_ERROR_INVALID_DELAY_TAP_INFO = 0x804A0534; +constexpr int ORBIS_NGS2_ERROR_INVALID_PITCH_SHIFT_QUALITY = 0x804A0540; constexpr int ORBIS_NGS2_ERROR_INVALID_FILTER_INDEX = 0x804A0600; constexpr int ORBIS_NGS2_ERROR_INVALID_FILTER_TYPE = 0x804A0601; constexpr int ORBIS_NGS2_ERROR_INVALID_FILTER_LOCATION = 0x804A0602; -constexpr int ORBIS_NGS2_ERROR_INVALID_LFE_CUT_OFF_FREQUENCY = 0x804A0603; -constexpr int ORBIS_NGS2_ERROR_INVALID_MATRIX_INDEX_OR_TYPE = 0x804A0700; \ No newline at end of file +constexpr int ORBIS_NGS2_ERROR_INVALID_LFE_FC = 0x804A0603; +constexpr int ORBIS_NGS2_ERROR_INVALID_USER_FX_PARAM_SIZE = 0x804A0650; +constexpr int ORBIS_NGS2_ERROR_INVALID_MATRIX_INDEX = 0x804A0700; +constexpr int ORBIS_NGS2_ERROR_CODEC_UNKNOWN_WAVEFORM = 0x804A0800; +constexpr int ORBIS_NGS2_ERROR_CODEC_EMPTY_INSTANCE = 0x804A0801; +constexpr int ORBIS_NGS2_ERROR_CODEC_EMPTY_CHANNEL = 0x804A0802; +constexpr int ORBIS_NGS2_ERROR_CODEC_SETUP_FAIL = 0x804A0803; +constexpr int ORBIS_NGS2_ERROR_CODEC_RESET_FAIL = 0x804A0804; +constexpr int ORBIS_NGS2_ERROR_CODEC_DECODE_FAIL = 0x804A0805; +constexpr int ORBIS_NGS2_ERROR_INVALID_REVERB_SIZE = 0x804A0900; +constexpr int ORBIS_NGS2_ERROR_INVALID_PAN_UNIT_ANGLE = 0x804A0910; +constexpr int ORBIS_NGS2_ERROR_INVALID_PAN_SPEAKER = 0x804A0911; +constexpr int ORBIS_NGS2_ERROR_INVALID_PAN_MATRIX_FORMAT = 0x804A0912; +constexpr int ORBIS_NGS2_ERROR_INVALID_PAN_WORK = 0x804A0913; +constexpr int ORBIS_NGS2_ERROR_INVALID_PAN_PARAM = 0x804A0914; +constexpr int ORBIS_NGS2_ERROR_INVALID_GEOM_DISTANCE = 0x804A0920; +constexpr int ORBIS_NGS2_ERROR_INVALID_GEOM_LISTENER_ADDRESS = 0x804A0921; +constexpr int ORBIS_NGS2_ERROR_INVALID_GEOM_SOURCE_ADDRESS = 0x804A0922; +constexpr int ORBIS_NGS2_ERROR_INVALID_GEOM_FLAG = 0x804A0923; +constexpr int ORBIS_NGS2_ERROR_INVALID_GEOM_CONE = 0x804A0924; +constexpr int ORBIS_NGS2_ERROR_INVALID_MODULE_ID = 0x804A0A00; +constexpr int ORBIS_NGS2_ERROR_INVALID_NUM_MODULES = 0x804A0A01; +constexpr int ORBIS_NGS2_ERROR_INVALID_NUM_MODULE_BUFFERS = 0x804A0A02; +constexpr int ORBIS_NGS2_ERROR_INVALID_MODULE_BUFFER_ID = 0x804A0A03; +constexpr int ORBIS_NGS2_ERROR_INVALID_MODULE_STATE_OFFSET = 0x804A0A04; +constexpr int ORBIS_NGS2_ERROR_INVALID_MODULE_STATE_SIZE = 0x804A0A05; +constexpr int ORBIS_NGS2_ERROR_INVALID_VOICE_STATE_SIZE = 0x804A0A06; +constexpr int ORBIS_NGS2_ERROR_INVALID_MODULE_INDEX = 0x804A0B00; +constexpr int ORBIS_NGS2_ERROR_INVALID_MODULE_INFO_SIZE = 0x804A0B01; \ No newline at end of file diff --git a/src/core/libraries/ngs2/ngs2_impl.cpp b/src/core/libraries/ngs2/ngs2_impl.cpp index 185be94d7..793435d83 100644 --- a/src/core/libraries/ngs2/ngs2_impl.cpp +++ b/src/core/libraries/ngs2/ngs2_impl.cpp @@ -12,22 +12,23 @@ using namespace Libraries::Kernel; namespace Libraries::Ngs2 { -s32 Ngs2::ReportInvalid(u32 handle_type) const { +s32 Ngs2::ReportInvalid(Ngs2Handle* handle, u32 handle_type) const { + uintptr_t hAddress = reinterpret_cast(handle); switch (handle_type) { case 1: - LOG_ERROR(Lib_Ngs2, "Invalid system handle {}", this); + LOG_ERROR(Lib_Ngs2, "Invalid system handle {}", hAddress); return ORBIS_NGS2_ERROR_INVALID_SYSTEM_HANDLE; case 2: - LOG_ERROR(Lib_Ngs2, "Invalid rack handle {}", this); + LOG_ERROR(Lib_Ngs2, "Invalid rack handle {}", hAddress); return ORBIS_NGS2_ERROR_INVALID_RACK_HANDLE; case 4: - LOG_ERROR(Lib_Ngs2, "Invalid voice handle {}", this); + LOG_ERROR(Lib_Ngs2, "Invalid voice handle {}", hAddress); return ORBIS_NGS2_ERROR_INVALID_VOICE_HANDLE; case 8: - LOG_ERROR(Lib_Ngs2, "Invalid report handle {}", this); + LOG_ERROR(Lib_Ngs2, "Invalid report handle {}", hAddress); return ORBIS_NGS2_ERROR_INVALID_REPORT_HANDLE; default: - LOG_ERROR(Lib_Ngs2, "Invalid handle {}", this); + LOG_ERROR(Lib_Ngs2, "Invalid handle {}", hAddress); return ORBIS_NGS2_ERROR_INVALID_HANDLE; } } @@ -58,24 +59,24 @@ s32 Ngs2::HandleCleanup(Ngs2Handle* handle, u32 hType, void* dataOut) { } } } - return HandleReportInvalid(handle, hType); + return this->ReportInvalid(handle, hType); } s32 Ngs2::HandleEnter(Ngs2Handle* handle, u32 hType, Ngs2Handle* handleOut) { if (!handle) { - return HandleReportInvalid(handle, 0); + return this->ReportInvalid(handle, 0); } if (handle->selfPointer != handle || !handle->atomicPtr || !handle->dataPointer || (~hType & handle->handleType)) { - return HandleReportInvalid(handle, handle->handleType); + return this->ReportInvalid(handle, handle->handleType); } std::atomic* atomic = handle->atomicPtr; while (true) { u32 i = atomic->load(); if (i == 0) { - return HandleReportInvalid(handle, handle->handleType); + return this->ReportInvalid(handle, handle->handleType); } if (atomic->compare_exchange_strong(i, i + 1)) { break; @@ -83,7 +84,7 @@ s32 Ngs2::HandleEnter(Ngs2Handle* handle, u32 hType, Ngs2Handle* handleOut) { } if (handleOut) { - *handleOut = handle; + handleOut = handle; } return ORBIS_OK; } @@ -137,13 +138,13 @@ s32 Ngs2::SystemSetupCore(StackBuffer* buf, SystemOptions* options, Ngs2Handle** // Validate maxGrainSamples if (maxGrainSamples < 64 || maxGrainSamples > 1024 || (maxGrainSamples & 0x3F) != 0) { LOG_ERROR(Lib_Ngs2, "Invalid system option (maxGrainSamples={},x64)", maxGrainSamples); - return ORBIS_NGS2_ERROR_INVALID_MAXIMUM_GRAIN_SAMPLES; + return ORBIS_NGS2_ERROR_INVALID_MAX_GRAIN_SAMPLES; } // Validate numGrainSamples if (numGrainSamples < 64 || numGrainSamples > 1024 || (numGrainSamples & 0x3F) != 0) { LOG_ERROR(Lib_Ngs2, "Invalid system option (numGrainSamples={},x64)", numGrainSamples); - return ORBIS_NGS2_ERROR_INVALID_GRAIN_SAMPLES; + return ORBIS_NGS2_ERROR_INVALID_NUM_GRAIN_SAMPLES; } // Validate sampleRate diff --git a/src/core/libraries/ngs2/ngs2_impl.h b/src/core/libraries/ngs2/ngs2_impl.h index 360017799..fea87c51c 100644 --- a/src/core/libraries/ngs2/ngs2_impl.h +++ b/src/core/libraries/ngs2/ngs2_impl.h @@ -9,7 +9,7 @@ namespace Libraries::Ngs2 { class Ngs2 { public: - s32 ReportInvalid(u32 handle_type) const; + s32 ReportInvalid(Ngs2Handle* handle, u32 handle_type) const; s32 HandleSetup(Ngs2Handle* handle, void* data, std::atomic* atomic, u32 type, u32 flags); s32 HandleCleanup(Ngs2Handle* handle, u32 hType, void* dataOut); s32 HandleEnter(Ngs2Handle* handle, u32 hType, Ngs2Handle* handleOut); diff --git a/src/core/libraries/np_manager/np_manager.cpp b/src/core/libraries/np_manager/np_manager.cpp index c657fbf60..a761caa73 100644 --- a/src/core/libraries/np_manager/np_manager.cpp +++ b/src/core/libraries/np_manager/np_manager.cpp @@ -1,7 +1,6 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -// Generated By moduleGenerator #include "common/config.h" #include "common/logging/log.h" #include "core/libraries/error_codes.h" diff --git a/src/core/libraries/np_score/np_score.cpp b/src/core/libraries/np_score/np_score.cpp index d6e4631ce..dc16e12d2 100644 --- a/src/core/libraries/np_score/np_score.cpp +++ b/src/core/libraries/np_score/np_score.cpp @@ -1,7 +1,6 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -// Generated By moduleGenerator #include "common/logging/log.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" diff --git a/src/core/libraries/np_trophy/np_trophy.cpp b/src/core/libraries/np_trophy/np_trophy.cpp index 4b1f43694..ed25322b4 100644 --- a/src/core/libraries/np_trophy/np_trophy.cpp +++ b/src/core/libraries/np_trophy/np_trophy.cpp @@ -1,7 +1,6 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -// Generated By moduleGenerator #include #include "common/logging/log.h" diff --git a/src/core/libraries/pad/pad.cpp b/src/core/libraries/pad/pad.cpp index 305b20bd6..cb0da552a 100644 --- a/src/core/libraries/pad/pad.cpp +++ b/src/core/libraries/pad/pad.cpp @@ -1,10 +1,10 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -// Generated By moduleGenerator -#include -#include +#include "common/assert.h" +#include "common/config.h" #include "common/logging/log.h" +#include "common/singleton.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" #include "input/controller.h" @@ -25,6 +25,9 @@ int PS4_SYSV_ABI scePadConnectPort() { int PS4_SYSV_ABI scePadDeviceClassGetExtendedInformation( s32 handle, OrbisPadDeviceClassExtendedInformation* pExtInfo) { LOG_ERROR(Lib_Pad, "(STUBBED) called"); + if (Config::getUseSpecialPad()) { + pExtInfo->deviceClass = (OrbisPadDeviceClass)Config::getSpecialPadClass(); + } return ORBIS_OK; } @@ -107,6 +110,10 @@ int PS4_SYSV_ABI scePadGetControllerInformation(s32 handle, OrbisPadControllerIn pInfo->connectedCount = 1; pInfo->connected = true; pInfo->deviceClass = ORBIS_PAD_DEVICE_CLASS_STANDARD; + if (Config::getUseSpecialPad()) { + pInfo->connectionType = ORBIS_PAD_PORT_TYPE_SPECIAL; + pInfo->deviceClass = (OrbisPadDeviceClass)Config::getSpecialPadClass(); + } return SCE_OK; } @@ -239,11 +246,26 @@ int PS4_SYSV_ABI scePadMbusTerm() { int PS4_SYSV_ABI scePadOpen(s32 userId, s32 type, s32 index, const OrbisPadOpenParam* pParam) { LOG_INFO(Lib_Pad, "(DUMMY) called user_id = {} type = {} index = {}", userId, type, index); + if (Config::getUseSpecialPad()) { + if (type != ORBIS_PAD_PORT_TYPE_SPECIAL) + return ORBIS_PAD_ERROR_DEVICE_NOT_CONNECTED; + } else { + if (type != ORBIS_PAD_PORT_TYPE_STANDARD) + return ORBIS_PAD_ERROR_DEVICE_NOT_CONNECTED; + } return 1; // dummy } -int PS4_SYSV_ABI scePadOpenExt() { +int PS4_SYSV_ABI scePadOpenExt(s32 userId, s32 type, s32 index, + const OrbisPadOpenExtParam* pParam) { LOG_ERROR(Lib_Pad, "(STUBBED) called"); + if (Config::getUseSpecialPad()) { + if (type != ORBIS_PAD_PORT_TYPE_SPECIAL) + return ORBIS_PAD_ERROR_DEVICE_NOT_CONNECTED; + } else { + if (type != ORBIS_PAD_PORT_TYPE_STANDARD) + return ORBIS_PAD_ERROR_DEVICE_NOT_CONNECTED; + } return 1; // dummy } @@ -286,12 +308,13 @@ int PS4_SYSV_ABI scePadRead(s32 handle, OrbisPadData* pData, s32 num) { pData[i].angularVelocity.x = 0.0f; pData[i].angularVelocity.y = 0.0f; pData[i].angularVelocity.z = 0.0f; - pData[i].touchData.touchNum = 0; - pData[i].touchData.touch[0].x = 0; - pData[i].touchData.touch[0].y = 0; + pData[i].touchData.touchNum = + (states[i].touchpad[0].state ? 1 : 0) + (states[i].touchpad[1].state ? 1 : 0); + pData[i].touchData.touch[0].x = states[i].touchpad[0].x; + pData[i].touchData.touch[0].y = states[i].touchpad[0].y; pData[i].touchData.touch[0].id = 1; - pData[i].touchData.touch[1].x = 0; - pData[i].touchData.touch[1].y = 0; + pData[i].touchData.touch[1].x = states[i].touchpad[1].x; + pData[i].touchData.touch[1].y = states[i].touchpad[1].y; pData[i].touchData.touch[1].id = 2; pData[i].connected = connected; pData[i].timestamp = states[i].time; diff --git a/src/core/libraries/pad/pad.h b/src/core/libraries/pad/pad.h index 3e9c14a17..b18bbc355 100644 --- a/src/core/libraries/pad/pad.h +++ b/src/core/libraries/pad/pad.h @@ -229,6 +229,13 @@ struct OrbisPadOpenParam { u8 reserve[8]; }; +struct OrbisPadOpenExtParam { + u16 vendorId; + u16 productId; + u16 productId_2; + u8 reserve[10]; +}; + struct OrbisPadLightBarParam { u8 r; u8 g; @@ -284,7 +291,7 @@ int PS4_SYSV_ABI scePadIsValidHandle(); int PS4_SYSV_ABI scePadMbusInit(); int PS4_SYSV_ABI scePadMbusTerm(); int PS4_SYSV_ABI scePadOpen(s32 userId, s32 type, s32 index, const OrbisPadOpenParam* pParam); -int PS4_SYSV_ABI scePadOpenExt(); +int PS4_SYSV_ABI scePadOpenExt(s32 userId, s32 type, s32 index, const OrbisPadOpenExtParam* pParam); int PS4_SYSV_ABI scePadOpenExt2(); int PS4_SYSV_ABI scePadOutputReport(); int PS4_SYSV_ABI scePadRead(s32 handle, OrbisPadData* pData, s32 num); diff --git a/src/core/libraries/playgo/playgo.cpp b/src/core/libraries/playgo/playgo.cpp index 66422dc28..d4f5c6b7c 100644 --- a/src/core/libraries/playgo/playgo.cpp +++ b/src/core/libraries/playgo/playgo.cpp @@ -1,9 +1,9 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include #include "common/logging/log.h" #include "common/singleton.h" +#include "core/file_format/playgo_chunk.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" #include "core/libraries/system/systemservice.h" diff --git a/src/core/libraries/playgo/playgo.h b/src/core/libraries/playgo/playgo.h index f5ae1baa6..2338c9ebf 100644 --- a/src/core/libraries/playgo/playgo.h +++ b/src/core/libraries/playgo/playgo.h @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #pragma once + #include "common/types.h" #include "playgo_types.h" diff --git a/src/core/libraries/random/random.cpp b/src/core/libraries/random/random.cpp index 8147c5183..f7cc3fd2c 100644 --- a/src/core/libraries/random/random.cpp +++ b/src/core/libraries/random/random.cpp @@ -1,14 +1,16 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "random.h" +#include "random_error.h" + #include "common/logging/log.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" -#include "random.h" namespace Libraries::Random { -s32 PS4_SYSV_ABI sceRandomGetRandomNumber(u8* buf, size_t size) { +s32 PS4_SYSV_ABI sceRandomGetRandomNumber(u8* buf, std::size_t size) { LOG_TRACE(Lib_Random, "called"); if (size > SCE_RANDOM_MAX_SIZE) { return SCE_RANDOM_ERROR_INVALID; diff --git a/src/core/libraries/random/random.h b/src/core/libraries/random/random.h index b5f87f877..b483cf6ed 100644 --- a/src/core/libraries/random/random.h +++ b/src/core/libraries/random/random.h @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #pragma once + #include "common/types.h" namespace Core::Loader { @@ -11,7 +12,7 @@ class SymbolsResolver; namespace Libraries::Random { constexpr int32_t SCE_RANDOM_MAX_SIZE = 64; -s32 PS4_SYSV_ABI sceRandomGetRandomNumber(u8* buf, size_t size); +s32 PS4_SYSV_ABI sceRandomGetRandomNumber(u8* buf, std::size_t size); void RegisterlibSceRandom(Core::Loader::SymbolsResolver* sym); } // namespace Libraries::Random \ No newline at end of file diff --git a/src/core/libraries/random/random_error.h b/src/core/libraries/random/random_error.h new file mode 100644 index 000000000..772316ae5 --- /dev/null +++ b/src/core/libraries/random/random_error.h @@ -0,0 +1,8 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +constexpr int SCE_RANDOM_ERROR_INVALID = 0x817C0016; +constexpr int SCE_RANDOM_ERROR_OUT_OF_RESOURCES = 0x817C001C; +constexpr int SCE_RANDOM_ERROR_FATAL = 0x817C00FF; \ No newline at end of file diff --git a/src/core/libraries/rtc/rtc.cpp b/src/core/libraries/rtc/rtc.cpp index 82e6db67e..387a8558b 100644 --- a/src/core/libraries/rtc/rtc.cpp +++ b/src/core/libraries/rtc/rtc.cpp @@ -1,12 +1,13 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -// Generated By moduleGenerator #include + #include "common/logging/log.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" #include "rtc.h" +#include "rtc_error.h" namespace Libraries::Rtc { @@ -123,8 +124,7 @@ int PS4_SYSV_ABI sceRtcGetTick() { } int PS4_SYSV_ABI sceRtcGetTickResolution() { - LOG_ERROR(Lib_Rtc, "(STUBBED) called"); - return ORBIS_OK; + return 1000000; } int PS4_SYSV_ABI sceRtcGetTime_t() { diff --git a/src/core/libraries/rtc/rtc_error.h b/src/core/libraries/rtc/rtc_error.h new file mode 100644 index 000000000..04eecbbdf --- /dev/null +++ b/src/core/libraries/rtc/rtc_error.h @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +constexpr int ORBIS_RTC_ERROR_INVALID_PARAMETER = 0x80010602; +constexpr int ORBIS_RTC_ERROR_INVALID_TICK_PARAMETER = 0x80010603; +constexpr int ORBIS_RTC_ERROR_INVALID_DATE_PARAMETER = 0x80010604; +constexpr int ORBIS_RTC_ERROR_NOT_IMPLEMENTED = 0x80010605; +constexpr int ORBIS_RTC_ERROR_INVALID_TIMEZONE_FORMAT = 0x80010607; +constexpr int ORBIS_RTC_ERROR_INVALID_YEARS_PARAMETER = 0x80010621; +constexpr int ORBIS_RTC_ERROR_INVALID_MONTHS_PARAMETER = 0x80010622; +constexpr int ORBIS_RTC_ERROR_INVALID_DAYS_PARAMETER = 0x80010623; +constexpr int ORBIS_RTC_ERROR_INVALID_HOURS_PARAMETER = 0x80010624; +constexpr int ORBIS_RTC_ERROR_INVALID_MINUTES_PARAMETER = 0x80010625; +constexpr int ORBIS_RTC_ERROR_INVALID_SECONDS_PARAMETER = 0x80010626; +constexpr int ORBIS_RTC_ERROR_INVALID_MILLISECONDS_PARAMETER = 0x80010627; \ No newline at end of file diff --git a/src/core/libraries/save_data/savedata.cpp b/src/core/libraries/save_data/savedata.cpp index 20496d76d..959a75705 100644 --- a/src/core/libraries/save_data/savedata.cpp +++ b/src/core/libraries/save_data/savedata.cpp @@ -3,12 +3,13 @@ #include #include -#include -#include -#include -#include + #include "common/assert.h" #include "common/logging/log.h" +#include "common/path_util.h" +#include "common/singleton.h" +#include "core/file_format/psf.h" +#include "core/file_sys/fs.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" #include "core/libraries/save_data/savedata.h" @@ -505,8 +506,11 @@ s32 saveDataMount(u32 user_id, char* dir_name, u32 mount_mode, ORBIS_SAVE_DATA_MOUNT_MODE_DESTRUCT_OFF: case ORBIS_SAVE_DATA_MOUNT_MODE_CREATE | ORBIS_SAVE_DATA_MOUNT_MODE_RDWR | ORBIS_SAVE_DATA_MOUNT_MODE_COPY_ICON: + case ORBIS_SAVE_DATA_MOUNT_MODE_CREATE | ORBIS_SAVE_DATA_MOUNT_MODE_COPY_ICON: case ORBIS_SAVE_DATA_MOUNT_MODE_CREATE | ORBIS_SAVE_DATA_MOUNT_MODE_DESTRUCT_OFF | - ORBIS_SAVE_DATA_MOUNT_MODE_COPY_ICON: { + ORBIS_SAVE_DATA_MOUNT_MODE_COPY_ICON: + case ORBIS_SAVE_DATA_MOUNT_MODE_CREATE | ORBIS_SAVE_DATA_MOUNT_MODE_RDWR | + ORBIS_SAVE_DATA_MOUNT_MODE_DESTRUCT_OFF | ORBIS_SAVE_DATA_MOUNT_MODE_COPY_ICON: { if (std::filesystem::exists(mount_dir)) { return ORBIS_SAVE_DATA_ERROR_EXISTS; } @@ -516,9 +520,13 @@ s32 saveDataMount(u32 user_id, char* dir_name, u32 mount_mode, mount_result->mount_status = 1; } } break; + case ORBIS_SAVE_DATA_MOUNT_MODE_CREATE2: case ORBIS_SAVE_DATA_MOUNT_MODE_CREATE2 | ORBIS_SAVE_DATA_MOUNT_MODE_RDWR: + case ORBIS_SAVE_DATA_MOUNT_MODE_CREATE2 | ORBIS_SAVE_DATA_MOUNT_MODE_COPY_ICON: case ORBIS_SAVE_DATA_MOUNT_MODE_CREATE2 | ORBIS_SAVE_DATA_MOUNT_MODE_RDWR | - ORBIS_SAVE_DATA_MOUNT_MODE_COPY_ICON: { + ORBIS_SAVE_DATA_MOUNT_MODE_COPY_ICON: + case ORBIS_SAVE_DATA_MOUNT_MODE_CREATE2 | ORBIS_SAVE_DATA_MOUNT_MODE_RDWR | + ORBIS_SAVE_DATA_MOUNT_MODE_DESTRUCT_OFF | ORBIS_SAVE_DATA_MOUNT_MODE_COPY_ICON: { if (!std::filesystem::exists(mount_dir)) { std::filesystem::create_directories(mount_dir); } diff --git a/src/core/libraries/screenshot/screenshot.cpp b/src/core/libraries/screenshot/screenshot.cpp index 1924ddf54..eaa535de7 100644 --- a/src/core/libraries/screenshot/screenshot.cpp +++ b/src/core/libraries/screenshot/screenshot.cpp @@ -1,7 +1,6 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -// Generated By moduleGenerator #include "common/logging/log.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" diff --git a/src/core/libraries/system/msgdialog.cpp b/src/core/libraries/system/msgdialog.cpp index 1c8653f50..452feec93 100644 --- a/src/core/libraries/system/msgdialog.cpp +++ b/src/core/libraries/system/msgdialog.cpp @@ -6,6 +6,8 @@ #include "core/libraries/libs.h" #include "core/libraries/system/msgdialog.h" +#include + namespace Libraries::MsgDialog { int PS4_SYSV_ABI sceMsgDialogClose() { @@ -30,9 +32,22 @@ int PS4_SYSV_ABI sceMsgDialogInitialize() { s32 PS4_SYSV_ABI sceMsgDialogOpen(const OrbisMsgDialogParam* param) { LOG_ERROR(Lib_MsgDlg, "(STUBBED) called"); - OrbisMsgDialogUserMessageParam* userMsgParam = param->userMsgParam; - const char* msg = userMsgParam->msg; - printf("sceMsgDialogOpen msg : %s", msg); + switch (param->mode) { + case ORBIS_MSG_DIALOG_MODE_USER_MSG: + LOG_INFO(Lib_MsgDlg, "sceMsgDialogOpen userMsg type = %s msg = %s", + magic_enum::enum_name(param->userMsgParam->buttonType), param->userMsgParam->msg); + break; + case ORBIS_MSG_DIALOG_MODE_PROGRESS_BAR: + LOG_INFO(Lib_MsgDlg, "sceMsgDialogOpen progressBar type = %s msg = %s", + magic_enum::enum_name(param->progBarParam->barType), param->progBarParam->msg); + break; + case ORBIS_MSG_DIALOG_MODE_SYSTEM_MSG: + LOG_INFO(Lib_MsgDlg, "sceMsgDialogOpen systemMsg type: %s", + magic_enum::enum_name(param->sysMsgParam->sysMsgType)); + break; + default: + break; + } return ORBIS_OK; } diff --git a/src/core/libraries/system/savedatadialog.cpp b/src/core/libraries/system/savedatadialog.cpp index d842fd11a..5aad480d0 100644 --- a/src/core/libraries/system/savedatadialog.cpp +++ b/src/core/libraries/system/savedatadialog.cpp @@ -30,7 +30,7 @@ int PS4_SYSV_ABI sceSaveDataDialogInitialize() { int PS4_SYSV_ABI sceSaveDataDialogIsReadyToDisplay() { LOG_ERROR(Lib_SaveDataDialog, "(STUBBED) called"); - return ORBIS_OK; + return 1; } int PS4_SYSV_ABI sceSaveDataDialogOpen() { diff --git a/src/core/libraries/usbd/usbd.cpp b/src/core/libraries/usbd/usbd.cpp index 2a1f6028a..c0e1b7ea8 100644 --- a/src/core/libraries/usbd/usbd.cpp +++ b/src/core/libraries/usbd/usbd.cpp @@ -1,7 +1,6 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -// Generated By moduleGenerator #include "common/logging/log.h" #include "common/singleton.h" #include "core/libraries/error_codes.h" diff --git a/src/core/libraries/usbd/usbd.h b/src/core/libraries/usbd/usbd.h index 6aad31ebc..4ed153587 100644 --- a/src/core/libraries/usbd/usbd.h +++ b/src/core/libraries/usbd/usbd.h @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #pragma once + #include "common/types.h" namespace Core::Loader { diff --git a/src/core/libraries/videoout/buffer.h b/src/core/libraries/videoout/buffer.h index 8f49be591..08552bddb 100644 --- a/src/core/libraries/videoout/buffer.h +++ b/src/core/libraries/videoout/buffer.h @@ -4,6 +4,7 @@ #pragma once #include + #include "common/assert.h" #include "common/types.h" diff --git a/src/core/libraries/videoout/driver.cpp b/src/core/libraries/videoout/driver.cpp index 25de48a4d..91694cfaf 100644 --- a/src/core/libraries/videoout/driver.cpp +++ b/src/core/libraries/videoout/driver.cpp @@ -1,7 +1,8 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include + #include "common/assert.h" #include "common/config.h" #include "common/debug.h" @@ -96,7 +97,7 @@ int VideoOutDriver::RegisterBuffers(VideoOutPort* port, s32 startIndex, void* co } if (attribute->reserved0 != 0 || attribute->reserved1 != 0) { - LOG_ERROR(Lib_VideoOut, "Invalid reserved memebers"); + LOG_ERROR(Lib_VideoOut, "Invalid reserved members"); return ORBIS_VIDEO_OUT_ERROR_INVALID_VALUE; } if (attribute->aspect_ratio != 0) { @@ -252,8 +253,8 @@ void VideoOutDriver::SubmitFlipInternal(VideoOutPort* port, s32 index, s64 flip_ requests.push({ .frame = frame, .port = port, - .index = index, .flip_arg = flip_arg, + .index = index, .eop = is_eop, }); } diff --git a/src/core/libraries/videoout/driver.h b/src/core/libraries/videoout/driver.h index bee800602..6fc74e012 100644 --- a/src/core/libraries/videoout/driver.h +++ b/src/core/libraries/videoout/driver.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -92,8 +92,8 @@ private: struct Request { Vulkan::Frame* frame; VideoOutPort* port; - s32 index; s64 flip_arg; + s32 index; bool eop; operator bool() const noexcept { diff --git a/src/core/libraries/videoout/video_out.cpp b/src/core/libraries/videoout/video_out.cpp index d13062cd4..a6c1a7623 100644 --- a/src/core/libraries/videoout/video_out.cpp +++ b/src/core/libraries/videoout/video_out.cpp @@ -185,14 +185,16 @@ s32 PS4_SYSV_ABI sceVideoOutGetFlipStatus(s32 handle, FlipStatus* status) { return ORBIS_VIDEO_OUT_ERROR_INVALID_HANDLE; } - std::unique_lock lock{port->port_mutex}; - *status = port->flip_status; + { + std::unique_lock lock{port->port_mutex}; + *status = port->flip_status; + } - LOG_INFO(Lib_VideoOut, - "count = {}, processTime = {}, tsc = {}, submitTsc = {}, flipArg = {}, gcQueueNum = " - "{}, flipPendingNum = {}, currentBuffer = {}", - status->count, status->processTime, status->tsc, status->submitTsc, status->flipArg, - status->gcQueueNum, status->flipPendingNum, status->currentBuffer); + LOG_TRACE(Lib_VideoOut, + "count = {}, processTime = {}, tsc = {}, submitTsc = {}, flipArg = {}, gcQueueNum = " + "{}, flipPendingNum = {}, currentBuffer = {}", + status->count, status->processTime, status->tsc, status->submitTsc, status->flipArg, + status->gcQueueNum, status->flipPendingNum, status->currentBuffer); return ORBIS_OK; } diff --git a/src/core/linker.cpp b/src/core/linker.cpp index d4a15825b..0d76f4b9e 100644 --- a/src/core/linker.cpp +++ b/src/core/linker.cpp @@ -10,6 +10,7 @@ #include "common/thread.h" #include "core/aerolib/aerolib.h" #include "core/aerolib/stubs.h" +#include "core/cpu_patches.h" #include "core/libraries/kernel/memory_management.h" #include "core/libraries/kernel/thread_management.h" #include "core/linker.h" @@ -68,14 +69,23 @@ void Linker::Execute() { } // Configure used flexible memory size. - // if (auto* mem_param = GetProcParam()->mem_param) { - // if (u64* flexible_size = mem_param->flexible_memory_size) { - // memory->SetTotalFlexibleSize(*flexible_size); - // } - // } + if (const auto* proc_param = GetProcParam()) { + if (proc_param->size >= + offsetof(OrbisProcParam, mem_param) + sizeof(OrbisKernelMemParam*)) { + if (const auto* mem_param = proc_param->mem_param) { + if (mem_param->size >= + offsetof(OrbisKernelMemParam, flexible_memory_size) + sizeof(u64*)) { + if (const auto* flexible_size = mem_param->flexible_memory_size) { + memory->SetupMemoryRegions(*flexible_size); + } + } + } + } + } // Init primary thread. Common::SetCurrentThreadName("GAME_MainThread"); + InitializeThreadPatchStack(); Libraries::Kernel::pthreadInitSelfMainThread(); InitTlsForThread(true); @@ -96,6 +106,8 @@ void Linker::Execute() { RunMainEntry(m->GetEntryAddress(), &p, ProgramExitFunc); } } + + SetTcbBase(nullptr); } s32 Linker::LoadModule(const std::filesystem::path& elf_name, bool is_dynamic) { @@ -168,7 +180,7 @@ void Linker::Relocate(Module* module) { auto sym_bind = sym.GetBind(); auto sym_type = sym.GetType(); auto sym_visibility = sym.GetVisibility(); - u64 symbol_vitrual_addr = 0; + u64 symbol_virtual_addr = 0; Loader::SymbolRecord symrec{}; switch (sym_type) { case STT_FUN: @@ -185,12 +197,12 @@ void Linker::Relocate(Module* module) { } if (sym_visibility != 0) { - LOG_INFO(Core_Linker, "symbol visilibity !=0"); + LOG_INFO(Core_Linker, "symbol visibility !=0"); } switch (sym_bind) { case STB_LOCAL: - symbol_vitrual_addr = rel_base_virtual_addr + sym.st_value; + symbol_virtual_addr = rel_base_virtual_addr + sym.st_value; module->SetRelaBit(bit_idx); break; case STB_GLOBAL: @@ -200,14 +212,14 @@ void Linker::Relocate(Module* module) { // Only set the rela bit if the symbol was actually resolved and not stubbed. module->SetRelaBit(bit_idx); } - symbol_vitrual_addr = symrec.virtual_address; + symbol_virtual_addr = symrec.virtual_address; break; } default: ASSERT_MSG(0, "unknown bind type {}", sym_bind); } - rel_is_resolved = (symbol_vitrual_addr != 0); - rel_value = (rel_is_resolved ? symbol_vitrual_addr + addend : 0); + rel_is_resolved = (symbol_virtual_addr != 0); + rel_value = (rel_is_resolved ? symbol_virtual_addr + addend : 0); rel_name = symrec.name; break; } diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 9280f63b6..af7d92ff9 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -12,9 +12,11 @@ namespace Core { +constexpr u64 SCE_DEFAULT_FLEXIBLE_MEMORY_SIZE = 448_MB; + MemoryManager::MemoryManager() { - // Insert an area that covers direct memory physical block. - dmem_map.emplace(0, DirectMemoryArea{0, SCE_KERNEL_MAIN_DMEM_SIZE}); + // Set up the direct and flexible memory regions. + SetupMemoryRegions(SCE_DEFAULT_FLEXIBLE_MEMORY_SIZE); // Insert a virtual memory area that covers the entire area we manage. const VAddr system_managed_base = impl.SystemManagedVirtualBase(); @@ -36,6 +38,19 @@ MemoryManager::MemoryManager() { MemoryManager::~MemoryManager() = default; +void MemoryManager::SetupMemoryRegions(u64 flexible_size) { + total_flexible_size = flexible_size; + total_direct_size = SCE_KERNEL_MAIN_DMEM_SIZE - flexible_size; + + // Insert an area that covers direct memory physical block. + // Note that this should never be called after direct memory allocations have been made. + dmem_map.clear(); + dmem_map.emplace(0, DirectMemoryArea{0, total_direct_size}); + + LOG_INFO(Kernel_Vmm, "Configured memory regions: flexible size = {:#x}, direct size = {:#x}", + total_flexible_size, total_direct_size); +} + PAddr MemoryManager::Allocate(PAddr search_start, PAddr search_end, size_t size, u64 alignment, int memory_type) { std::scoped_lock lk{mutex}; @@ -43,12 +58,17 @@ PAddr MemoryManager::Allocate(PAddr search_start, PAddr search_end, size_t size, auto dmem_area = FindDmemArea(search_start); const auto is_suitable = [&] { - return dmem_area->second.is_free && dmem_area->second.size >= size; + const auto aligned_base = alignment > 0 ? Common::AlignUp(dmem_area->second.base, alignment) + : dmem_area->second.base; + const auto alignment_size = aligned_base - dmem_area->second.base; + const auto remaining_size = + dmem_area->second.size >= alignment_size ? dmem_area->second.size - alignment_size : 0; + return dmem_area->second.is_free && remaining_size >= size; }; while (!is_suitable() && dmem_area->second.GetEnd() <= search_end) { dmem_area++; } - ASSERT_MSG(is_suitable(), "Unable to find free direct memory area"); + ASSERT_MSG(is_suitable(), "Unable to find free direct memory area: size = {:#x}", size); // Align free position PAddr free_addr = dmem_area->second.base; @@ -82,7 +102,7 @@ void MemoryManager::Free(PAddr phys_addr, size_t size) { } } for (const auto& [addr, size] : remove_list) { - UnmapMemory(addr, size); + UnmapMemoryImpl(addr, size); } // Mark region as free and attempt to coalesce it with neighbours. @@ -105,7 +125,7 @@ int MemoryManager::Reserve(void** out_addr, VAddr virtual_addr, size_t size, Mem const auto& vma = FindVMA(mapped_addr)->second; // If the VMA is mapped, unmap the region first. if (vma.IsMapped()) { - UnmapMemory(mapped_addr, size); + UnmapMemoryImpl(mapped_addr, size); } const size_t remaining_size = vma.base + vma.size - mapped_addr; ASSERT_MSG(vma.type == VMAType::Free && remaining_size >= size); @@ -214,7 +234,10 @@ int MemoryManager::MapFile(void** out_addr, VAddr virtual_addr, size_t size, Mem void MemoryManager::UnmapMemory(VAddr virtual_addr, size_t size) { std::scoped_lock lk{mutex}; + UnmapMemoryImpl(virtual_addr, size); +} +void MemoryManager::UnmapMemoryImpl(VAddr virtual_addr, size_t size) { const auto it = FindVMA(virtual_addr); const auto& vma_base = it->second; ASSERT_MSG(vma_base.Contains(virtual_addr, size), @@ -243,10 +266,11 @@ void MemoryManager::UnmapMemory(VAddr virtual_addr, size_t size) { vma.disallow_merge = false; vma.name = ""; MergeAdjacent(vma_map, new_it); + bool readonly_file = vma.prot == MemoryProt::CpuRead && type == VMAType::File; // Unmap the memory region. impl.Unmap(vma_base_addr, vma_base_size, start_in_vma, start_in_vma + size, phys_base, is_exec, - has_backing); + has_backing, readonly_file); TRACK_FREE(virtual_addr, "VMEM"); } @@ -397,9 +421,10 @@ int MemoryManager::VirtualQuery(VAddr addr, int flags, const auto& vma = it->second; info->start = vma.base; info->end = vma.base + vma.size; + info->protection = static_cast(vma.prot); info->is_flexible.Assign(vma.type == VMAType::Flexible); info->is_direct.Assign(vma.type == VMAType::Direct); - info->is_commited.Assign(vma.type != VMAType::Free); + info->is_commited.Assign(vma.type != VMAType::Free && vma.type != VMAType::Reserved); vma.name.copy(info->name.data(), std::min(info->name.size(), vma.name.size())); if (vma.type == VMAType::Direct) { const auto dmem_it = FindDmemArea(vma.phys_base); @@ -440,14 +465,24 @@ int MemoryManager::DirectQueryAvailable(PAddr search_start, PAddr search_end, si PAddr paddr{}; size_t max_size{}; while (dmem_area != dmem_map.end() && dmem_area->second.GetEnd() <= search_end) { - if (dmem_area->second.size > max_size) { - paddr = dmem_area->second.base; - max_size = dmem_area->second.size; + if (!dmem_area->second.is_free) { + dmem_area++; + continue; + } + + const auto aligned_base = alignment > 0 ? Common::AlignUp(dmem_area->second.base, alignment) + : dmem_area->second.base; + const auto alignment_size = aligned_base - dmem_area->second.base; + const auto remaining_size = + dmem_area->second.size >= alignment_size ? dmem_area->second.size - alignment_size : 0; + if (remaining_size > max_size) { + paddr = aligned_base; + max_size = remaining_size; } dmem_area++; } - *phys_addr_out = alignment > 0 ? Common::AlignUp(paddr, alignment) : paddr; + *phys_addr_out = paddr; *size_out = max_size; return ORBIS_OK; } @@ -456,7 +491,7 @@ void MemoryManager::NameVirtualRange(VAddr virtual_addr, size_t size, std::strin auto it = FindVMA(virtual_addr); ASSERT_MSG(it->second.Contains(virtual_addr, size), - "Range provided is not fully containted in vma"); + "Range provided is not fully contained in vma"); it->second.name = name; } VAddr MemoryManager::SearchFree(VAddr virtual_addr, size_t size, u32 alignment) { @@ -525,7 +560,8 @@ MemoryManager::DMemHandle MemoryManager::CarveDmemArea(PAddr addr, size_t size) const PAddr start_in_area = addr - area.base; const PAddr end_in_vma = start_in_area + size; - ASSERT_MSG(end_in_vma <= area.size, "Mapping cannot fit inside free region"); + ASSERT_MSG(end_in_vma <= area.size, "Mapping cannot fit inside free region: size = {:#x}", + size); if (end_in_vma != area.size) { // Split VMA at the end of the allocated region diff --git a/src/core/memory.h b/src/core/memory.h index 6502829b1..d0935ffb7 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -131,8 +131,8 @@ public: rasterizer = rasterizer_; } - void SetTotalFlexibleSize(u64 size) { - total_flexible_size = size; + u64 GetTotalDirectSize() const { + return total_direct_size; } u64 GetAvailableFlexibleSize() const { @@ -143,6 +143,8 @@ public: return impl.SystemReservedVirtualBase(); } + void SetupMemoryRegions(u64 flexible_size); + PAddr Allocate(PAddr search_start, PAddr search_end, size_t size, u64 alignment, int memory_type); @@ -218,12 +220,15 @@ private: DMemHandle Split(DMemHandle dmem_handle, size_t offset_in_area); + void UnmapMemoryImpl(VAddr virtual_addr, size_t size); + private: AddressSpace impl; DMemMap dmem_map; VMAMap vma_map; - std::recursive_mutex mutex; - size_t total_flexible_size = 448_MB; + std::mutex mutex; + size_t total_direct_size{}; + size_t total_flexible_size{}; size_t flexible_usage{}; Vulkan::Rasterizer* rasterizer{}; }; diff --git a/src/core/module.cpp b/src/core/module.cpp index 775e1ef19..f48848bbd 100644 --- a/src/core/module.cpp +++ b/src/core/module.cpp @@ -5,6 +5,9 @@ #include "common/alignment.h" #include "common/assert.h" #include "common/logging/log.h" +#ifdef ENABLE_QT_GUI +#include "qt_gui/memory_patcher.h" +#endif #include "common/string_util.h" #include "core/aerolib/aerolib.h" #include "core/cpu_patches.h" @@ -192,6 +195,16 @@ void Module::LoadModuleToMemory(u32& max_tls_index) { const VAddr entry_addr = base_virtual_addr + elf.GetElfEntry(); LOG_INFO(Core_Linker, "program entry addr ..........: {:#018x}", entry_addr); + +#ifdef ENABLE_QT_GUI + if (MemoryPatcher::g_eboot_address == 0) { + if (name == "eboot") { + MemoryPatcher::g_eboot_address = base_virtual_addr; + MemoryPatcher::g_eboot_image_size = base_size; + MemoryPatcher::OnGameLoaded(); + } + } +#endif } void Module::LoadDynamicInfo() { diff --git a/src/core/tls.cpp b/src/core/tls.cpp index 3216d0fe4..4a0cdb0dc 100644 --- a/src/core/tls.cpp +++ b/src/core/tls.cpp @@ -9,7 +9,10 @@ #ifdef _WIN32 #include #elif defined(__APPLE__) -#include +#include +#include +#include +#include #endif namespace Core { @@ -17,11 +20,17 @@ namespace Core { #ifdef _WIN32 static DWORD slot = 0; +static std::once_flag slot_alloc_flag; static void AllocTcbKey() { slot = TlsAlloc(); } +u32 GetTcbKey() { + std::call_once(slot_alloc_flag, &AllocTcbKey); + return slot; +} + void SetTcbBase(void* image_address) { const BOOL result = TlsSetValue(GetTcbKey(), image_address); ASSERT(result != 0); @@ -33,27 +42,98 @@ Tcb* GetTcbBase() { #elif defined(__APPLE__) -static pthread_key_t slot = 0; +// Reserve space in the 32-bit address range for allocating TCB pages. +asm(".zerofill TCB_SPACE,TCB_SPACE,__guest_system,0x3FC000"); -static void AllocTcbKey() { - ASSERT(pthread_key_create(&slot, nullptr) == 0); +static constexpr u64 ldt_region_base = 0x4000; +static constexpr u64 ldt_region_size = 0x3FC000; +static constexpr u16 ldt_block_size = 0x1000; +static constexpr u16 ldt_index_base = 8; +static constexpr u16 ldt_index_total = (ldt_region_size - ldt_region_base) / ldt_block_size; + +static boost::icl::interval_set free_ldts{}; +static std::mutex free_ldts_lock; +static std::once_flag ldt_region_init_flag; + +static u16 GetLdtIndex() { + sel_t selector; + asm volatile("mov %%fs, %0" : "=r"(selector)); + return selector.index; +} + +static void InitLdtRegion() { + const void* result = + mmap(reinterpret_cast(ldt_region_base), ldt_region_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); + ASSERT_MSG(result != MAP_FAILED, "Failed to map memory region for LDT entries."); + + free_ldts += + boost::icl::interval::right_open(ldt_index_base, ldt_index_base + ldt_index_total); +} + +static void** SetupThreadLdt() { + std::call_once(ldt_region_init_flag, InitLdtRegion); + + // Allocate a new LDT index for the current thread. + u16 ldt_index; + { + std::unique_lock lock{free_ldts_lock}; + ASSERT_MSG(!free_ldts.empty(), "Out of LDT space."); + ldt_index = first(*free_ldts.begin()); + free_ldts -= ldt_index; + } + const u64 addr = ldt_region_base + (ldt_index - ldt_index_base) * ldt_block_size; + + // Create an LDT entry for the TCB. + const ldt_entry ldt{.data{ + .base00 = static_cast(addr), + .base16 = static_cast(addr >> 16), + .base24 = static_cast(addr >> 24), + .limit00 = static_cast(ldt_block_size - 1), + .limit16 = 0, + .type = DESC_DATA_WRITE, + .dpl = 3, // User accessible + .present = 1, // Segment present + .stksz = DESC_DATA_32B, + .granular = DESC_GRAN_BYTE, + }}; + int ret = i386_set_ldt(ldt_index, &ldt, 1); + ASSERT_MSG(ret == ldt_index, + "Failed to set LDT for TLS area: expected {}, but syscall returned {}", ldt_index, + ret); + + // Set the FS segment to the created LDT. + const sel_t sel{ + .rpl = USER_PRIV, + .ti = SEL_LDT, + .index = ldt_index, + }; + asm volatile("mov %0, %%fs" ::"r"(sel)); + + return reinterpret_cast(addr); +} + +static void FreeThreadLdt() { + std::unique_lock lock{free_ldts_lock}; + free_ldts += GetLdtIndex(); } void SetTcbBase(void* image_address) { - ASSERT(pthread_setspecific(GetTcbKey(), image_address) == 0); + if (image_address != nullptr) { + *SetupThreadLdt() = image_address; + } else { + FreeThreadLdt(); + } } Tcb* GetTcbBase() { - return reinterpret_cast(pthread_getspecific(GetTcbKey())); + Tcb* tcb; + asm volatile("mov %%fs:0x0, %0" : "=r"(tcb)); + return tcb; } #else -// Placeholder for code compatibility. -static constexpr u32 slot = 0; - -static void AllocTcbKey() {} - void SetTcbBase(void* image_address) { asm volatile("wrgsbase %0" ::"r"(image_address) : "memory"); } @@ -66,11 +146,4 @@ Tcb* GetTcbBase() { #endif -static std::once_flag slot_alloc_flag; - -u32 GetTcbKey() { - std::call_once(slot_alloc_flag, &AllocTcbKey); - return slot; -} - } // namespace Core diff --git a/src/core/tls.h b/src/core/tls.h index 9829c8d9a..f5bf33184 100644 --- a/src/core/tls.h +++ b/src/core/tls.h @@ -22,8 +22,10 @@ struct Tcb { void* tcb_thread; }; +#ifdef _WIN32 /// Gets the thread local storage key for the TCB block. u32 GetTcbKey(); +#endif /// Sets the data pointer to the TCB block. void SetTcbBase(void* image_address); diff --git a/src/emulator.cpp b/src/emulator.cpp index 37e227ddf..85a4d745a 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -7,6 +7,9 @@ #include "common/debug.h" #include "common/logging/backend.h" #include "common/logging/log.h" +#ifdef ENABLE_QT_GUI +#include "qt_gui/memory_patcher.h" +#endif #include "common/ntapi.h" #include "common/path_util.h" #include "common/polyfill_thread.h" @@ -19,7 +22,6 @@ #include "core/file_sys/fs.h" #include "core/libraries/disc_map/disc_map.h" #include "core/libraries/kernel/thread_management.h" -#include "core/libraries/libc/libc.h" #include "core/libraries/libc_internal/libc_internal.h" #include "core/libraries/libs.h" #include "core/libraries/ngs2/ngs2.h" @@ -52,6 +54,18 @@ Emulator::Emulator() { LOG_INFO(Loader, "Branch {}", Common::g_scm_branch); LOG_INFO(Loader, "Description {}", Common::g_scm_desc); + LOG_INFO(Config, "General isNeo: {}", Config::isNeoMode()); + LOG_INFO(Config, "GPU isNullGpu: {}", Config::nullGpu()); + LOG_INFO(Config, "GPU shouldDumpShaders: {}", Config::dumpShaders()); + LOG_INFO(Config, "GPU shouldDumpPM4: {}", Config::dumpPM4()); + LOG_INFO(Config, "GPU vblankDivider: {}", Config::vblankDiv()); + LOG_INFO(Config, "Vulkan gpuId: {}", Config::getGpuId()); + LOG_INFO(Config, "Vulkan vkValidation: {}", Config::vkValidationEnabled()); + LOG_INFO(Config, "Vulkan vkValidationSync: {}", Config::vkValidationSyncEnabled()); + LOG_INFO(Config, "Vulkan vkValidationGpu: {}", Config::vkValidationGpuEnabled()); + LOG_INFO(Config, "Vulkan rdocEnable: {}", Config::isRdocEnabled()); + LOG_INFO(Config, "Vulkan rdocMarkersEnable: {}", Config::isMarkersEnabled()); + // Defer until after logging is initialized. memory = Core::Memory::Instance(); controller = Common::Singleton::Instance(); @@ -70,6 +84,8 @@ void Emulator::Run(const std::filesystem::path& file) { // Applications expect to be run from /app0 so mount the file's parent path as app0. auto* mnt = Common::Singleton::Instance(); mnt->Mount(file.parent_path(), "/app0"); + // Certain games may use /hostapp as well such as CUSA001100 + mnt->Mount(file.parent_path(), "/hostapp"); // Loading param.sfo file if exists std::string id; @@ -82,6 +98,9 @@ void Emulator::Run(const std::filesystem::path& file) { auto* param_sfo = Common::Singleton::Instance(); param_sfo->open(sce_sys_folder.string() + "/param.sfo", {}); id = std::string(param_sfo->GetString("CONTENT_ID"), 7, 9); +#ifdef ENABLE_QT_GUI + MemoryPatcher::g_game_serial = id; +#endif title = param_sfo->GetString("TITLE"); LOG_INFO(Loader, "Game id: {} Title: {}", id, title); u32 fw_version = param_sfo->GetInteger("SYSTEM_VER"); @@ -154,23 +173,14 @@ void Emulator::Run(const std::filesystem::path& file) { // check if we have system modules to load LoadSystemModules(file); - // Check if there is a libc.prx in sce_module folder - bool found = false; - if (Config::isLleLibc()) { - std::filesystem::path sce_module_folder = file.parent_path() / "sce_module"; - if (std::filesystem::is_directory(sce_module_folder)) { - for (const auto& entry : std::filesystem::directory_iterator(sce_module_folder)) { - if (entry.path().filename() == "libc.prx") { - found = true; - } - LOG_INFO(Loader, "Loading {}", entry.path().string().c_str()); - linker->LoadModule(entry.path()); - } + // Load all prx from game's sce_module folder + std::filesystem::path sce_module_folder = file.parent_path() / "sce_module"; + if (std::filesystem::is_directory(sce_module_folder)) { + for (const auto& entry : std::filesystem::directory_iterator(sce_module_folder)) { + LOG_INFO(Loader, "Loading {}", entry.path().string().c_str()); + linker->LoadModule(entry.path()); } } - if (!found) { - Libraries::LibC::libcSymbolsRegister(&linker->GetHLESymbols()); - } // start execution std::jthread mainthread = diff --git a/src/input/controller.cpp b/src/input/controller.cpp index 4a3db1633..4de6d83b8 100644 --- a/src/input/controller.cpp +++ b/src/input/controller.cpp @@ -132,15 +132,29 @@ bool GameController::SetVibration(u8 smallMotor, u8 largeMotor) { return true; } +void GameController::SetTouchpadState(int touchIndex, bool touchDown, float x, float y) { + if (touchIndex < 2) { + std::scoped_lock lock{m_mutex}; + auto state = GetLastState(); + state.time = Libraries::Kernel::sceKernelGetProcessTime(); + + state.touchpad[touchIndex].state = touchDown; + state.touchpad[touchIndex].x = static_cast(x * 1920); + state.touchpad[touchIndex].y = static_cast(y * 941); + + AddState(state); + } +} + void GameController::TryOpenSDLController() { if (m_sdl_gamepad == nullptr || !SDL_GamepadConnected(m_sdl_gamepad)) { int gamepad_count; SDL_JoystickID* gamepads = SDL_GetGamepads(&gamepad_count); m_sdl_gamepad = gamepad_count > 0 ? SDL_OpenGamepad(gamepads[0]) : nullptr; SDL_free(gamepads); - } - SetLightBarRGB(0, 0, 255); + SetLightBarRGB(0, 0, 255); + } } } // namespace Input diff --git a/src/input/controller.h b/src/input/controller.h index ef0991568..0a0d663a1 100644 --- a/src/input/controller.h +++ b/src/input/controller.h @@ -21,10 +21,17 @@ enum class Axis { AxisMax }; +struct TouchpadEntry { + bool state{}; + u16 x{}; + u16 y{}; +}; + struct State { u32 buttonsState = 0; u64 time = 0; int axes[static_cast(Axis::AxisMax)] = {128, 128, 128, 128, 0, 0}; + TouchpadEntry touchpad[2] = {{false, 0, 0}, {false, 0, 0}}; }; inline int GetAxis(int min, int max, int value) { @@ -47,6 +54,7 @@ public: void Axis(int id, Input::Axis axis, int value); void SetLightBarRGB(u8 r, u8 g, u8 b); bool SetVibration(u8 smallMotor, u8 largeMotor); + void SetTouchpadState(int touchIndex, bool touchDown, float x, float y); void TryOpenSDLController(); private: diff --git a/src/qt_gui/about_dialog.ui b/src/qt_gui/about_dialog.ui index 2b60476b8..e2e76f4c4 100644 --- a/src/qt_gui/about_dialog.ui +++ b/src/qt_gui/about_dialog.ui @@ -40,9 +40,6 @@ true - - true - @@ -106,5 +103,6 @@ + diff --git a/src/qt_gui/cheats_patches.cpp b/src/qt_gui/cheats_patches.cpp new file mode 100644 index 000000000..f7326192c --- /dev/null +++ b/src/qt_gui/cheats_patches.cpp @@ -0,0 +1,1265 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cheats_patches.h" +#include "common/path_util.h" +#include "core/module.h" +#include "qt_gui/memory_patcher.h" + +using namespace Common::FS; + +CheatsPatches::CheatsPatches(const QString& gameName, const QString& gameSerial, + const QString& gameVersion, const QString& gameSize, + const QPixmap& gameImage, QWidget* parent) + : QWidget(parent), m_gameName(gameName), m_gameSerial(gameSerial), m_gameVersion(gameVersion), + m_gameSize(gameSize), m_gameImage(gameImage), manager(new QNetworkAccessManager(this)) { + setupUI(); + resize(500, 400); + setWindowTitle(tr("Cheats / Patches")); +} + +CheatsPatches::~CheatsPatches() {} + +void CheatsPatches::setupUI() { + defaultTextEdit = tr("defaultTextEdit_MSG"); + defaultTextEdit.replace("\\n", "\n"); + + QString CHEATS_DIR_QString = + QString::fromStdString(Common::FS::GetUserPath(Common::FS::PathType::CheatsDir).string()); + QString NameCheatJson = m_gameSerial + "_" + m_gameVersion + ".json"; + m_cheatFilePath = CHEATS_DIR_QString + "/" + NameCheatJson; + + QHBoxLayout* mainLayout = new QHBoxLayout(this); + + // Create the game info group box + QGroupBox* gameInfoGroupBox = new QGroupBox(); + QVBoxLayout* gameInfoLayout = new QVBoxLayout(gameInfoGroupBox); + gameInfoLayout->setAlignment(Qt::AlignTop); + + QLabel* gameImageLabel = new QLabel(); + if (!m_gameImage.isNull()) { + gameImageLabel->setPixmap(m_gameImage.scaled(275, 275, Qt::KeepAspectRatio)); + } else { + gameImageLabel->setText(tr("No Image Available")); + } + gameImageLabel->setAlignment(Qt::AlignCenter); + gameInfoLayout->addWidget(gameImageLabel, 0, Qt::AlignCenter); + + QLabel* gameNameLabel = new QLabel(m_gameName); + gameNameLabel->setAlignment(Qt::AlignLeft); + gameNameLabel->setWordWrap(true); + gameInfoLayout->addWidget(gameNameLabel); + + QLabel* gameSerialLabel = new QLabel(tr("Serial: ") + m_gameSerial); + gameSerialLabel->setAlignment(Qt::AlignLeft); + gameInfoLayout->addWidget(gameSerialLabel); + + QLabel* gameVersionLabel = new QLabel(tr("Version: ") + m_gameVersion); + gameVersionLabel->setAlignment(Qt::AlignLeft); + gameInfoLayout->addWidget(gameVersionLabel); + + QLabel* gameSizeLabel = new QLabel(tr("Size: ") + m_gameSize); + gameSizeLabel->setAlignment(Qt::AlignLeft); + gameInfoLayout->addWidget(gameSizeLabel); + + // Add a text area for instructions and 'Patch' descriptions + instructionsTextEdit = new QTextEdit(); + instructionsTextEdit->setText(defaultTextEdit); + instructionsTextEdit->setReadOnly(true); + instructionsTextEdit->setFixedHeight(290); + gameInfoLayout->addWidget(instructionsTextEdit); + + // Create the tab widget + QTabWidget* tabWidget = new QTabWidget(); + QWidget* cheatsTab = new QWidget(); + QWidget* patchesTab = new QWidget(); + + // Layouts for the tabs + QVBoxLayout* cheatsLayout = new QVBoxLayout(); + QVBoxLayout* patchesLayout = new QVBoxLayout(); + + // Setup the cheats tab + QGroupBox* cheatsGroupBox = new QGroupBox(); + rightLayout = new QVBoxLayout(cheatsGroupBox); + rightLayout->setAlignment(Qt::AlignTop); + + cheatsGroupBox->setLayout(rightLayout); + QScrollArea* scrollArea = new QScrollArea(); + scrollArea->setWidgetResizable(true); + scrollArea->setWidget(cheatsGroupBox); + scrollArea->setMinimumHeight(490); + cheatsLayout->addWidget(scrollArea); + + // QListView + listView_selectFile = new QListView(); + listView_selectFile->setSelectionMode(QAbstractItemView::SingleSelection); + listView_selectFile->setEditTriggers(QAbstractItemView::NoEditTriggers); + + // Add QListView to layout + QVBoxLayout* fileListLayout = new QVBoxLayout(); + fileListLayout->addWidget(new QLabel(tr("Select Cheat File:"))); + fileListLayout->addWidget(listView_selectFile); + cheatsLayout->addLayout(fileListLayout, 2); + + // Call the method to fill the list of cheat files + populateFileListCheats(); + + QLabel* repositoryLabel = new QLabel(tr("Repository:")); + repositoryLabel->setAlignment(Qt::AlignLeft); + repositoryLabel->setAlignment(Qt::AlignVCenter); + + // Add a combo box and a download button + QHBoxLayout* controlLayout = new QHBoxLayout(); + controlLayout->addWidget(repositoryLabel); + controlLayout->setAlignment(Qt::AlignLeft); + QComboBox* downloadComboBox = new QComboBox(); + + downloadComboBox->addItem("wolf2022", "wolf2022"); + downloadComboBox->addItem("GoldHEN", "GoldHEN"); + downloadComboBox->addItem("shadPS4", "shadPS4"); + + controlLayout->addWidget(downloadComboBox); + + QPushButton* downloadButton = new QPushButton(tr("Download Cheats")); + connect(downloadButton, &QPushButton::clicked, [=]() { + QString source = downloadComboBox->currentData().toString(); + downloadCheats(source, m_gameSerial, m_gameVersion, true); + }); + + QPushButton* deleteCheatButton = new QPushButton(tr("Delete File")); + connect(deleteCheatButton, &QPushButton::clicked, [=]() { + QStringListModel* model = qobject_cast(listView_selectFile->model()); + if (!model) { + return; + } + QItemSelectionModel* selectionModel = listView_selectFile->selectionModel(); + if (!selectionModel) { + return; + } + QModelIndexList selectedIndexes = selectionModel->selectedIndexes(); + if (selectedIndexes.isEmpty()) { + QMessageBox::warning( + this, tr("Delete File"), + tr("No files selected.") + "\n" + + tr("You can delete the cheats you don't want after downloading them.")); + return; + } + QModelIndex selectedIndex = selectedIndexes.first(); + QString selectedFileName = model->data(selectedIndex).toString(); + + int ret = QMessageBox::warning( + this, tr("Delete File"), + QString(tr("Do you want to delete the selected file?\\n%1").replace("\\n", "\n")) + .arg(selectedFileName), + QMessageBox::Yes | QMessageBox::No); + + if (ret == QMessageBox::Yes) { + QString filePath = CHEATS_DIR_QString + "/" + selectedFileName; + QFile::remove(filePath); + populateFileListCheats(); + } + }); + + controlLayout->addWidget(downloadButton); + controlLayout->addWidget(deleteCheatButton); + + cheatsLayout->addLayout(controlLayout); + cheatsTab->setLayout(cheatsLayout); + + // Setup the patches tab + QGroupBox* patchesGroupBox = new QGroupBox(); + patchesGroupBoxLayout = new QVBoxLayout(patchesGroupBox); + patchesGroupBoxLayout->setAlignment(Qt::AlignTop); + patchesGroupBox->setLayout(patchesGroupBoxLayout); + + QScrollArea* patchesScrollArea = new QScrollArea(); + patchesScrollArea->setWidgetResizable(true); + patchesScrollArea->setWidget(patchesGroupBox); + patchesScrollArea->setMinimumHeight(490); + patchesLayout->addWidget(patchesScrollArea); + + // List of files in patchesListView + patchesListView = new QListView(); + patchesListView->setSelectionMode(QAbstractItemView::SingleSelection); + patchesListView->setEditTriggers(QAbstractItemView::NoEditTriggers); + + // Add new label "Select Patch File:" above the QListView + QVBoxLayout* patchFileListLayout = new QVBoxLayout(); + patchFileListLayout->addWidget(new QLabel(tr("Select Patch File:"))); + patchFileListLayout->addWidget(patchesListView); + patchesLayout->addLayout(patchFileListLayout, 2); + + QStringListModel* patchesModel = new QStringListModel(); + patchesListView->setModel(patchesModel); + + QHBoxLayout* patchesControlLayout = new QHBoxLayout(); + + QLabel* patchesRepositoryLabel = new QLabel(tr("Repository:")); + patchesRepositoryLabel->setAlignment(Qt::AlignLeft); + patchesRepositoryLabel->setAlignment(Qt::AlignVCenter); + patchesControlLayout->addWidget(patchesRepositoryLabel); + + // Add the combo box with options + patchesComboBox = new QComboBox(); + patchesComboBox->addItem("GoldHEN", "GoldHEN"); + patchesComboBox->addItem("shadPS4", "shadPS4"); + patchesControlLayout->addWidget(patchesComboBox); + + QPushButton* patchesButton = new QPushButton(tr("Download Patches")); + connect(patchesButton, &QPushButton::clicked, [=]() { + QString selectedOption = patchesComboBox->currentData().toString(); + downloadPatches(selectedOption, true); + }); + patchesControlLayout->addWidget(patchesButton); + + QPushButton* saveButton = new QPushButton(tr("Save")); + connect(saveButton, &QPushButton::clicked, this, &CheatsPatches::onSaveButtonClicked); + + patchesControlLayout->addWidget(saveButton); + + patchesLayout->addLayout(patchesControlLayout); + patchesTab->setLayout(patchesLayout); + + tabWidget->addTab(cheatsTab, tr("Cheats")); + tabWidget->addTab(patchesTab, tr("Patches")); + + connect(tabWidget, &QTabWidget::currentChanged, this, [this](int index) { + if (index == 1) { + populateFileListPatches(); + } + }); + + mainLayout->addWidget(gameInfoGroupBox, 1); + mainLayout->addWidget(tabWidget, 3); + + manager = new QNetworkAccessManager(this); + + setLayout(mainLayout); +} + +void CheatsPatches::onSaveButtonClicked() { + // Get the name of the selected folder in the patchesListView + QString selectedPatchName; + QModelIndexList selectedIndexes = patchesListView->selectionModel()->selectedIndexes(); + if (selectedIndexes.isEmpty()) { + QMessageBox::warning(this, tr("Error"), tr("No patch selected.")); + return; + } + selectedPatchName = patchesListView->model()->data(selectedIndexes.first()).toString(); + int separatorIndex = selectedPatchName.indexOf(" | "); + selectedPatchName = selectedPatchName.mid(separatorIndex + 3); + + QString patchDir = + QString::fromStdString(Common::FS::GetUserPath(Common::FS::PathType::PatchesDir).string()) + + "/" + selectedPatchName; + + QString filesJsonPath = patchDir + "/files.json"; + QFile jsonFile(filesJsonPath); + if (!jsonFile.open(QIODevice::ReadOnly)) { + QMessageBox::critical(this, tr("Error"), tr("Unable to open files.json for reading.")); + return; + } + + QByteArray jsonData = jsonFile.readAll(); + jsonFile.close(); + + QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData); + QJsonObject jsonObject = jsonDoc.object(); + + QString selectedFileName; + QString serial = m_gameSerial; + + for (auto it = jsonObject.constBegin(); it != jsonObject.constEnd(); ++it) { + QString filePath = it.key(); + QJsonArray idsArray = it.value().toArray(); + + if (idsArray.contains(QJsonValue(serial))) { + selectedFileName = filePath; + break; + } + } + + if (selectedFileName.isEmpty()) { + QMessageBox::critical(this, tr("Error"), tr("No patch file found for the current serial.")); + return; + } + + QString filePath = patchDir + "/" + selectedFileName; + QFile file(filePath); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + QMessageBox::critical(this, tr("Error"), tr("Unable to open the file for reading.")); + return; + } + + QByteArray xmlData = file.readAll(); + file.close(); + + QString newXmlData; + QXmlStreamWriter xmlWriter(&newXmlData); + xmlWriter.setAutoFormatting(true); + xmlWriter.writeStartDocument(); + + QXmlStreamReader xmlReader(xmlData); + bool insideMetadata = false; + + while (!xmlReader.atEnd()) { + xmlReader.readNext(); + + if (xmlReader.isStartElement()) { + if (xmlReader.name() == QStringLiteral("Metadata")) { + insideMetadata = true; + xmlWriter.writeStartElement(xmlReader.name().toString()); + + QString name = xmlReader.attributes().value("Name").toString(); + bool isEnabled = false; + bool hasIsEnabled = false; + bool foundPatchInfo = false; + + // Check and update the isEnabled attribute + for (const QXmlStreamAttribute& attr : xmlReader.attributes()) { + if (attr.name() == QStringLiteral("isEnabled")) { + hasIsEnabled = true; + auto it = m_patchInfos.find(name); + if (it != m_patchInfos.end()) { + QCheckBox* checkBox = findCheckBoxByName(it->name); + if (checkBox) { + foundPatchInfo = true; + isEnabled = checkBox->isChecked(); + xmlWriter.writeAttribute("isEnabled", isEnabled ? "true" : "false"); + } + } + if (!foundPatchInfo) { + auto maskIt = m_patchInfos.find(name + " (any version)"); + if (maskIt != m_patchInfos.end()) { + QCheckBox* checkBox = findCheckBoxByName(maskIt->name); + if (checkBox) { + foundPatchInfo = true; + isEnabled = checkBox->isChecked(); + xmlWriter.writeAttribute("isEnabled", + isEnabled ? "true" : "false"); + } + } + } + + } else { + xmlWriter.writeAttribute(attr.name().toString(), attr.value().toString()); + } + } + + if (!hasIsEnabled) { + auto it = m_patchInfos.find(name); + if (it != m_patchInfos.end()) { + QCheckBox* checkBox = findCheckBoxByName(it->name); + if (checkBox) { + foundPatchInfo = true; + isEnabled = checkBox->isChecked(); + } + } + if (!foundPatchInfo) { + auto maskIt = m_patchInfos.find(name + " (any version)"); + if (maskIt != m_patchInfos.end()) { + QCheckBox* checkBox = findCheckBoxByName(maskIt->name); + if (checkBox) { + foundPatchInfo = true; + isEnabled = checkBox->isChecked(); + } + } + } + xmlWriter.writeAttribute("isEnabled", isEnabled ? "true" : "false"); + } + } else { + xmlWriter.writeStartElement(xmlReader.name().toString()); + for (const QXmlStreamAttribute& attr : xmlReader.attributes()) { + xmlWriter.writeAttribute(attr.name().toString(), attr.value().toString()); + } + } + } else if (xmlReader.isEndElement()) { + if (xmlReader.name() == QStringLiteral("Metadata")) { + insideMetadata = false; + } + xmlWriter.writeEndElement(); + } else if (xmlReader.isCharacters() && !xmlReader.isWhitespace()) { + xmlWriter.writeCharacters(xmlReader.text().toString()); + } + } + + xmlWriter.writeEndDocument(); + + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + QMessageBox::critical(this, tr("Error"), tr("Unable to open the file for writing.")); + return; + } + + QTextStream textStream(&file); + textStream << newXmlData; + file.close(); + + if (xmlReader.hasError()) { + QMessageBox::critical(this, tr("Error"), + tr("Failed to parse XML: ") + "\n" + xmlReader.errorString()); + } else { + QMessageBox::information(this, tr("Success"), tr("Options saved successfully.")); + } +} + +QCheckBox* CheatsPatches::findCheckBoxByName(const QString& name) { + for (int i = 0; i < patchesGroupBoxLayout->count(); ++i) { + QLayoutItem* item = patchesGroupBoxLayout->itemAt(i); + if (item) { + QWidget* widget = item->widget(); + QCheckBox* checkBox = qobject_cast(widget); + if (checkBox) { + if (checkBox->text().toStdString().find(name.toStdString()) != std::string::npos) { + return checkBox; + } + } + } + } + return nullptr; +} + +void CheatsPatches::downloadCheats(const QString& source, const QString& m_gameSerial, + const QString& m_gameVersion, const bool showMessageBox) { + QDir dir(Common::FS::GetUserPath(Common::FS::PathType::CheatsDir)); + if (!dir.exists()) { + dir.mkpath("."); + } + + QString url; + if (source == "GoldHEN") { + url = "https://raw.githubusercontent.com/GoldHEN/GoldHEN_Cheat_Repository/main/json.txt"; + } else if (source == "wolf2022") { + url = "https://wolf2022.ir/trainer/" + m_gameSerial + "_" + m_gameVersion + ".json"; + } else if (source == "shadPS4") { + url = "https://raw.githubusercontent.com/shadps4-emu/ps4_cheats/main/" + "CHEATS_JSON.txt"; + } else { + QMessageBox::warning(this, tr("Invalid Source"), + QString(tr("The selected source is invalid.") + "\n%1").arg(source)); + return; + } + + QNetworkRequest request(url); + QNetworkReply* reply = manager->get(request); + + connect(reply, &QNetworkReply::finished, [=]() { + if (reply->error() == QNetworkReply::NoError) { + QByteArray jsonData = reply->readAll(); + bool foundFiles = false; + + if (source == "GoldHEN" || source == "shadPS4") { + QString textContent(jsonData); + QRegularExpression regex( + QString("%1_%2[^=]*\.json").arg(m_gameSerial).arg(m_gameVersion)); + QRegularExpressionMatchIterator matches = regex.globalMatch(textContent); + QString baseUrl; + + if (source == "GoldHEN") { + baseUrl = "https://raw.githubusercontent.com/GoldHEN/GoldHEN_Cheat_Repository/" + "main/json/"; + } else { + baseUrl = "https://raw.githubusercontent.com/shadps4-emu/ps4_cheats/" + "main/CHEATS/"; + } + + while (matches.hasNext()) { + QRegularExpressionMatch match = matches.next(); + QString fileName = match.captured(0); + + if (!fileName.isEmpty()) { + QString newFileName = fileName; + int dotIndex = newFileName.lastIndexOf('.'); + if (dotIndex != -1) { + + if (source == "GoldHEN") { + newFileName.insert(dotIndex, "_GoldHEN"); + } else { + newFileName.insert(dotIndex, "_shadPS4"); + } + } + QString fileUrl = baseUrl + fileName; + QString localFilePath = dir.filePath(newFileName); + + if (QFile::exists(localFilePath) && showMessageBox) { + QMessageBox::StandardButton reply; + reply = QMessageBox::question( + this, tr("File Exists"), + tr("File already exists. Do you want to replace it?"), + QMessageBox::Yes | QMessageBox::No); + if (reply == QMessageBox::No) { + continue; + } + } + QNetworkRequest fileRequest(fileUrl); + QNetworkReply* fileReply = manager->get(fileRequest); + + connect(fileReply, &QNetworkReply::finished, [=]() { + if (fileReply->error() == QNetworkReply::NoError) { + QByteArray fileData = fileReply->readAll(); + QFile localFile(localFilePath); + if (localFile.open(QIODevice::WriteOnly)) { + localFile.write(fileData); + localFile.close(); + } else { + QMessageBox::warning( + this, tr("Error"), + QString(tr("Failed to save file:") + "\n%1") + .arg(localFilePath)); + } + } else { + QMessageBox::warning(this, tr("Error"), + QString(tr("Failed to download file:") + + "%1\n\n" + tr("Error:") + "%2") + .arg(fileUrl) + .arg(fileReply->errorString())); + } + fileReply->deleteLater(); + }); + + foundFiles = true; + } + } + if (!foundFiles && showMessageBox) { + QMessageBox::warning(this, tr("Cheats Not Found"), tr("CheatsNotFound_MSG")); + } + } else if (source == "wolf2022") { + QString fileName = QFileInfo(QUrl(url).path()).fileName(); + QString baseFileName = fileName; + int dotIndex = baseFileName.lastIndexOf('.'); + if (dotIndex != -1) { + baseFileName.insert(dotIndex, "_wolf2022"); + } + QString filePath = + QString::fromStdString( + Common::FS::GetUserPath(Common::FS::PathType::CheatsDir).string()) + + "/" + baseFileName; + if (QFile::exists(filePath) && showMessageBox) { + QMessageBox::StandardButton reply2; + reply2 = + QMessageBox::question(this, tr("File Exists"), + tr("File already exists. Do you want to replace it?"), + QMessageBox::Yes | QMessageBox::No); + if (reply2 == QMessageBox::No) { + reply->deleteLater(); + return; + } + } + QFile cheatFile(filePath); + if (cheatFile.open(QIODevice::WriteOnly)) { + cheatFile.write(jsonData); + cheatFile.close(); + foundFiles = true; + populateFileListCheats(); + } else { + QMessageBox::warning( + this, tr("Error"), + QString(tr("Failed to save file:") + "\n%1").arg(filePath)); + } + } + if (foundFiles && showMessageBox) { + QMessageBox::information(this, tr("Cheats Downloaded Successfully"), + tr("CheatsDownloadedSuccessfully_MSG")); + populateFileListCheats(); + } + + } else { + if (showMessageBox) { + QMessageBox::warning(this, tr("Cheats Not Found"), tr("CheatsNotFound_MSG")); + } + } + reply->deleteLater(); + emit downloadFinished(); + }); + + // connect(reply, &QNetworkReply::errorOccurred, [=](QNetworkReply::NetworkError code) { + // if (showMessageBox) + // QMessageBox::warning(this, "Download Error", + // QString("Error in response: %1").arg(reply->errorString())); + // }); +} + +void CheatsPatches::populateFileListPatches() { + QLayoutItem* item; + while ((item = patchesGroupBoxLayout->takeAt(0)) != nullptr) { + delete item->widget(); + delete item; + } + m_patchInfos.clear(); + + QString patchesDir = + QString::fromStdString(Common::FS::GetUserPath(Common::FS::PathType::PatchesDir).string()); + QDir dir(patchesDir); + + QStringList folders = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); + QStringList matchingFiles; + + foreach (const QString& folder, folders) { + QString folderPath = dir.filePath(folder); + QDir subDir(folderPath); + + QString filesJsonPath = subDir.filePath("files.json"); + QFile file(filesJsonPath); + + if (file.open(QIODevice::ReadOnly)) { + QByteArray fileData = file.readAll(); + file.close(); + + QJsonDocument jsonDoc(QJsonDocument::fromJson(fileData)); + QJsonObject jsonObj = jsonDoc.object(); + + for (auto it = jsonObj.constBegin(); it != jsonObj.constEnd(); ++it) { + QString fileName = it.key(); + QJsonArray serials = it.value().toArray(); + + if (serials.contains(QJsonValue(m_gameSerial))) { + QString fileEntry = fileName + " | " + folder; + if (!matchingFiles.contains(fileEntry)) { + matchingFiles << fileEntry; + } + } + } + } + } + QStringListModel* model = new QStringListModel(matchingFiles, this); + patchesListView->setModel(model); + + connect( + patchesListView->selectionModel(), &QItemSelectionModel::selectionChanged, this, [this]() { + QModelIndexList selectedIndexes = patchesListView->selectionModel()->selectedIndexes(); + if (!selectedIndexes.isEmpty()) { + QString selectedText = selectedIndexes.first().data().toString(); + addPatchesToLayout(selectedText); + } + }); + + if (!matchingFiles.isEmpty()) { + QModelIndex firstIndex = model->index(0, 0); + patchesListView->selectionModel()->select(firstIndex, QItemSelectionModel::Select | + QItemSelectionModel::Rows); + patchesListView->setCurrentIndex(firstIndex); + } +} + +void CheatsPatches::downloadPatches(const QString repository, const bool showMessageBox) { + QString url; + if (repository == "GoldHEN") { + url = "https://github.com/GoldHEN/GoldHEN_Patch_Repository/tree/main/" + "patches/xml"; + } + if (repository == "shadPS4") { + url = "https://github.com/shadps4-emu/ps4_cheats/tree/main/" + "PATCHES"; + } + QNetworkAccessManager* manager = new QNetworkAccessManager(this); + QNetworkRequest request(url); + QNetworkReply* reply = manager->get(request); + + connect(reply, &QNetworkReply::finished, [=]() { + if (reply->error() == QNetworkReply::NoError) { + QByteArray htmlData = reply->readAll(); + reply->deleteLater(); + + // Parsear HTML e extrair JSON usando QRegularExpression + QString htmlString = QString::fromUtf8(htmlData); + QRegularExpression jsonRegex( + R"()"); + QRegularExpressionMatch match = jsonRegex.match(htmlString); + + if (match.hasMatch()) { + QByteArray jsonData = match.captured(1).toUtf8(); + QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData); + QJsonObject jsonObj = jsonDoc.object(); + QJsonArray itemsArray = + jsonObj["payload"].toObject()["tree"].toObject()["items"].toArray(); + + QDir dir(Common::FS::GetUserPath(Common::FS::PathType::PatchesDir)); + QString fullPath = dir.filePath(repository); + if (!dir.exists(fullPath)) { + dir.mkpath(fullPath); + } + dir.setPath(fullPath); + + foreach (const QJsonValue& value, itemsArray) { + QJsonObject fileObj = value.toObject(); + QString fileName = fileObj["name"].toString(); + QString filePath = fileObj["path"].toString(); + + if (fileName.endsWith(".xml")) { + QString fileUrl; + if (repository == "GoldHEN") { + fileUrl = QString("https://raw.githubusercontent.com/GoldHEN/" + "GoldHEN_Patch_Repository/main/%1") + .arg(filePath); + } + if (repository == "shadPS4") { + fileUrl = QString("https://raw.githubusercontent.com/shadps4-emu/" + "ps4_cheats/main/%1") + .arg(filePath); + } + QNetworkRequest fileRequest(fileUrl); + QNetworkReply* fileReply = manager->get(fileRequest); + + connect(fileReply, &QNetworkReply::finished, [=]() { + if (fileReply->error() == QNetworkReply::NoError) { + QByteArray fileData = fileReply->readAll(); + QFile localFile(dir.filePath(fileName)); + if (localFile.open(QIODevice::WriteOnly)) { + localFile.write(fileData); + localFile.close(); + } else { + if (showMessageBox) { + QMessageBox::warning( + this, tr("Error"), + QString(tr("Failed to save:") + "\n%1").arg(fileName)); + } + } + } else { + if (showMessageBox) { + QMessageBox::warning( + this, tr("Error"), + QString(tr("Failed to download:") + "\n%1").arg(fileUrl)); + } + } + fileReply->deleteLater(); + }); + } + } + if (showMessageBox) { + QMessageBox::information(this, tr("Download Complete"), + QString(tr("DownloadComplete_MSG"))); + } + + // Create the files.json file with the identification of which file to open + createFilesJson(repository); + populateFileListPatches(); + + } else { + if (showMessageBox) { + QMessageBox::warning(this, tr("Error"), + tr("Failed to parse JSON data from HTML.")); + } + } + } else { + if (showMessageBox) { + QMessageBox::warning(this, tr("Error"), tr("Failed to retrieve HTML page.")); + } + } + emit downloadFinished(); + }); +} + +void CheatsPatches::createFilesJson(const QString& repository) { + + QDir dir(Common::FS::GetUserPath(Common::FS::PathType::PatchesDir)); + QString fullPath = dir.filePath(repository); + if (!dir.exists(fullPath)) { + dir.mkpath(fullPath); + } + dir.setPath(fullPath); + + QJsonObject filesObject; + QStringList xmlFiles = dir.entryList(QStringList() << "*.xml", QDir::Files); + + foreach (const QString& xmlFile, xmlFiles) { + QFile file(dir.filePath(xmlFile)); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + QMessageBox::warning(this, tr("ERROR"), + QString(tr("Failed to open file:") + "\n%1").arg(xmlFile)); + continue; + } + + QXmlStreamReader xmlReader(&file); + QJsonArray titleIdsArray; + + while (!xmlReader.atEnd() && !xmlReader.hasError()) { + QXmlStreamReader::TokenType token = xmlReader.readNext(); + if (token == QXmlStreamReader::StartElement) { + if (xmlReader.name() == QStringLiteral("ID")) { + titleIdsArray.append(xmlReader.readElementText()); + } + } + } + + if (xmlReader.hasError()) { + QMessageBox::warning(this, tr("ERROR"), + QString(tr("XML ERROR:") + "\n%1").arg(xmlReader.errorString())); + } + filesObject[xmlFile] = titleIdsArray; + } + + QFile jsonFile(dir.absolutePath() + "/files.json"); + if (!jsonFile.open(QIODevice::WriteOnly)) { + QMessageBox::warning(this, tr("ERROR"), tr("Failed to open files.json for writing")); + return; + } + + QJsonDocument jsonDoc(filesObject); + jsonFile.write(jsonDoc.toJson()); + jsonFile.close(); +} + +void CheatsPatches::addCheatsToLayout(const QJsonArray& modsArray, const QJsonArray& creditsArray) { + QLayoutItem* item; + while ((item = rightLayout->takeAt(0)) != nullptr) { + delete item->widget(); + delete item; + } + m_cheats.clear(); + m_cheatCheckBoxes.clear(); + + int maxWidthButton = 0; + + for (const QJsonValue& modValue : modsArray) { + QJsonObject modObject = modValue.toObject(); + QString modName = modObject["name"].toString(); + QString modType = modObject["type"].toString(); + + Cheat cheat; + cheat.name = modName; + cheat.type = modType; + + QJsonArray memoryArray = modObject["memory"].toArray(); + for (const QJsonValue& memoryValue : memoryArray) { + QJsonObject memoryObject = memoryValue.toObject(); + MemoryMod memoryMod; + memoryMod.offset = memoryObject["offset"].toString(); + memoryMod.on = memoryObject["on"].toString(); + memoryMod.off = memoryObject["off"].toString(); + cheat.memoryMods.append(memoryMod); + } + + // Check for the presence of 'hint' field + cheat.hasHint = modObject.contains("hint"); + + m_cheats[modName] = cheat; + + if (modType == "checkbox") { + QCheckBox* cheatCheckBox = new QCheckBox(modName); + rightLayout->addWidget(cheatCheckBox); + m_cheatCheckBoxes.append(cheatCheckBox); + connect(cheatCheckBox, &QCheckBox::toggled, + [=](bool checked) { applyCheat(modName, checked); }); + } else if (modType == "button") { + QPushButton* cheatButton = new QPushButton(modName); + cheatButton->adjustSize(); + int buttonWidth = cheatButton->sizeHint().width(); + if (buttonWidth > maxWidthButton) { + maxWidthButton = buttonWidth; + } + + // Create a horizontal layout for buttons + QHBoxLayout* buttonLayout = new QHBoxLayout(); + buttonLayout->setContentsMargins(0, 0, 0, 0); + buttonLayout->addWidget(cheatButton); + buttonLayout->addStretch(); + + rightLayout->addLayout(buttonLayout); + connect(cheatButton, &QPushButton::clicked, [=]() { applyCheat(modName, true); }); + } + } + + // Set minimum and fixed size for all buttons + 20 + for (int i = 0; i < rightLayout->count(); ++i) { + QLayoutItem* layoutItem = rightLayout->itemAt(i); + QWidget* widget = layoutItem->widget(); + if (widget) { + QPushButton* button = qobject_cast(widget); + if (button) { + button->setMinimumWidth(maxWidthButton); + button->setFixedWidth(maxWidthButton + 20); + } + } else { + QLayout* layout = layoutItem->layout(); + if (layout) { + for (int j = 0; j < layout->count(); ++j) { + QLayoutItem* innerItem = layout->itemAt(j); + QWidget* innerWidget = innerItem->widget(); + if (innerWidget) { + QPushButton* button = qobject_cast(innerWidget); + if (button) { + button->setMinimumWidth(maxWidthButton); + button->setFixedWidth(maxWidthButton + 20); + } + } + } + } + } + } + + // Set credits label + QLabel* creditsLabel = new QLabel(); + QString creditsText = tr("Author: "); + if (!creditsArray.isEmpty()) { + creditsText += creditsArray[0].toString(); + } + creditsLabel->setText(creditsText); + creditsLabel->setAlignment(Qt::AlignLeft); + rightLayout->addWidget(creditsLabel); +} + +void CheatsPatches::populateFileListCheats() { + QString cheatsDir = + QString::fromStdString(Common::FS::GetUserPath(Common::FS::PathType::CheatsDir).string()); + QString pattern = m_gameSerial + "_" + m_gameVersion + "*.json"; + + QDir dir(cheatsDir); + QStringList filters; + filters << pattern; + dir.setNameFilters(filters); + + QFileInfoList fileList = dir.entryInfoList(QDir::Files); + QStringList fileNames; + + for (const QFileInfo& fileInfo : fileList) { + fileNames << fileInfo.fileName(); + } + + QStringListModel* model = new QStringListModel(fileNames, this); + listView_selectFile->setModel(model); + + connect(listView_selectFile->selectionModel(), &QItemSelectionModel::selectionChanged, this, + [this]() { + QModelIndexList selectedIndexes = + listView_selectFile->selectionModel()->selectedIndexes(); + if (!selectedIndexes.isEmpty()) { + + QString selectedFileName = selectedIndexes.first().data().toString(); + QString cheatsDir = QString::fromStdString( + Common::FS::GetUserPath(Common::FS::PathType::CheatsDir).string()); + + QFile file(cheatsDir + "/" + selectedFileName); + if (file.open(QIODevice::ReadOnly)) { + QByteArray jsonData = file.readAll(); + QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData); + QJsonObject jsonObject = jsonDoc.object(); + QJsonArray modsArray = jsonObject["mods"].toArray(); + QJsonArray creditsArray = jsonObject["credits"].toArray(); + addCheatsToLayout(modsArray, creditsArray); + } + } + }); + + if (!fileNames.isEmpty()) { + QModelIndex firstIndex = model->index(0, 0); + listView_selectFile->selectionModel()->select(firstIndex, QItemSelectionModel::Select | + QItemSelectionModel::Rows); + listView_selectFile->setCurrentIndex(firstIndex); + } +} + +void CheatsPatches::addPatchesToLayout(const QString& filePath) { + if (filePath == "") { + return; + } + QString folderPath = filePath.section(" | ", 1, 1); + + // Clear existing layout items + QLayoutItem* item; + while ((item = patchesGroupBoxLayout->takeAt(0)) != nullptr) { + delete item->widget(); + delete item; + } + m_patchInfos.clear(); + + QDir dir(Common::FS::GetUserPath(Common::FS::PathType::PatchesDir)); + QString fullPath = dir.filePath(folderPath); + + if (!dir.exists(fullPath)) { + QMessageBox::warning(this, tr("ERROR"), + QString(tr("Directory does not exist:") + "\n%1").arg(fullPath)); + return; + } + dir.setPath(fullPath); + + QString filesJsonPath = dir.filePath("files.json"); + + QFile jsonFile(filesJsonPath); + if (!jsonFile.open(QIODevice::ReadOnly)) { + QMessageBox::warning(this, tr("ERROR"), tr("Failed to open files.json for reading.")); + return; + } + + QByteArray jsonData = jsonFile.readAll(); + jsonFile.close(); + + QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData); + QJsonObject jsonObject = jsonDoc.object(); + + bool patchAdded = false; + + // Iterate over each entry in the JSON file + for (auto it = jsonObject.constBegin(); it != jsonObject.constEnd(); ++it) { + QString xmlFileName = it.key(); + QJsonArray idsArray = it.value().toArray(); + + // Check if the serial is in the ID list + if (idsArray.contains(QJsonValue(m_gameSerial))) { + QString xmlFilePath = dir.filePath(xmlFileName); + QFile xmlFile(xmlFilePath); + + if (!xmlFile.open(QIODevice::ReadOnly)) { + QMessageBox::warning( + this, tr("ERROR"), + QString(tr("Failed to open file:") + "\n%1").arg(xmlFile.fileName())); + continue; + } + QXmlStreamReader xmlReader(&xmlFile); + QString patchName; + QString patchAuthor; + QString patchNote; + QJsonArray patchLines; + bool isEnabled = false; + + while (!xmlReader.atEnd() && !xmlReader.hasError()) { + xmlReader.readNext(); + + if (xmlReader.tokenType() == QXmlStreamReader::StartElement) { + if (xmlReader.name() == QStringLiteral("Metadata")) { + QXmlStreamAttributes attributes = xmlReader.attributes(); + QString appVer = attributes.value("AppVer").toString(); + if (appVer == m_gameVersion) { + patchName = attributes.value("Name").toString(); + patchAuthor = attributes.value("Author").toString(); + patchNote = attributes.value("Note").toString(); + isEnabled = + attributes.value("isEnabled").toString() == QStringLiteral("true"); + } + if (appVer == "mask") { + patchName = attributes.value("Name").toString() + " (any version)"; + patchAuthor = attributes.value("Author").toString(); + patchNote = attributes.value("Note").toString(); + isEnabled = + attributes.value("isEnabled").toString() == QStringLiteral("true"); + } + } else if (xmlReader.name() == QStringLiteral("PatchList")) { + QJsonArray linesArray; + while (!xmlReader.atEnd() && + !(xmlReader.tokenType() == QXmlStreamReader::EndElement && + xmlReader.name() == QStringLiteral("PatchList"))) { + xmlReader.readNext(); + if (xmlReader.tokenType() == QXmlStreamReader::StartElement && + xmlReader.name() == QStringLiteral("Line")) { + QXmlStreamAttributes attributes = xmlReader.attributes(); + QJsonObject lineObject; + lineObject["Type"] = attributes.value("Type").toString(); + lineObject["Address"] = attributes.value("Address").toString(); + lineObject["Value"] = attributes.value("Value").toString(); + linesArray.append(lineObject); + } + } + patchLines = linesArray; + } + } + + if (!patchName.isEmpty() && !patchLines.isEmpty()) { + QCheckBox* patchCheckBox = new QCheckBox(patchName); + patchCheckBox->setChecked(isEnabled); + patchesGroupBoxLayout->addWidget(patchCheckBox); + + PatchInfo patchInfo; + patchInfo.name = patchName; + patchInfo.author = patchAuthor; + patchInfo.note = patchNote; + patchInfo.linesArray = patchLines; + patchInfo.serial = m_gameSerial; + m_patchInfos[patchName] = patchInfo; + + patchCheckBox->installEventFilter(this); + + connect(patchCheckBox, &QCheckBox::toggled, + [=](bool checked) { applyPatch(patchName, checked); }); + + patchName.clear(); + patchAuthor.clear(); + patchNote.clear(); + patchLines = QJsonArray(); + patchAdded = true; + } + } + xmlFile.close(); + } + } + + // Remove the item from the list view if no patches were added (the game has patches, but not + // for the current version) + if (!patchAdded) { + QStringListModel* model = qobject_cast(patchesListView->model()); + if (model) { + QStringList items = model->stringList(); + int index = items.indexOf(filePath); + if (index != -1) { + items.removeAt(index); + model->setStringList(items); + } + } + } +} + +void CheatsPatches::updateNoteTextEdit(const QString& patchName) { + if (m_patchInfos.contains(patchName)) { + const PatchInfo& patchInfo = m_patchInfos[patchName]; + QString text = QString(tr("Name:") + " %1\n" + tr("Author: ") + "%2\n\n%3") + .arg(patchInfo.name) + .arg(patchInfo.author) + .arg(patchInfo.note); + + foreach (const QJsonValue& value, patchInfo.linesArray) { + QJsonObject lineObject = value.toObject(); + QString type = lineObject["Type"].toString(); + QString address = lineObject["Address"].toString(); + QString patchValue = lineObject["Value"].toString(); + + // add the values ​​to be modified in instructionsTextEdit + // text.append(QString("\nType: %1\nAddress: %2\n\nValue: %3") + // .arg(type) + // .arg(address) + // .arg(patchValue)); + } + text.replace("\\n", "\n"); + instructionsTextEdit->setText(text); + } +} + +bool showErrorMessage = true; +void CheatsPatches::uncheckAllCheatCheckBoxes() { + for (auto& cheatCheckBox : m_cheatCheckBoxes) { + cheatCheckBox->setChecked(false); + } + showErrorMessage = true; +} + +void CheatsPatches::applyCheat(const QString& modName, bool enabled) { + if (!m_cheats.contains(modName)) + return; + + if (MemoryPatcher::g_eboot_address == 0 && enabled) { + QMessageBox::critical(this, tr("Error"), + tr("Can't apply cheats before the game is started")); + uncheckAllCheatCheckBoxes(); + return; + } + + Cheat cheat = m_cheats[modName]; + + for (const MemoryMod& memoryMod : cheat.memoryMods) { + QString value = enabled ? memoryMod.on : memoryMod.off; + + std::string modNameStr = modName.toStdString(); + std::string offsetStr = memoryMod.offset.toStdString(); + std::string valueStr = value.toStdString(); + + if (MemoryPatcher::g_eboot_address == 0) + return; + + // Determine if the hint field is present + bool isHintPresent = m_cheats[modName].hasHint; + MemoryPatcher::PatchMemory(modNameStr, offsetStr, valueStr, !isHintPresent, false); + } +} + +void CheatsPatches::applyPatch(const QString& patchName, bool enabled) { + if (!enabled) + return; + if (m_patchInfos.contains(patchName)) { + const PatchInfo& patchInfo = m_patchInfos[patchName]; + + foreach (const QJsonValue& value, patchInfo.linesArray) { + QJsonObject lineObject = value.toObject(); + QString type = lineObject["Type"].toString(); + QString address = lineObject["Address"].toString(); + QString patchValue = lineObject["Value"].toString(); + QString maskOffsetStr = lineObject["Offset"].toString(); + + patchValue = MemoryPatcher::convertValueToHex(type, patchValue); + + bool littleEndian = false; + + if (type == "bytes16") { + littleEndian = true; + } else if (type == "bytes32") { + littleEndian = true; + } else if (type == "bytes64") { + littleEndian = true; + } + + MemoryPatcher::PatchMask patchMask = MemoryPatcher::PatchMask::None; + int maskOffsetValue = 0; + + if (type == "mask") { + patchMask = MemoryPatcher::PatchMask::Mask; + + // im not sure if this works, there is no games to test the mask offset on yet + if (!maskOffsetStr.toStdString().empty()) + maskOffsetValue = std::stoi(maskOffsetStr.toStdString(), 0, 10); + } + + if (type == "mask_jump32") + patchMask = MemoryPatcher::PatchMask::Mask_Jump32; + + if (MemoryPatcher::g_eboot_address == 0) { + MemoryPatcher::patchInfo addingPatch; + addingPatch.gameSerial = patchInfo.serial.toStdString(); + addingPatch.modNameStr = patchName.toStdString(); + addingPatch.offsetStr = address.toStdString(); + addingPatch.valueStr = patchValue.toStdString(); + addingPatch.isOffset = false; + addingPatch.littleEndian = littleEndian; + addingPatch.patchMask = patchMask; + addingPatch.maskOffset = maskOffsetValue; + + MemoryPatcher::AddPatchToQueue(addingPatch); + continue; + } + MemoryPatcher::PatchMemory(patchName.toStdString(), address.toStdString(), + patchValue.toStdString(), false, littleEndian, patchMask); + } + } +} + +bool CheatsPatches::eventFilter(QObject* obj, QEvent* event) { + if (event->type() == QEvent::HoverEnter || event->type() == QEvent::HoverLeave) { + QCheckBox* checkBox = qobject_cast(obj); + if (checkBox) { + bool hovered = (event->type() == QEvent::HoverEnter); + onPatchCheckBoxHovered(checkBox, hovered); + return true; + } + } + // Pass the event on to base class + return QWidget::eventFilter(obj, event); +} + +void CheatsPatches::onPatchCheckBoxHovered(QCheckBox* checkBox, bool hovered) { + if (hovered) { + QString text = checkBox->text(); + updateNoteTextEdit(text); + } else { + instructionsTextEdit->setText(defaultTextEdit); + } +} diff --git a/src/qt_gui/cheats_patches.h b/src/qt_gui/cheats_patches.h new file mode 100644 index 000000000..a9932886c --- /dev/null +++ b/src/qt_gui/cheats_patches.h @@ -0,0 +1,115 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#ifndef CHEATS_PATCHES_H +#define CHEATS_PATCHES_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class CheatsPatches : public QWidget { + Q_OBJECT + +public: + CheatsPatches(const QString& gameName, const QString& gameSerial, const QString& gameVersion, + const QString& gameSize, const QPixmap& gameImage, QWidget* parent = nullptr); + ~CheatsPatches(); + + // Public Methods + void downloadCheats(const QString& source, const QString& m_gameSerial, + const QString& m_gameVersion, bool showMessageBox); + void downloadPatches(const QString repository, const bool showMessageBox); + void createFilesJson(const QString& repository); + +signals: + void downloadFinished(); + +private: + // UI Setup and Event Handlers + void setupUI(); + void onSaveButtonClicked(); + QCheckBox* findCheckBoxByName(const QString& name); + bool eventFilter(QObject* obj, QEvent* event); + void onPatchCheckBoxHovered(QCheckBox* checkBox, bool hovered); + + // Cheat and Patch Management + void populateFileListCheats(); + void populateFileListPatches(); + + void addCheatsToLayout(const QJsonArray& modsArray, const QJsonArray& creditsArray); + void addPatchesToLayout(const QString& serial); + + void applyCheat(const QString& modName, bool enabled); + void applyPatch(const QString& patchName, bool enabled); + + void uncheckAllCheatCheckBoxes(); + void updateNoteTextEdit(const QString& patchName); + + // Network Manager + QNetworkAccessManager* manager; + + // Patch Info Structures + struct MemoryMod { + QString offset; + QString on; + QString off; + }; + + struct Cheat { + QString name; + QString type; + bool hasHint; + QVector memoryMods; + }; + + struct PatchInfo { + QString name; + QString author; + QString note; + QJsonArray linesArray; + QString serial; + }; + + // Members + QString m_gameName; + QString m_gameSerial; + QString m_gameVersion; + QString m_gameSize; + QPixmap m_gameImage; + QString m_cheatFilePath; + QMap m_cheats; + QMap m_patchInfos; + QVector m_cheatCheckBoxes; + + // UI Elements + QVBoxLayout* rightLayout; + QVBoxLayout* patchesGroupBoxLayout; + QGroupBox* patchesGroupBox; + QVBoxLayout* patchesLayout; + QTextEdit* instructionsTextEdit; + QListView* listView_selectFile; + QItemSelectionModel* selectionModel; + QComboBox* patchesComboBox; + QListView* patchesListView; + + QString defaultTextEdit; +}; + +#endif // CHEATS_PATCHES_H \ No newline at end of file diff --git a/src/qt_gui/elf_viewer.cpp b/src/qt_gui/elf_viewer.cpp index 72861d15f..e80fa25c1 100644 --- a/src/qt_gui/elf_viewer.cpp +++ b/src/qt_gui/elf_viewer.cpp @@ -1,8 +1,6 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include - #include "elf_viewer.h" ElfViewer::ElfViewer(QWidget* parent) : QTableWidget(parent) { diff --git a/src/qt_gui/elf_viewer.h b/src/qt_gui/elf_viewer.h index a3b85223d..1a65d70de 100644 --- a/src/qt_gui/elf_viewer.h +++ b/src/qt_gui/elf_viewer.h @@ -3,17 +3,7 @@ #pragma once -#include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include #include "core/loader/elf.h" #include "game_list_frame.h" diff --git a/src/qt_gui/game_grid_frame.h b/src/qt_gui/game_grid_frame.h index ce775315e..50b53a581 100644 --- a/src/qt_gui/game_grid_frame.h +++ b/src/qt_gui/game_grid_frame.h @@ -3,17 +3,7 @@ #pragma once -#include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include #include "common/config.h" #include "game_info.h" diff --git a/src/qt_gui/game_info.cpp b/src/qt_gui/game_info.cpp index 0a472eaef..2821a0322 100644 --- a/src/qt_gui/game_info.cpp +++ b/src/qt_gui/game_info.cpp @@ -1,10 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include -#include #include -#include #include "game_info.h" @@ -26,8 +23,8 @@ void GameInfoClass::GetGameInfo(QWidget* parent) { }).results(); // Progress bar, please be patient :) - QProgressDialog dialog("Loading game list, please wait :3", "Cancel", 0, 0, parent); - dialog.setWindowTitle("Loading..."); + QProgressDialog dialog(tr("Loading game list, please wait :3"), tr("Cancel"), 0, 0, parent); + dialog.setWindowTitle(tr("Loading...")); QFutureWatcher futureWatcher; GameListUtils game_util; diff --git a/src/qt_gui/game_info.h b/src/qt_gui/game_info.h index b2b102e00..6032e1c3a 100644 --- a/src/qt_gui/game_info.h +++ b/src/qt_gui/game_info.h @@ -3,10 +3,8 @@ #pragma once -#include -#include -#include -#include +#include +#include #include "common/config.h" #include "core/file_format/psf.h" @@ -43,7 +41,6 @@ public: : fw.left(3).insert(1, '.'); game.fw = (fw_int == 0) ? "0.00" : fw_.toStdString(); game.version = psf.GetString("APP_VER"); - game.category = psf.GetString("CATEGORY"); } return game; } diff --git a/src/qt_gui/game_install_dialog.cpp b/src/qt_gui/game_install_dialog.cpp index 4b2b8528b..5f031bdec 100644 --- a/src/qt_gui/game_install_dialog.cpp +++ b/src/qt_gui/game_install_dialog.cpp @@ -21,14 +21,14 @@ GameInstallDialog::GameInstallDialog() : m_gamesDirectory(nullptr) { layout->addStretch(); layout->addWidget(SetupDialogActions()); - setWindowTitle("shadPS4 - Choose directory"); + setWindowTitle(tr("shadPS4 - Choose directory")); setWindowIcon(QIcon(":images/shadps4.ico")); } GameInstallDialog::~GameInstallDialog() {} void GameInstallDialog::Browse() { - auto path = QFileDialog::getExistingDirectory(this, "Directory to install games"); + auto path = QFileDialog::getExistingDirectory(this, tr("Directory to install games")); if (!path.isEmpty()) { m_gamesDirectory->setText(QDir::toNativeSeparators(path)); @@ -36,7 +36,7 @@ void GameInstallDialog::Browse() { } QWidget* GameInstallDialog::SetupGamesDirectory() { - auto group = new QGroupBox("Directory to install games"); + auto group = new QGroupBox(tr("Directory to install games")); auto layout = new QHBoxLayout(group); // Input. @@ -47,7 +47,7 @@ QWidget* GameInstallDialog::SetupGamesDirectory() { layout->addWidget(m_gamesDirectory); // Browse button. - auto browse = new QPushButton("Browse"); + auto browse = new QPushButton(tr("Browse")); connect(browse, &QPushButton::clicked, this, &GameInstallDialog::Browse); @@ -71,7 +71,7 @@ void GameInstallDialog::Save() { if (gamesDirectory.isEmpty() || !QDir(gamesDirectory).exists() || !QDir::isAbsolutePath(gamesDirectory)) { - QMessageBox::critical(this, "Error", + QMessageBox::critical(this, tr("Error"), "The value for location to install games is not valid."); return; } diff --git a/src/qt_gui/game_list_frame.cpp b/src/qt_gui/game_list_frame.cpp index 2699c9615..f5c2facbe 100644 --- a/src/qt_gui/game_list_frame.cpp +++ b/src/qt_gui/game_list_frame.cpp @@ -23,14 +23,13 @@ GameListFrame::GameListFrame(std::shared_ptr game_info_get, QWidg this->horizontalHeader()->setSortIndicatorShown(true); this->horizontalHeader()->setStretchLastSection(true); this->setContextMenuPolicy(Qt::CustomContextMenu); - this->setColumnCount(9); - this->setColumnWidth(1, 250); - this->setColumnWidth(2, 110); - this->setColumnWidth(3, 80); - this->setColumnWidth(4, 90); - this->setColumnWidth(5, 80); - this->setColumnWidth(6, 80); - this->setColumnWidth(7, 80); + this->setColumnCount(8); + this->setColumnWidth(1, 300); // Name + this->setColumnWidth(2, 120); // Serial + this->setColumnWidth(3, 90); // Region + this->setColumnWidth(4, 90); // Firmware + this->setColumnWidth(5, 90); // Size + this->setColumnWidth(6, 90); // Version QStringList headers; headers << "Icon" << "Name" @@ -39,7 +38,6 @@ GameListFrame::GameListFrame(std::shared_ptr game_info_get, QWidg << "Firmware" << "Size" << "Version" - << "Category" << "Path"; this->setHorizontalHeaderLabels(headers); this->horizontalHeader()->setSortIndicatorShown(true); @@ -87,8 +85,7 @@ void GameListFrame::PopulateGameList() { SetTableItem(i, 4, QString::fromStdString(m_game_info->m_games[i].fw)); SetTableItem(i, 5, QString::fromStdString(m_game_info->m_games[i].size)); SetTableItem(i, 6, QString::fromStdString(m_game_info->m_games[i].version)); - SetTableItem(i, 7, QString::fromStdString(m_game_info->m_games[i].category)); - SetTableItem(i, 8, QString::fromStdString(m_game_info->m_games[i].path)); + SetTableItem(i, 7, QString::fromStdString(m_game_info->m_games[i].path)); } } @@ -168,7 +165,7 @@ void GameListFrame::SetTableItem(int row, int column, QString itemStr) { QVBoxLayout* layout = new QVBoxLayout(widget); QLabel* label = new QLabel(itemStr, widget); - label->setStyleSheet("color: white; font-size: 15px; font-weight: bold;"); + label->setStyleSheet("color: white; font-size: 16px; font-weight: bold;"); // Create shadow effect QGraphicsDropShadowEffect* shadowEffect = new QGraphicsDropShadowEffect(); diff --git a/src/qt_gui/game_list_frame.h b/src/qt_gui/game_list_frame.h index d8bccf466..ab70e6f6b 100644 --- a/src/qt_gui/game_list_frame.h +++ b/src/qt_gui/game_list_frame.h @@ -3,19 +3,7 @@ #pragma once -#include -#include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include #include "game_info.h" #include "game_list_utils.h" diff --git a/src/qt_gui/game_list_utils.h b/src/qt_gui/game_list_utils.h index 2e25f1220..476c90035 100644 --- a/src/qt_gui/game_list_utils.h +++ b/src/qt_gui/game_list_utils.h @@ -3,13 +3,8 @@ #pragma once -#include -#include -#include -#include - struct GameInfo { - std::string path; // root path of game directory (normaly directory that contains eboot.bin) + std::string path; // root path of game directory (normally directory that contains eboot.bin) std::string icon_path; // path of icon0.png std::string pic_path; // path of pic1.png QImage icon; @@ -19,7 +14,6 @@ struct GameInfo { std::string serial = "Unknown"; std::string version = "Unknown"; std::string region = "Unknown"; - std::string category = "Unknown"; std::string fw = "Unknown"; }; diff --git a/src/qt_gui/gui_context_menus.h b/src/qt_gui/gui_context_menus.h index 2f4b884cf..fb1994bb0 100644 --- a/src/qt_gui/gui_context_menus.h +++ b/src/qt_gui/gui_context_menus.h @@ -4,20 +4,13 @@ #pragma once #include -#include #include -#include -#include -#include #include #include -#include -#include -#include -#include #include #include +#include "cheats_patches.h" #include "game_info.h" #include "trophy_viewer.h" @@ -28,6 +21,7 @@ #include #include #endif +#include "common/path_util.h" class GuiContextMenus : public QObject { Q_OBJECT @@ -42,23 +36,30 @@ public: itemID = widget->currentRow() * widget->columnCount() + widget->currentColumn(); } + // Do not show the menu if an item is selected + if (itemID == -1) { + return; + } + // Setup menu. QMenu menu(widget); - QAction createShortcut("Create Shortcut", widget); - QAction openFolder("Open Game Folder", widget); - QAction openSfoViewer("SFO Viewer", widget); - QAction openTrophyViewer("Trophy Viewer", widget); + QAction createShortcut(tr("Create Shortcut"), widget); + QAction openFolder(tr("Open Game Folder"), widget); + QAction openCheats(tr("Cheats / Patches"), widget); + QAction openSfoViewer(tr("SFO Viewer"), widget); + QAction openTrophyViewer(tr("Trophy Viewer"), widget); menu.addAction(&openFolder); menu.addAction(&createShortcut); + menu.addAction(&openCheats); menu.addAction(&openSfoViewer); menu.addAction(&openTrophyViewer); // "Copy" submenu. - QMenu* copyMenu = new QMenu("Copy info", widget); - QAction* copyName = new QAction("Copy Name", widget); - QAction* copySerial = new QAction("Copy Serial", widget); - QAction* copyNameAll = new QAction("Copy All", widget); + QMenu* copyMenu = new QMenu(tr("Copy info"), widget); + QAction* copyName = new QAction(tr("Copy Name"), widget); + QAction* copySerial = new QAction(tr("Copy Serial"), widget); + QAction* copyNameAll = new QAction(tr("Copy All"), widget); copyMenu->addAction(copyName); copyMenu->addAction(copySerial); @@ -84,7 +85,7 @@ public: QTableWidget* tableWidget = new QTableWidget(rows, 2); tableWidget->setAttribute(Qt::WA_DeleteOnClose); connect(widget->parent(), &QWidget::destroyed, tableWidget, - [widget, tableWidget]() { tableWidget->deleteLater(); }); + [tableWidget]() { tableWidget->deleteLater(); }); tableWidget->verticalHeader()->setVisible(false); // Hide vertical header int row = 0; @@ -124,18 +125,31 @@ public: tableWidget->horizontalHeader()->setVisible(false); tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed); - tableWidget->setWindowTitle("SFO Viewer"); + tableWidget->setWindowTitle(tr("SFO Viewer")); tableWidget->show(); } } + if (selected == &openCheats) { + QString gameName = QString::fromStdString(m_games[itemID].name); + QString gameSerial = QString::fromStdString(m_games[itemID].serial); + QString gameVersion = QString::fromStdString(m_games[itemID].version); + QString gameSize = QString::fromStdString(m_games[itemID].size); + QPixmap gameImage(QString::fromStdString(m_games[itemID].icon_path)); + CheatsPatches* cheatsPatches = + new CheatsPatches(gameName, gameSerial, gameVersion, gameSize, gameImage); + cheatsPatches->show(); + connect(widget->parent(), &QWidget::destroyed, cheatsPatches, + [cheatsPatches]() { cheatsPatches->deleteLater(); }); + } + if (selected == &openTrophyViewer) { QString trophyPath = QString::fromStdString(m_games[itemID].serial); QString gameTrpPath = QString::fromStdString(m_games[itemID].path); TrophyViewer* trophyViewer = new TrophyViewer(trophyPath, gameTrpPath); trophyViewer->show(); connect(widget->parent(), &QWidget::destroyed, trophyViewer, - [widget, trophyViewer]() { trophyViewer->deleteLater(); }); + [trophyViewer]() { trophyViewer->deleteLater(); }); } if (selected == &createShortcut) { @@ -178,15 +192,15 @@ public: if (createShortcutLinux(linkPath, ebootPath, iconPath)) { #endif QMessageBox::information( - nullptr, "Shortcut creation", - QString("Shortcut created successfully!\n %1").arg(linkPath)); + nullptr, tr("Shortcut creation"), + QString(tr("Shortcut created successfully!\n %1")).arg(linkPath)); } else { QMessageBox::critical( - nullptr, "Error", - QString("Error creating shortcut!\n %1").arg(linkPath)); + nullptr, tr("Error"), + QString(tr("Error creating shortcut!\n %1")).arg(linkPath)); } } else { - QMessageBox::critical(nullptr, "Error", "Failed to convert icon."); + QMessageBox::critical(nullptr, tr("Error"), tr("Failed to convert icon.")); } } else { // If the icon is already in ICO format, we just create the shortcut @@ -196,11 +210,12 @@ public: if (createShortcutLinux(linkPath, ebootPath, iconPath)) { #endif QMessageBox::information( - nullptr, "Shortcut creation", - QString("Shortcut created successfully!\n %1").arg(linkPath)); + nullptr, tr("Shortcut creation"), + QString(tr("Shortcut created successfully!\n %1")).arg(linkPath)); } else { - QMessageBox::critical(nullptr, "Error", - QString("Error creating shortcut!\n %1").arg(linkPath)); + QMessageBox::critical( + nullptr, tr("Error"), + QString(tr("Error creating shortcut!\n %1")).arg(linkPath)); } } } @@ -257,7 +272,7 @@ public: int itemIndex = GetRowIndex(treeWidget, currentItem); // row QMenu menu(treeWidget); - QAction installPackage("Install PKG", treeWidget); + QAction installPackage(tr("Install PKG"), treeWidget); menu.addAction(&installPackage); diff --git a/src/qt_gui/main.cpp b/src/qt_gui/main.cpp index cff01cc2b..02957c6d5 100644 --- a/src/qt_gui/main.cpp +++ b/src/qt_gui/main.cpp @@ -1,14 +1,11 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include -#include - #include "common/config.h" #include "core/file_sys/fs.h" #include "emulator.h" -#include "qt_gui/game_install_dialog.h" -#include "qt_gui/main_window.h" +#include "game_install_dialog.h" +#include "main_window.h" // Custom message handler to ignore Qt logs void customMessageHandler(QtMsgType, const QMessageLogContext&, const QString&) {} diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index 34ef0d868..988e01a50 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -1,16 +1,13 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include #include -#include -#include #include -#include -#include #include "about_dialog.h" +#include "cheats_patches.h" #include "common/io_file.h" +#include "common/string_util.h" #include "common/version.h" #include "core/file_format/pkg.h" #include "core/loader.h" @@ -42,6 +39,7 @@ bool MainWindow::Init() { SetLastUsedTheme(); SetLastIconSizeBullet(); GetPhysicalDevices(); + LoadTranslation(); // show ui setMinimumSize(350, minimumSizeHint().height()); setWindowTitle(QString::fromStdString("shadPS4 v" + std::string(Common::VERSION))); @@ -120,7 +118,7 @@ void MainWindow::CreateDockWindows() { m_elf_viewer->hide(); m_game_list_frame->show(); m_dock_widget->setWidget(m_game_list_frame.data()); - slider_pos = Config::getSliderPositon(); + slider_pos = Config::getSliderPosition(); ui->sizeSlider->setSliderPosition(slider_pos); // set slider pos at start; isTableList = true; } else if (table_mode == 1) { // Grid @@ -128,7 +126,7 @@ void MainWindow::CreateDockWindows() { m_elf_viewer->hide(); m_game_grid_frame->show(); m_dock_widget->setWidget(m_game_grid_frame.data()); - slider_pos = Config::getSliderPositonGrid(); + slider_pos = Config::getSliderPositionGrid(); ui->sizeSlider->setSliderPosition(slider_pos); // set slider pos at start; isTableList = false; } else { @@ -168,7 +166,7 @@ void MainWindow::GetPhysicalDevices() { auto prop = physical_device.getProperties(); QString name = QString::fromUtf8(prop.deviceName, -1); if (prop.apiVersion < Vulkan::TargetVulkanApiVersion) { - name += " * Unsupported Vulkan Version"; + name += tr(" * Unsupported Vulkan Version"); } m_physical_devices.push_back(name); } @@ -188,12 +186,12 @@ void MainWindow::CreateConnects() { 36 + value; // 36 is the minimum icon size to use due to text disappearing. m_game_list_frame->ResizeIcons(36 + value); Config::setIconSize(36 + value); - Config::setSliderPositon(value); + Config::setSliderPosition(value); } else { m_game_grid_frame->icon_size = 69 + value; m_game_grid_frame->PopulateGameGrid(m_game_info->m_games, false); Config::setIconSizeGrid(69 + value); - Config::setSliderPositonGrid(value); + Config::setSliderPositionGrid(value); } }); @@ -205,11 +203,19 @@ void MainWindow::CreateConnects() { connect(ui->configureAct, &QAction::triggered, this, [this]() { auto settingsDialog = new SettingsDialog(m_physical_devices, this); + + connect(settingsDialog, &SettingsDialog::LanguageChanged, this, + &MainWindow::OnLanguageChanged); + settingsDialog->exec(); }); connect(ui->settingsButton, &QPushButton::clicked, this, [this]() { auto settingsDialog = new SettingsDialog(m_physical_devices, this); + + connect(settingsDialog, &SettingsDialog::LanguageChanged, this, + &MainWindow::OnLanguageChanged); + settingsDialog->exec(); }); @@ -224,11 +230,11 @@ void MainWindow::CreateConnects() { 36; // 36 is the minimum icon size to use due to text disappearing. ui->sizeSlider->setValue(0); // icone_size - 36 Config::setIconSize(36); - Config::setSliderPositon(0); + Config::setSliderPosition(0); } else { ui->sizeSlider->setValue(0); // icone_size - 36 Config::setIconSizeGrid(69); - Config::setSliderPositonGrid(0); + Config::setSliderPositionGrid(0); } }); @@ -237,11 +243,11 @@ void MainWindow::CreateConnects() { m_game_list_frame->icon_size = 64; ui->sizeSlider->setValue(28); Config::setIconSize(64); - Config::setSliderPositon(28); + Config::setSliderPosition(28); } else { ui->sizeSlider->setValue(28); Config::setIconSizeGrid(97); - Config::setSliderPositonGrid(28); + Config::setSliderPositionGrid(28); } }); @@ -250,11 +256,11 @@ void MainWindow::CreateConnects() { m_game_list_frame->icon_size = 128; ui->sizeSlider->setValue(92); Config::setIconSize(128); - Config::setSliderPositon(92); + Config::setSliderPosition(92); } else { ui->sizeSlider->setValue(92); Config::setIconSizeGrid(160); - Config::setSliderPositonGrid(91); + Config::setSliderPositionGrid(91); } }); @@ -263,11 +269,11 @@ void MainWindow::CreateConnects() { m_game_list_frame->icon_size = 256; ui->sizeSlider->setValue(220); Config::setIconSize(256); - Config::setSliderPositon(220); + Config::setSliderPosition(220); } else { ui->sizeSlider->setValue(220); Config::setIconSizeGrid(256); - Config::setSliderPositonGrid(220); + Config::setSliderPositionGrid(220); } }); // List @@ -282,7 +288,7 @@ void MainWindow::CreateConnects() { } isTableList = true; Config::setTableMode(0); - int slider_pos = Config::getSliderPositon(); + int slider_pos = Config::getSliderPosition(); ui->sizeSlider->setEnabled(true); ui->sizeSlider->setSliderPosition(slider_pos); }); @@ -298,7 +304,7 @@ void MainWindow::CreateConnects() { } isTableList = false; Config::setTableMode(1); - int slider_pos_grid = Config::getSliderPositonGrid(); + int slider_pos_grid = Config::getSliderPositionGrid(); ui->sizeSlider->setEnabled(true); ui->sizeSlider->setSliderPosition(slider_pos_grid); }); @@ -313,6 +319,85 @@ void MainWindow::CreateConnects() { Config::setTableMode(2); }); + // Cheats/Patches Download. + connect(ui->downloadCheatsPatchesAct, &QAction::triggered, this, [this]() { + QDialog* panelDialog = new QDialog(this); + QVBoxLayout* layout = new QVBoxLayout(panelDialog); + QPushButton* downloadAllCheatsButton = + new QPushButton(tr("Download Cheats For All Installed Games"), panelDialog); + QPushButton* downloadAllPatchesButton = + new QPushButton(tr("Download Patches For All Games"), panelDialog); + + layout->addWidget(downloadAllCheatsButton); + layout->addWidget(downloadAllPatchesButton); + + panelDialog->setLayout(layout); + + connect(downloadAllCheatsButton, &QPushButton::clicked, this, [this, panelDialog]() { + QEventLoop eventLoop; + int pendingDownloads = 0; + + auto onDownloadFinished = [&]() { + if (--pendingDownloads <= 0) { + eventLoop.quit(); + } + }; + + for (const GameInfo& game : m_game_info->m_games) { + QString empty = ""; + QString gameSerial = QString::fromStdString(game.serial); + QString gameVersion = QString::fromStdString(game.version); + + CheatsPatches* cheatsPatches = + new CheatsPatches(empty, empty, empty, empty, empty, nullptr); + connect(cheatsPatches, &CheatsPatches::downloadFinished, onDownloadFinished); + + pendingDownloads += 3; + + cheatsPatches->downloadCheats("wolf2022", gameSerial, gameVersion, false); + cheatsPatches->downloadCheats("GoldHEN", gameSerial, gameVersion, false); + cheatsPatches->downloadCheats("shadPS4", gameSerial, gameVersion, false); + } + eventLoop.exec(); + + QMessageBox::information( + nullptr, tr("Download Complete"), + tr("You have downloaded cheats for all the games you have installed.")); + + panelDialog->accept(); + }); + connect(downloadAllPatchesButton, &QPushButton::clicked, [panelDialog]() { + QEventLoop eventLoop; + int pendingDownloads = 0; + + auto onDownloadFinished = [&]() { + if (--pendingDownloads <= 0) { + eventLoop.quit(); + } + }; + + QString empty = ""; + CheatsPatches* cheatsPatches = + new CheatsPatches(empty, empty, empty, empty, empty, nullptr); + connect(cheatsPatches, &CheatsPatches::downloadFinished, onDownloadFinished); + + pendingDownloads += 2; + + cheatsPatches->downloadPatches("GoldHEN", false); + cheatsPatches->downloadPatches("shadPS4", false); + + eventLoop.exec(); + QMessageBox::information( + nullptr, tr("Download Complete"), + QString(tr("Patches Downloaded Successfully!") + "\n" + + tr("All Patches available for all games have been downloaded."))); + cheatsPatches->createFilesJson("GoldHEN"); + cheatsPatches->createFilesJson("shadPS4"); + panelDialog->accept(); + }); + panelDialog->exec(); + }); + // Dump game list. connect(ui->dumpGameListAct, &QAction::triggered, this, [&] { QString filePath = qApp->applicationDirPath().append("/GameList.txt"); @@ -464,7 +549,7 @@ void MainWindow::RefreshGameTable() { m_game_grid_frame->PopulateGameGrid(m_game_info->m_games, false); statusBar->clearMessage(); int numGames = m_game_info->m_games.size(); - QString statusMessage = "Games: " + QString::number(numGames); + QString statusMessage = tr("Games: ") + QString::number(numGames); statusBar->showMessage(statusMessage); } @@ -515,7 +600,8 @@ void MainWindow::BootGame() { int nFiles = fileNames.size(); if (nFiles > 1) { - QMessageBox::critical(nullptr, "Game Boot", QString("Only one file can be selected!")); + QMessageBox::critical(nullptr, tr("Game Boot"), + QString(tr("Only one file can be selected!"))); } else { std::filesystem::path path(fileNames[0].toStdString()); #ifdef _WIN64 @@ -532,38 +618,48 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int pkg = PKG(); pkg.Open(file); std::string failreason; - const auto extract_path = - std::filesystem::path(Config::getGameInstallDir()) / pkg.GetTitleID(); + auto extract_path = std::filesystem::path(Config::getGameInstallDir()) / pkg.GetTitleID(); QString pkgType = QString::fromStdString(pkg.GetPkgFlags()); QDir game_dir(QString::fromStdString(extract_path.string())); if (game_dir.exists()) { QMessageBox msgBox; - msgBox.setWindowTitle("PKG Extraction"); + msgBox.setWindowTitle(tr("PKG Extraction")); + + psf.open("", pkg.sfo); + + std::string content_id = psf.GetString("CONTENT_ID"); + std::string entitlement_label = Common::SplitString(content_id, '-')[2]; + + auto addon_extract_path = Common::FS::GetUserPath(Common::FS::PathType::AddonsDir) / + pkg.GetTitleID() / entitlement_label; + QDir addon_dir(QString::fromStdString(addon_extract_path.string())); + auto category = psf.GetString("CATEGORY"); + if (pkgType.contains("PATCH")) { - psf.open("", pkg.sfo); QString pkg_app_version = QString::fromStdString(psf.GetString("APP_VER")); psf.open(extract_path.string() + "/sce_sys/param.sfo", {}); QString game_app_version = QString::fromStdString(psf.GetString("APP_VER")); double appD = game_app_version.toDouble(); double pkgD = pkg_app_version.toDouble(); if (pkgD == appD) { - msgBox.setText( - QString("Patch detected!\nPKG and Game versions match!: %1\nWould you like " - "to overwrite?") - .arg(pkg_app_version)); + msgBox.setText(QString(tr("Patch detected!") + "\n" + + tr("PKG and Game versions match: ") + pkg_app_version + + "\n" + tr("Would you like to overwrite?"))); msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); msgBox.setDefaultButton(QMessageBox::No); } else if (pkgD < appD) { - msgBox.setText(QString("Patch detected!\nPKG Version %1 is older " - "than installed version!: %2\nWould you like " - "to overwrite?") - .arg(pkg_app_version, game_app_version)); + msgBox.setText(QString(tr("Patch detected!") + "\n" + + tr("PKG Version %1 is older than installed version: ") + .arg(pkg_app_version) + + game_app_version + "\n" + + tr("Would you like to overwrite?"))); msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); msgBox.setDefaultButton(QMessageBox::No); } else { - msgBox.setText(QString("Patch detected!\nGame is installed: %1\nWould you like " - "to install Patch: %2?") - .arg(game_app_version, pkg_app_version)); + msgBox.setText(QString(tr("Patch detected!") + "\n" + + tr("Game is installed: ") + game_app_version + "\n" + + tr("Would you like to install Patch: ") + + pkg_app_version + " ?")); msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); msgBox.setDefaultButton(QMessageBox::No); } @@ -573,9 +669,38 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int } else { return; } + } else if (category == "ac") { + if (!addon_dir.exists()) { + QMessageBox addonMsgBox; + addonMsgBox.setWindowTitle(tr("DLC Installation")); + addonMsgBox.setText(QString(tr("Would you like to install DLC: %1?")) + .arg(QString::fromStdString(entitlement_label))); + + addonMsgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); + addonMsgBox.setDefaultButton(QMessageBox::No); + int result = addonMsgBox.exec(); + if (result == QMessageBox::Yes) { + extract_path = addon_extract_path; + } else { + return; + } + } else { + msgBox.setText(QString(tr("DLC already installed:") + "\n" + + QString::fromStdString(addon_extract_path.string()) + + "\n\n" + tr("Would you like to overwrite?"))); + msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); + msgBox.setDefaultButton(QMessageBox::No); + int result = msgBox.exec(); + if (result == QMessageBox::Yes) { + extract_path = addon_extract_path; + } else { + return; + } + } } else { - msgBox.setText(QString("Game already installed\n%1\nWould you like to overwrite?") - .arg(QString::fromStdString(extract_path.string()))); + msgBox.setText(QString(tr("Game already installed") + "\n" + + QString::fromStdString(extract_path.string()) + "\n" + + tr("Would you like to overwrite?"))); msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); msgBox.setDefaultButton(QMessageBox::No); int result = msgBox.exec(); @@ -588,59 +713,63 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int } else { // Do nothing; if (pkgType.contains("PATCH")) { - QMessageBox::information(this, "PKG Extraction", - "PKG is a patch, please install the game first!"); + QMessageBox::information(this, tr("PKG Extraction"), + tr("PKG is a patch, please install the game first!")); return; } // what else? } if (!pkg.Extract(file, extract_path, failreason)) { - QMessageBox::critical(this, "PKG ERROR", QString::fromStdString(failreason)); + QMessageBox::critical(this, tr("PKG ERROR"), QString::fromStdString(failreason)); } else { int nfiles = pkg.GetNumberOfFiles(); - QVector indices; - for (int i = 0; i < nfiles; i++) { - indices.append(i); - } - - QProgressDialog dialog; - dialog.setWindowTitle("PKG Extraction"); - dialog.setWindowModality(Qt::WindowModal); - QString extractmsg = QString("Extracting PKG %1/%2").arg(pkgNum).arg(nPkg); - dialog.setLabelText(extractmsg); - dialog.setAutoClose(true); - dialog.setRange(0, nfiles); - - QFutureWatcher futureWatcher; - connect(&futureWatcher, &QFutureWatcher::finished, this, [=, this]() { - if (pkgNum == nPkg) { - QString path = QString::fromStdString(Config::getGameInstallDir()); - QMessageBox extractMsgBox(this); - extractMsgBox.setWindowTitle("Extraction Finished"); - extractMsgBox.setText(QString("Game successfully installed at %1").arg(path)); - extractMsgBox.addButton(QMessageBox::Ok); - extractMsgBox.setDefaultButton(QMessageBox::Ok); - connect(&extractMsgBox, &QMessageBox::buttonClicked, this, - [&](QAbstractButton* button) { - if (extractMsgBox.button(QMessageBox::Ok) == button) { - extractMsgBox.close(); - emit ExtractionFinished(); - } - }); - extractMsgBox.exec(); + if (nfiles > 0) { + QVector indices; + for (int i = 0; i < nfiles; i++) { + indices.append(i); } - }); - connect(&dialog, &QProgressDialog::canceled, [&]() { futureWatcher.cancel(); }); - connect(&futureWatcher, &QFutureWatcher::progressValueChanged, &dialog, - &QProgressDialog::setValue); - futureWatcher.setFuture( - QtConcurrent::map(indices, [&](int index) { pkg.ExtractFiles(index); })); - dialog.exec(); + + QProgressDialog dialog; + dialog.setWindowTitle(tr("PKG Extraction")); + dialog.setWindowModality(Qt::WindowModal); + QString extractmsg = QString(tr("Extracting PKG %1/%2")).arg(pkgNum).arg(nPkg); + dialog.setLabelText(extractmsg); + dialog.setAutoClose(true); + dialog.setRange(0, nfiles); + + QFutureWatcher futureWatcher; + connect(&futureWatcher, &QFutureWatcher::finished, this, [=, this]() { + if (pkgNum == nPkg) { + QString path = QString::fromStdString(Config::getGameInstallDir()); + QMessageBox extractMsgBox(this); + extractMsgBox.setWindowTitle(tr("Extraction Finished")); + extractMsgBox.setText( + QString(tr("Game successfully installed at %1")).arg(path)); + extractMsgBox.addButton(QMessageBox::Ok); + extractMsgBox.setDefaultButton(QMessageBox::Ok); + connect(&extractMsgBox, &QMessageBox::buttonClicked, this, + [&](QAbstractButton* button) { + if (extractMsgBox.button(QMessageBox::Ok) == button) { + extractMsgBox.close(); + emit ExtractionFinished(); + } + }); + extractMsgBox.exec(); + } + }); + connect(&dialog, &QProgressDialog::canceled, [&]() { futureWatcher.cancel(); }); + connect(&futureWatcher, &QFutureWatcher::progressValueChanged, &dialog, + &QProgressDialog::setValue); + futureWatcher.setFuture( + QtConcurrent::map(indices, [&](int index) { pkg.ExtractFiles(index); })); + dialog.exec(); + } } } else { - QMessageBox::critical(this, "PKG ERROR", "File doesn't appear to be a valid PKG file"); + QMessageBox::critical(this, tr("PKG ERROR"), + tr("File doesn't appear to be a valid PKG file")); } } @@ -786,3 +915,35 @@ void MainWindow::CreateRecentGameActions() { emulator.Run(gamePath.toUtf8().constData()); }); } + +void MainWindow::LoadTranslation() { + auto language = QString::fromStdString(Config::getEmulatorLanguage()); + + const QString base_dir = QStringLiteral(":/translations"); + QString base_path = QStringLiteral("%1/%2.qm").arg(base_dir).arg(language); + + if (QFile::exists(base_path)) { + if (translator != nullptr) { + qApp->removeTranslator(translator); + } + + translator = new QTranslator(qApp); + if (!translator->load(base_path)) { + QMessageBox::warning( + nullptr, QStringLiteral("Translation Error"), + QStringLiteral("Failed to find load translation file for '%1':\n%2") + .arg(language) + .arg(base_path)); + delete translator; + } else { + qApp->installTranslator(translator); + ui->retranslateUi(this); + } + } +} + +void MainWindow::OnLanguageChanged(const std::string& locale) { + Config::setEmulatorLanguage(locale); + + LoadTranslation(); +} \ No newline at end of file diff --git a/src/qt_gui/main_window.h b/src/qt_gui/main_window.h index 24de15b92..d3b83e619 100644 --- a/src/qt_gui/main_window.h +++ b/src/qt_gui/main_window.h @@ -3,13 +3,9 @@ #pragma once -#include #include #include -#include -#include -#include -#include +#include #include "common/config.h" #include "common/path_util.h" @@ -48,6 +44,7 @@ private Q_SLOTS: void ShowGameList(); void RefreshGameTable(); void HandleResize(QResizeEvent* event); + void OnLanguageChanged(const std::string& locale); private: Ui_MainWindow* ui; @@ -64,6 +61,7 @@ private: void InstallPkg(); void BootGame(); void AddRecentFiles(QString filePath); + void LoadTranslation(); QIcon RecolorIcon(const QIcon& icon, bool isWhite); bool isIconBlack = false; bool isTableList = true; @@ -89,6 +87,8 @@ private: std::shared_ptr m_game_info = std::make_shared(); + QTranslator* translator; + protected: void dragEnterEvent(QDragEnterEvent* event1) override { if (event1->mimeData()->hasUrls()) { diff --git a/src/qt_gui/main_window_ui.h b/src/qt_gui/main_window_ui.h index 7d0c58dd2..0acfade0e 100644 --- a/src/qt_gui/main_window_ui.h +++ b/src/qt_gui/main_window_ui.h @@ -1,31 +1,11 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -/******************************************************************************** -** Form generated from reading UI file 'main_window.ui' -** -** Created by: Qt User Interface Compiler version 6.6.1 -** -** WARNING! All changes made in this file will be lost when recompiling UI file! -********************************************************************************/ +#pragma once -#ifndef MAIN_WINDOW_UI_H -#define MAIN_WINDOW_UI_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE +#include +#include +#include class Ui_MainWindow { public: @@ -43,6 +23,7 @@ public: QAction* setlistModeGridAct; QAction* setlistElfAct; QAction* gameInstallPathAct; + QAction* downloadCheatsPatchesAct; QAction* dumpGameListAct; QAction* pkgViewerAct; QAction* aboutAct; @@ -138,11 +119,12 @@ public: gameInstallPathAct = new QAction(MainWindow); gameInstallPathAct->setObjectName("gameInstallPathAct"); gameInstallPathAct->setIcon(QIcon(":images/folder_icon.png")); + downloadCheatsPatchesAct = new QAction(MainWindow); + downloadCheatsPatchesAct->setObjectName("downloadCheatsPatchesAct"); dumpGameListAct = new QAction(MainWindow); dumpGameListAct->setObjectName("dumpGameList"); pkgViewerAct = new QAction(MainWindow); pkgViewerAct->setObjectName("pkgViewer"); - pkgViewerAct->setObjectName("pkgViewer"); pkgViewerAct->setIcon(QIcon(":images/file_icon.png")); aboutAct = new QAction(MainWindow); aboutAct->setObjectName("aboutAct"); @@ -295,6 +277,7 @@ public: menuSettings->addAction(configureAct); menuSettings->addAction(gameInstallPathAct); menuSettings->addAction(menuUtils->menuAction()); + menuUtils->addAction(downloadCheatsPatchesAct); menuUtils->addAction(dumpGameListAct); menuUtils->addAction(pkgViewerAct); menuAbout->addAction(aboutAct); @@ -341,6 +324,8 @@ public: setlistElfAct->setText(QCoreApplication::translate("MainWindow", "Elf Viewer", nullptr)); gameInstallPathAct->setText( QCoreApplication::translate("MainWindow", "Game Install Directory", nullptr)); + downloadCheatsPatchesAct->setText( + QCoreApplication::translate("MainWindow", "Download Cheats/Patches", nullptr)); dumpGameListAct->setText( QCoreApplication::translate("MainWindow", "Dump Game List", nullptr)); pkgViewerAct->setText(QCoreApplication::translate("MainWindow", "PKG Viewer", nullptr)); @@ -367,8 +352,4 @@ public: namespace Ui { class MainWindow : public Ui_MainWindow {}; -} // namespace Ui - -QT_END_NAMESPACE - -#endif // MAIN_WINDOW_UI_H +} // namespace Ui \ No newline at end of file diff --git a/src/qt_gui/memory_patcher.cpp b/src/qt_gui/memory_patcher.cpp new file mode 100644 index 000000000..d5ffa1c99 --- /dev/null +++ b/src/qt_gui/memory_patcher.cpp @@ -0,0 +1,347 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "common/logging/log.h" +#include "common/path_util.h" +#include "memory_patcher.h" + +namespace MemoryPatcher { + +uintptr_t g_eboot_address; +u64 g_eboot_image_size; +std::string g_game_serial; +std::vector pending_patches; + +QString toHex(unsigned long long value, size_t byteSize) { + std::stringstream ss; + ss << std::hex << std::setfill('0') << std::setw(byteSize * 2) << value; + return QString::fromStdString(ss.str()); +} + +QString convertValueToHex(const QString& type, const QString& valueStr) { + QString result; + std::string typeStr = type.toStdString(); + std::string valueStrStd = valueStr.toStdString(); + + if (typeStr == "byte") { + unsigned int value = std::stoul(valueStrStd, nullptr, 16); + result = toHex(value, 1); + } else if (typeStr == "bytes16") { + unsigned int value = std::stoul(valueStrStd, nullptr, 16); + result = toHex(value, 2); + } else if (typeStr == "bytes32") { + unsigned long value = std::stoul(valueStrStd, nullptr, 16); + result = toHex(value, 4); + } else if (typeStr == "bytes64") { + unsigned long long value = std::stoull(valueStrStd, nullptr, 16); + result = toHex(value, 8); + } else if (typeStr == "float32") { + union { + float f; + uint32_t i; + } floatUnion; + floatUnion.f = std::stof(valueStrStd); + result = toHex(floatUnion.i, sizeof(floatUnion.i)); + } else if (typeStr == "float64") { + union { + double d; + uint64_t i; + } doubleUnion; + doubleUnion.d = std::stod(valueStrStd); + result = toHex(doubleUnion.i, sizeof(doubleUnion.i)); + } else if (typeStr == "utf8") { + QByteArray byteArray = QString::fromStdString(valueStrStd).toUtf8(); + byteArray.append('\0'); + std::stringstream ss; + for (unsigned char c : byteArray) { + ss << std::hex << std::setfill('0') << std::setw(2) << static_cast(c); + } + result = QString::fromStdString(ss.str()); + } else if (typeStr == "utf16") { + QByteArray byteArray( + reinterpret_cast(QString::fromStdString(valueStrStd).utf16()), + QString::fromStdString(valueStrStd).size() * 2); + byteArray.append('\0'); + byteArray.append('\0'); + std::stringstream ss; + for (unsigned char c : byteArray) { + ss << std::hex << std::setfill('0') << std::setw(2) << static_cast(c); + } + result = QString::fromStdString(ss.str()); + } else if (typeStr == "bytes") { + result = valueStr; + } else if (typeStr == "mask" || typeStr == "mask_jump32") { + result = valueStr; + } else { + LOG_INFO(Loader, "Error applying Patch, unknown type: {}", typeStr); + } + return result; +} + +void OnGameLoaded() { + + // We use the QT headers for the xml and json parsing, this define is only true on QT builds + QString patchDir = + QString::fromStdString(Common::FS::GetUserPath(Common::FS::PathType::PatchesDir).string()); + QString repositories[] = {"GoldHEN", "shadPS4"}; + + for (const QString& repository : repositories) { + QString filesJsonPath = patchDir + "/" + repository + "/files.json"; + + QFile jsonFile(filesJsonPath); + if (!jsonFile.open(QIODevice::ReadOnly)) { + LOG_ERROR(Loader, "Unable to open files.json for reading."); + continue; + } + + QByteArray jsonData = jsonFile.readAll(); + jsonFile.close(); + + QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData); + QJsonObject jsonObject = jsonDoc.object(); + + QString selectedFileName; + QString serial = QString::fromStdString(g_game_serial); + + for (auto it = jsonObject.constBegin(); it != jsonObject.constEnd(); ++it) { + QString filePath = it.key(); + QJsonArray idsArray = it.value().toArray(); + + if (idsArray.contains(QJsonValue(serial))) { + selectedFileName = filePath; + break; + } + } + + if (selectedFileName.isEmpty()) { + LOG_ERROR(Loader, "No patch file found for the current serial."); + continue; + } + + QString filePath = patchDir + "/" + repository + "/" + selectedFileName; + QFile file(filePath); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + LOG_ERROR(Loader, "Unable to open the file for reading."); + continue; + } + + QByteArray xmlData = file.readAll(); + file.close(); + + QString newXmlData; + + QXmlStreamReader xmlReader(xmlData); + bool insideMetadata = false; + + bool isEnabled = false; + std::string currentPatchName; + while (!xmlReader.atEnd()) { + xmlReader.readNext(); + + if (xmlReader.isStartElement()) { + QJsonArray patchLines; + if (xmlReader.name() == QStringLiteral("Metadata")) { + insideMetadata = true; + + QString name = xmlReader.attributes().value("Name").toString(); + currentPatchName = name.toStdString(); + + // Check and update the isEnabled attribute + for (const QXmlStreamAttribute& attr : xmlReader.attributes()) { + if (attr.name() == QStringLiteral("isEnabled")) { + if (attr.value().toString() == "true") { + isEnabled = true; + } else + isEnabled = false; + } + } + } else if (xmlReader.name() == QStringLiteral("PatchList")) { + QJsonArray linesArray; + while (!xmlReader.atEnd() && + !(xmlReader.tokenType() == QXmlStreamReader::EndElement && + xmlReader.name() == QStringLiteral("PatchList"))) { + xmlReader.readNext(); + if (xmlReader.tokenType() == QXmlStreamReader::StartElement && + xmlReader.name() == QStringLiteral("Line")) { + QXmlStreamAttributes attributes = xmlReader.attributes(); + QJsonObject lineObject; + lineObject["Type"] = attributes.value("Type").toString(); + lineObject["Address"] = attributes.value("Address").toString(); + lineObject["Value"] = attributes.value("Value").toString(); + linesArray.append(lineObject); + } + } + + patchLines = linesArray; + if (isEnabled) { + foreach (const QJsonValue& value, patchLines) { + QJsonObject lineObject = value.toObject(); + QString type = lineObject["Type"].toString(); + QString address = lineObject["Address"].toString(); + QString patchValue = lineObject["Value"].toString(); + QString maskOffsetStr = lineObject["Offset"].toString(); + + patchValue = convertValueToHex(type, patchValue); + + bool littleEndian = false; + + if (type == "bytes16") { + littleEndian = true; + } else if (type == "bytes32") { + littleEndian = true; + } else if (type == "bytes64") { + littleEndian = true; + } + + MemoryPatcher::PatchMask patchMask = MemoryPatcher::PatchMask::None; + int maskOffsetValue = 0; + + if (type == "mask") { + patchMask = MemoryPatcher::PatchMask::Mask; + + // im not sure if this works, there is no games to test the mask + // offset on yet + if (!maskOffsetStr.toStdString().empty()) + maskOffsetValue = std::stoi(maskOffsetStr.toStdString(), 0, 10); + } + + if (type == "mask_jump32") + patchMask = MemoryPatcher::PatchMask::Mask_Jump32; + + MemoryPatcher::PatchMemory(currentPatchName, address.toStdString(), + patchValue.toStdString(), false, + littleEndian, patchMask); + } + } + } + } + } + + if (xmlReader.hasError()) { + LOG_ERROR(Loader, "Failed to parse XML for {}", g_game_serial); + } else { + LOG_INFO(Loader, "Patches loaded successfully"); + } + ApplyPendingPatches(); + } +} + +void AddPatchToQueue(patchInfo patchToAdd) { + pending_patches.push_back(patchToAdd); +} + +void ApplyPendingPatches() { + + for (size_t i = 0; i < pending_patches.size(); ++i) { + patchInfo currentPatch = pending_patches[i]; + + if (currentPatch.gameSerial != g_game_serial) + continue; + + PatchMemory(currentPatch.modNameStr, currentPatch.offsetStr, currentPatch.valueStr, + currentPatch.isOffset, currentPatch.littleEndian, currentPatch.patchMask, + currentPatch.maskOffset); + } + + pending_patches.clear(); +} + +void PatchMemory(std::string modNameStr, std::string offsetStr, std::string valueStr, bool isOffset, + bool littleEndian, PatchMask patchMask, int maskOffset) { + // Send a request to modify the process memory. + void* cheatAddress = nullptr; + + if (patchMask == PatchMask::None) { + if (isOffset) { + cheatAddress = reinterpret_cast(g_eboot_address + std::stoi(offsetStr, 0, 16)); + } else { + cheatAddress = + reinterpret_cast(g_eboot_address + (std::stoi(offsetStr, 0, 16) - 0x400000)); + } + } + + if (patchMask == PatchMask::Mask) { + cheatAddress = reinterpret_cast(PatternScan(offsetStr) + maskOffset); + } + + // TODO: implement mask_jump32 + + if (cheatAddress == nullptr) { + LOG_ERROR(Loader, "Failed to get address for patch {}", modNameStr); + return; + } + + std::vector bytePatch; + + for (size_t i = 0; i < valueStr.length(); i += 2) { + unsigned char byte = + static_cast(std::strtol(valueStr.substr(i, 2).c_str(), nullptr, 16)); + + bytePatch.push_back(byte); + } + + if (littleEndian) { + std::reverse(bytePatch.begin(), bytePatch.end()); + } + + std::memcpy(cheatAddress, bytePatch.data(), bytePatch.size()); + + LOG_INFO(Loader, "Applied patch: {}, Offset: {}, Value: {}", modNameStr, + (uintptr_t)cheatAddress, valueStr); +} + +static std::vector PatternToByte(const std::string& pattern) { + std::vector bytes; + const char* start = pattern.data(); + const char* end = start + pattern.size(); + + for (const char* current = start; current < end; ++current) { + if (*current == '?') { + ++current; + if (*current == '?') + ++current; + bytes.push_back(-1); + } else { + bytes.push_back(strtoul(current, const_cast(¤t), 16)); + } + } + + return bytes; +} + +uintptr_t PatternScan(const std::string& signature) { + std::vector patternBytes = PatternToByte(signature); + const auto scanBytes = static_cast((void*)g_eboot_address); + + const int32_t* sigPtr = patternBytes.data(); + const size_t sigSize = patternBytes.size(); + + uint32_t foundResults = 0; + for (uint32_t i = 0; i < g_eboot_image_size - sigSize; ++i) { + bool found = true; + for (uint32_t j = 0; j < sigSize; ++j) { + if (scanBytes[i + j] != sigPtr[j] && sigPtr[j] != -1) { + found = false; + break; + } + } + + if (found) { + foundResults++; + return reinterpret_cast(&scanBytes[i]); + } + } + + return 0; +} + +} // namespace MemoryPatcher \ No newline at end of file diff --git a/src/qt_gui/memory_patcher.h b/src/qt_gui/memory_patcher.h new file mode 100644 index 000000000..821184ade --- /dev/null +++ b/src/qt_gui/memory_patcher.h @@ -0,0 +1,47 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once +#include +#include +#include +#include + +namespace MemoryPatcher { + +extern uintptr_t g_eboot_address; +extern u64 g_eboot_image_size; +extern std::string g_game_serial; + +enum PatchMask : uint8_t { + None, + Mask, + Mask_Jump32, +}; + +struct patchInfo { + std::string gameSerial; + std::string modNameStr; + std::string offsetStr; + std::string valueStr; + bool isOffset; + bool littleEndian; + PatchMask patchMask; + int maskOffset; +}; + +extern std::vector pending_patches; + +QString convertValueToHex(const QString& type, const QString& valueStr); + +void OnGameLoaded(); +void AddPatchToQueue(patchInfo patchToAdd); +void ApplyPendingPatches(); + +void PatchMemory(std::string modNameStr, std::string offsetStr, std::string valueStr, bool isOffset, + bool littleEndian, PatchMask patchMask = PatchMask::None, int maskOffset = 0); + +static std::vector PatternToByte(const std::string& pattern); +uintptr_t PatternScan(const std::string& signature); + +} // namespace MemoryPatcher \ No newline at end of file diff --git a/src/qt_gui/pkg_viewer.cpp b/src/qt_gui/pkg_viewer.cpp index cd2ce2b6e..49005c720 100644 --- a/src/qt_gui/pkg_viewer.cpp +++ b/src/qt_gui/pkg_viewer.cpp @@ -1,9 +1,6 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include -#include - #include "pkg_viewer.h" PKGViewer::PKGViewer(std::shared_ptr game_info_get, QWidget* parent, @@ -54,7 +51,7 @@ PKGViewer::PKGViewer(std::shared_ptr game_info_get, QWidget* pare InstallDragDropPkg); }); - connect(parent, &QWidget::destroyed, this, [parent, this]() { this->deleteLater(); }); + connect(parent, &QWidget::destroyed, this, [this]() { this->deleteLater(); }); } PKGViewer::~PKGViewer() {} diff --git a/src/qt_gui/pkg_viewer.h b/src/qt_gui/pkg_viewer.h index e040d5950..9598328a0 100644 --- a/src/qt_gui/pkg_viewer.h +++ b/src/qt_gui/pkg_viewer.h @@ -3,18 +3,9 @@ #pragma once -#include -#include -#include -#include #include -#include -#include #include #include -#include -#include -#include #include "common/io_file.h" #include "core/file_format/pkg.h" diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index aa2c73384..d572915c6 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -2,6 +2,9 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include + +#include "main_window.h" #include "settings_dialog.h" #include "ui_settings_dialog.h" @@ -59,6 +62,7 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge completer->setCaseSensitivity(Qt::CaseInsensitive); ui->consoleLanguageComboBox->setCompleter(completer); + InitializeEmulatorLanguages(); LoadValuesFromConfig(); connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &QWidget::close); @@ -86,7 +90,7 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge [](const QString& text) { Config::setUserName(text.toStdString()); }); connect(ui->consoleLanguageComboBox, QOverload::of(&QComboBox::currentIndexChanged), - this, [this](int index) { + this, [](int index) { if (index >= 0 && index < languageIndexes.size()) { int languageCode = languageIndexes[index]; Config::setLanguage(languageCode); @@ -157,6 +161,7 @@ void SettingsDialog::LoadValuesFromConfig() { languageIndexes.begin(), std::find(languageIndexes.begin(), languageIndexes.end(), Config::GetLanguage())) % languageIndexes.size()); + ui->emulatorLanguageComboBox->setCurrentIndex(languages[Config::getEmulatorLanguage()]); ui->graphicsAdapterBox->setCurrentIndex(Config::getGpuId() + 1); ui->widthSpinBox->setValue(Config::getScreenWidth()); ui->heightSpinBox->setValue(Config::getScreenHeight()); @@ -178,6 +183,35 @@ void SettingsDialog::LoadValuesFromConfig() { ui->rdocCheckBox->setChecked(Config::isRdocEnabled()); } +void SettingsDialog::InitializeEmulatorLanguages() { + QDirIterator it(QStringLiteral(":/translations"), QDirIterator::NoIteratorFlags); + + int idx = 0; + while (it.hasNext()) { + QString locale = it.next(); + locale.truncate(locale.lastIndexOf(QLatin1Char{'.'})); + locale.remove(0, locale.lastIndexOf(QLatin1Char{'/'}) + 1); + const QString lang = QLocale::languageToString(QLocale(locale).language()); + const QString country = QLocale::territoryToString(QLocale(locale).territory()); + ui->emulatorLanguageComboBox->addItem(QStringLiteral("%1 (%2)").arg(lang, country), locale); + + languages[locale.toStdString()] = idx; + idx++; + } + + connect(ui->emulatorLanguageComboBox, qOverload(&QComboBox::currentIndexChanged), this, + &SettingsDialog::OnLanguageChanged); +} + +void SettingsDialog::OnLanguageChanged(int index) { + if (index == -1) + return; + + ui->retranslateUi(this); + + emit LanguageChanged(ui->emulatorLanguageComboBox->itemData(index).toString().toStdString()); +} + int SettingsDialog::exec() { return QDialog::exec(); } diff --git a/src/qt_gui/settings_dialog.h b/src/qt_gui/settings_dialog.h index 7d8701093..ddae7daac 100644 --- a/src/qt_gui/settings_dialog.h +++ b/src/qt_gui/settings_dialog.h @@ -22,8 +22,15 @@ public: int exec() override; +signals: + void LanguageChanged(const std::string& locale); + private: void LoadValuesFromConfig(); + void InitializeEmulatorLanguages(); + void OnLanguageChanged(int index); std::unique_ptr ui; + + std::map languages; }; diff --git a/src/qt_gui/settings_dialog.ui b/src/qt_gui/settings_dialog.ui index dbb15206e..1ecd12495 100644 --- a/src/qt_gui/settings_dialog.ui +++ b/src/qt_gui/settings_dialog.ui @@ -51,8 +51,8 @@ 0 0 - 832 - 418 + 836 + 428 @@ -74,11 +74,73 @@ - + System + + + + Console Language + + + + + + + + + + + + Emulator Language + + + + + + + + + + + + + + + + + + + Emulator + + + + + + + + Enable Fullscreen + + + + + + + Show Splash + + + + + + + Is PS4 Pro + + + + + @@ -105,82 +167,6 @@ - - - - Console Language - - - - - - - - - - - - - Qt::Orientation::Vertical - - - QSizePolicy::Policy::MinimumExpanding - - - - 0 - 0 - - - - - - - - - - - - - - - Emulator - - - - - - Enable Fullscreen - - - - - - - Show Splash - - - - - - - Is PS4 Pro - - - - - - - Qt::Orientation::Vertical - - - - 20 - 40 - - - - @@ -261,19 +247,6 @@ - - - - Qt::Orientation::Vertical - - - - 20 - 40 - - - - @@ -282,34 +255,29 @@ - - - - 0 - - - 0 - - - 0 - - - 0 - - - + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + - + - GPU + Graphics - + - + - + @@ -323,8 +291,8 @@ - - + + 0 @@ -340,44 +308,10 @@ - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - Qt::Orientation::Vertical - - - QSizePolicy::Policy::MinimumExpanding - - - - 0 - 0 - - - - - + @@ -505,26 +439,10 @@ - - - - Qt::Orientation::Vertical - - - QSizePolicy::Policy::MinimumExpanding - - - - 0 - 0 - - - - - + 12 @@ -534,7 +452,7 @@ - Additional Settings + Advanced Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter @@ -565,17 +483,14 @@ - + Qt::Orientation::Vertical - - QSizePolicy::Policy::MinimumExpanding - - 0 - 0 + 20 + 40 @@ -584,6 +499,19 @@ + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + diff --git a/src/qt_gui/translations/ar.ts b/src/qt_gui/translations/ar.ts new file mode 100644 index 000000000..d35bbbf86 --- /dev/null +++ b/src/qt_gui/translations/ar.ts @@ -0,0 +1,915 @@ + + + + + + AboutDialog + + + About shadPS4 + حول shadPS4 + + + + shadPS4 + shadPS4 + + + + shadPS4 is an experimental open-source emulator for the PlayStation 4. + shadPS4 هو محاكي تجريبي مفتوح المصدر لجهاز PlayStation 4. + + + + This software should not be used to play games you have not legally obtained. + يجب عدم استخدام هذا البرنامج لتشغيل الألعاب التي لم تحصل عليها بشكل قانوني. + + + + ElfViewer + + + Open Folder + فتح المجلد + + + + GameInfoClass + + + Loading game list, please wait :3 + جارٍ تحميل قائمة الألعاب، يرجى الانتظار :3 + + + + Cancel + إلغاء + + + + Loading... + ...جارٍ التحميل + + + + GameInstallDialog + + + shadPS4 - Choose directory + shadPS4 - اختر المجلد + + + + Directory to install games + مجلد تثبيت الألعاب + + + + Browse + تصفح + + + + Error + خطأ + + + + The value for location to install games is not valid. + قيمة موقع تثبيت الألعاب غير صالحة. + + + + GuiContextMenus + + + Create Shortcut + إنشاء اختصار + + + + Open Game Folder + فتح مجلد اللعبة + + + + Cheats / Patches + الغش / التصحيحات + + + + SFO Viewer + عارض SFO + + + + Trophy Viewer + عارض الجوائز + + + + Copy info + نسخ المعلومات + + + + Copy Name + نسخ الاسم + + + + Copy Serial + نسخ الرقم التسلسلي + + + + Copy All + نسخ الكل + + + + Shortcut creation + إنشاء اختصار + + + + Shortcut created successfully!\n %1 + تم إنشاء الاختصار بنجاح!\n %1 + + + + Error + خطأ + + + + Error creating shortcut!\n %1 + !\n %1 خطأ في إنشاء الاختصار + + + + Install PKG + PKG تثبيت + + + + MainWindow + + + Open/Add Elf Folder + Elf فتح/إضافة مجلد + + + + Install Packages (PKG) + (PKG) تثبيت الحزم + + + + Boot Game + تشغيل اللعبة + + + + About shadPS4 + shadPS4 حول + + + + Configure... + ...تكوين + + + + Install application from a .pkg file + .pkg تثبيت التطبيق من ملف + + + + Recent Games + الألعاب الأخيرة + + + + Exit + خروج + + + + Exit shadPS4 + الخروج من shadPS4 + + + + Exit the application. + الخروج من التطبيق. + + + + Show Game List + إظهار قائمة الألعاب + + + + Game List Refresh + تحديث قائمة الألعاب + + + + Tiny + صغير جدًا + + + + Small + صغير + + + + Medium + متوسط + + + + Large + كبير + + + + List View + عرض القائمة + + + + Grid View + عرض الشبكة + + + + Elf Viewer + عارض Elf + + + + Game Install Directory + دليل تثبيت اللعبة + + + + Download Cheats/Patches + تنزيل الغش/التصحيحات + + + + Dump Game List + تفريغ قائمة الألعاب + + + + PKG Viewer + عارض PKG + + + + Search... + ...بحث + + + + File + ملف + + + + View + عرض + + + + Game List Icons + أيقونات قائمة الألعاب + + + + Game List Mode + وضع قائمة الألعاب + + + + Settings + الإعدادات + + + + Utils + الأدوات + + + + Themes + السمات + + + + About + حول + + + + Dark + داكن + + + + Light + فاتح + + + + Green + أخضر + + + + Blue + أزرق + + + + Violet + بنفسجي + + + + toolBar + شريط الأدوات + + + + PKGViewer + + + Open Folder + فتح المجلد + + + + TrophyViewer + + + Trophy Viewer + عارض الجوائز + + + + SettingsDialog + + + Settings + الإعدادات + + + + General + عام + + + + System + النظام + + + + Console Language + لغة وحدة التحكم + + + + Emulator Language + لغة المحاكي + + + + Emulator + المحاكي + + + + Enable Fullscreen + تمكين ملء الشاشة + + + + Show Splash + إظهار شاشة البداية + + + + Is PS4 Pro + PS4 Pro هل هو + + + + Username + اسم المستخدم + + + + Logger + المسجل + + + + Log Type + نوع السجل + + + + Log Filter + مرشح السجل + + + + Graphics + الرسومات + + + + Graphics Device + جهاز الرسومات + + + + Width + العرض + + + + Height + الارتفاع + + + + Vblank Divider + Vblank مقسم + + + + Advanced + متقدم + + + + Enable Shaders Dumping + تمكين تفريغ الشيدرات + + + + Enable NULL GPU + تمكين وحدة معالجة الرسومات الفارغة + + + + Enable PM4 Dumping + PM4 تمكين تفريغ + + + + Debug + تصحيح الأخطاء + + + + Enable Debug Dumping + تمكين تفريغ التصحيح + + + + Enable Vulkan Validation Layers + Vulkan تمكين طبقات التحقق من + + + + Enable Vulkan Synchronization Validation + Vulkan تمكين التحقق من تزامن + + + + Enable RenderDoc Debugging + RenderDoc تمكين تصحيح أخطاء + + + + MainWindow + + + * Unsupported Vulkan Version + * إصدار Vulkan غير مدعوم + + + + Download Cheats For All Installed Games + تنزيل الغش لجميع الألعاب المثبتة + + + + Download Patches For All Games + تنزيل التصحيحات لجميع الألعاب + + + + Download Complete + اكتمل التنزيل + + + + You have downloaded cheats for all the games you have installed. + لقد قمت بتنزيل الغش لجميع الألعاب التي قمت بتثبيتها. + + + + Patches Downloaded Successfully! + !تم تنزيل التصحيحات بنجاح + + + + All Patches available for all games have been downloaded. + .تم تنزيل جميع التصحيحات المتاحة لجميع الألعاب + + + + Games: + :الألعاب + + + + PKG File (*.PKG) + PKG (*.PKG) ملف + + + + ELF files (*.bin *.elf *.oelf) + ELF (*.bin *.elf *.oelf) ملفات + + + + Game Boot + تشغيل اللعبة + + + + Only one file can be selected! + !يمكن تحديد ملف واحد فقط + + + + PKG Extraction + PKG استخراج + + + + Patch detected! + تم اكتشاف تصحيح! + + + + PKG and Game versions match: + :واللعبة تتطابق إصدارات PKG + + + + Would you like to overwrite? + هل ترغب في الكتابة فوق الملف الموجود؟ + + + + PKG Version %1 is older than installed version: + :أقدم من الإصدار المثبت PKG Version %1 + + + + Game is installed: + :اللعبة مثبتة + + + + Would you like to install Patch: + :هل ترغب في تثبيت التصحيح + + + + DLC Installation + تثبيت المحتوى القابل للتنزيل + + + + Would you like to install DLC: %1? + هل ترغب في تثبيت المحتوى القابل للتنزيل: 1%؟ + + + + DLC already installed: + :المحتوى القابل للتنزيل مثبت بالفعل + + + + Game already installed + اللعبة مثبتة بالفعل + + + + PKG is a patch, please install the game first! + !PKG هو تصحيح، يرجى تثبيت اللعبة أولاً + + + + PKG ERROR + PKG خطأ في + + + + Extracting PKG %1/%2 + PKG %1/%2 جاري استخراج + + + + Extraction Finished + اكتمل الاستخراج + + + + Game successfully installed at %1 + تم تثبيت اللعبة بنجاح في %1 + + + + File doesn't appear to be a valid PKG file + يبدو أن الملف ليس ملف PKG صالحًا + + + + CheatsPatches + + + Cheats / Patches + الغش / التصحيحات + + + + defaultTextEdit_MSG + الغش والتصحيحات هي ميزات تجريبية. + استخدمها بحذر. + + قم بتنزيل الغش بشكل فردي عن طريق اختيار المستودع والنقر على زر التنزيل. + في علامة تبويب التصحيحات، يمكنك تنزيل جميع التصحيحات دفعة واحدة، واختيار ما تريد استخدامه، وحفظ اختياراتك. + + نظرًا لأننا لا نقوم بتطوير الغش/التصحيحات، + يرجى الإبلاغ عن أي مشاكل إلى مؤلف الغش. + + هل قمت بإنشاء غش جديد؟ قم بزيارة: + https://github.com/shadps4-emu/ps4_cheats + + + + No Image Available + لا تتوفر صورة + + + + Serial: + الرقم التسلسلي: + + + + Version: + الإصدار: + + + + Size: + الحجم: + + + + Select Cheat File: + اختر ملف الغش: + + + + Repository: + المستودع: + + + + Download Cheats + تنزيل الغش + + + + Delete File + حذف الملف + + + + No files selected. + لم يتم اختيار أي ملفات. + + + + You can delete the cheats you don't want after downloading them. + يمكنك حذف الغش الذي لا تريده بعد تنزيله. + + + + Do you want to delete the selected file?\n%1 + هل تريد حذف الملف المحدد؟ + %1 + + + + Select Patch File: + اختر ملف التصحيح: + + + + Download Patches + تنزيل التصحيحات + + + + Save + حفظ + + + + Cheats + الغش + + + + Patches + التصحيحات + + + + Error + خطأ + + + + No patch selected. + لم يتم اختيار أي تصحيح. + + + + Unable to open files.json for reading. + تعذر فتح files.json للقراءة. + + + + No patch file found for the current serial. + لم يتم العثور على ملف تصحيح للرقم التسلسلي الحالي. + + + + Unable to open the file for reading. + تعذر فتح الملف للقراءة. + + + + Unable to open the file for writing. + تعذر فتح الملف للكتابة. + + + + Failed to parse XML: + :فشل في تحليل XML + + + + Success + نجاح + + + + Options saved successfully. + تم حفظ الخيارات بنجاح. + + + + Invalid Source + مصدر غير صالح + + + + The selected source is invalid. + المصدر المحدد غير صالح. + + + + File Exists + الملف موجود + + + + File already exists. Do you want to replace it? + الملف موجود بالفعل. هل تريد استبداله؟ + + + + Failed to save file: + :فشل في حفظ الملف + + + + Failed to download file: + :فشل في تنزيل الملف + + + + Cheats Not Found + لم يتم العثور على الغش + + + + CheatsNotFound_MSG + لم يتم العثور على غش لهذه اللعبة في هذا الإصدار من المستودع المحدد. حاول استخدام مستودع آخر أو إصدار آخر من اللعبة. + + + + Cheats Downloaded Successfully + تم تنزيل الغش بنجاح + + + + CheatsDownloadedSuccessfully_MSG + لقد نجحت في تنزيل الغش لهذا الإصدار من اللعبة من المستودع المحدد. يمكنك محاولة التنزيل من مستودع آخر. إذا كان متاحًا، يمكنك اختياره عن طريق تحديد الملف من القائمة. + + + + Failed to save: + :فشل في الحفظ + + + + Failed to download: + :فشل في التنزيل + + + + Download Complete + اكتمل التنزيل + + + + DownloadComplete_MSG + تم تنزيل التصحيحات بنجاح! تم تنزيل جميع التصحيحات لجميع الألعاب، ولا داعي لتنزيلها بشكل فردي لكل لعبة كما هو الحال مع الغش. + + + + Failed to parse JSON data from HTML. + فشل في تحليل بيانات JSON من HTML. + + + + Failed to retrieve HTML page. + .HTML فشل في استرجاع صفحة + + + + Failed to open file: + :فشل في فتح الملف + + + + XML ERROR: + :خطأ في XML + + + + Failed to open files.json for writing + فشل في فتح files.json للكتابة + + + + Author: + :المؤلف + + + + Directory does not exist: + :المجلد غير موجود + + + + Failed to open files.json for reading. + فشل في فتح files.json للقراءة. + + + + Name: + :الاسم + + + diff --git a/src/qt_gui/translations/da_DK.ts b/src/qt_gui/translations/da_DK.ts new file mode 100644 index 000000000..c67d29b1d --- /dev/null +++ b/src/qt_gui/translations/da_DK.ts @@ -0,0 +1,902 @@ + + + + AboutDialog + + + About shadPS4 + About shadPS4 + + + + shadPS4 + shadPS4 + + + + shadPS4 is an experimental open-source emulator for the PlayStation 4. + shadPS4 is an experimental open-source emulator for the PlayStation 4. + + + + This software should not be used to play games you have not legally obtained. + This software should not be used to play games you have not legally obtained. + + + + ElfViewer + + + Open Folder + Open Folder + + + + GameInfoClass + + + Loading game list, please wait :3 + Loading game list, please wait :3 + + + + Cancel + Cancel + + + + Loading... + Loading... + + + + GameInstallDialog + + + shadPS4 - Choose directory + shadPS4 - Choose directory + + + + Directory to install games + Directory to install games + + + + Browse + Browse + + + + Error + Error + + + + The value for location to install games is not valid. + The value for location to install games is not valid. + + + + GuiContextMenus + + + Create Shortcut + Create Shortcut + + + + Open Game Folder + Open Game Folder + + + + Cheats / Patches + Trick / Patches + + + + SFO Viewer + SFO Viewer + + + + Trophy Viewer + Trophy Viewer + + + + Copy info + Copy info + + + + Copy Name + Copy Name + + + + Copy Serial + Copy Serial + + + + Copy All + Copy All + + + + Shortcut creation + Shortcut creation + + + + Shortcut created successfully!\n %1 + Shortcut created successfully!\n %1 + + + + Error + Error + + + + Error creating shortcut!\n %1 + Error creating shortcut!\n %1 + + + + Install PKG + Install PKG + + + + MainWindow + + + Open/Add Elf Folder + Open/Add Elf Folder + + + + Install Packages (PKG) + Install Packages (PKG) + + + + Boot Game + Boot Game + + + + About shadPS4 + About shadPS4 + + + + Configure... + Configure... + + + + Install application from a .pkg file + Install application from a .pkg file + + + + Recent Games + Recent Games + + + + Exit + Exit + + + + Exit shadPS4 + Exit shadPS4 + + + + Exit the application. + Exit the application. + + + + Show Game List + Show Game List + + + + Game List Refresh + Game List Refresh + + + + Tiny + Tiny + + + + Small + Small + + + + Medium + Medium + + + + Large + Large + + + + List View + List View + + + + Grid View + Grid View + + + + Elf Viewer + Elf Viewer + + + + Game Install Directory + Game Install Directory + + + + Download Cheats/Patches + Download Tricks / Patches + + + + Dump Game List + Dump Game List + + + + PKG Viewer + PKG Viewer + + + + Search... + Search... + + + + File + File + + + + View + View + + + + Game List Icons + Game List Icons + + + + Game List Mode + Game List Mode + + + + Settings + Settings + + + + Utils + Utils + + + + Themes + Themes + + + + About + About + + + + Dark + Dark + + + + Light + Light + + + + Green + Green + + + + Blue + Blue + + + + Violet + Violet + + + + toolBar + toolBar + + + + PKGViewer + + + Open Folder + Open Folder + + + + TrophyViewer + + + Trophy Viewer + Trophy Viewer + + + + SettingsDialog + + + Settings + Settings + + + + General + General + + + + System + System + + + + Console Language + Console Language + + + + Emulator Language + Emulator Language + + + + Emulator + Emulator + + + + Enable Fullscreen + Enable Fullscreen + + + + Show Splash + Show Splash + + + + Is PS4 Pro + Is PS4 Pro + + + + Username + Username + + + + Logger + Logger + + + + Log Type + Log Type + + + + Log Filter + Log Filter + + + + Graphics + Graphics + + + + Graphics Device + Graphics Device + + + + Width + Width + + + + Height + Height + + + + Vblank Divider + Vblank Divider + + + + Advanced + Advanced + + + + Enable Shaders Dumping + Enable Shaders Dumping + + + + Enable NULL GPU + Enable NULL GPU + + + + Enable PM4 Dumping + Enable PM4 Dumping + + + + Debug + Debug + + + + Enable Debug Dumping + Enable Debug Dumping + + + + Enable Vulkan Validation Layers + Enable Vulkan Validation Layers + + + + Enable Vulkan Synchronization Validation + Enable Vulkan Synchronization Validation + + + + Enable RenderDoc Debugging + Enable RenderDoc Debugging + + + + MainWindow + + + * Unsupported Vulkan Version + * Ikke understøttet Vulkan-version + + + + Download Cheats For All Installed Games + Hent snyd til alle installerede spil + + + + Download Patches For All Games + Hent patches til alle spil + + + + Download Complete + Download fuldført + + + + You have downloaded cheats for all the games you have installed. + Du har hentet snyd til alle de spil, du har installeret. + + + + Patches Downloaded Successfully! + Patcher hentet med succes! + + + + All Patches available for all games have been downloaded. + Alle patches til alle spil er blevet hentet. + + + + Games: + Spil: + + + + PKG File (*.PKG) + PKG-fil (*.PKG) + + + + ELF files (*.bin *.elf *.oelf) + ELF-filer (*.bin *.elf *.oelf) + + + + Game Boot + Spil-boot + + + + Only one file can be selected! + Kun én fil kan vælges! + + + + PKG Extraction + PKG-udtrækning + + + + Patch detected! + Opdatering detekteret! + + + + PKG and Game versions match: + PKG og spilversioner matcher: + + + + Would you like to overwrite? + Vil du overskrive? + + + + PKG Version %1 is older than installed version: + PKG Version %1 er ældre end den installerede version: + + + + Game is installed: + Spillet er installeret: + + + + Would you like to install Patch: + Vil du installere opdateringen: + + + + DLC Installation + DLC Installation + + + + Would you like to install DLC: %1? + Vil du installere DLC: %1? + + + + DLC already installed: + DLC allerede installeret: + + + + Game already installed + Spillet er allerede installeret + + + + PKG is a patch, please install the game first! + PKG er en patch, venligst installer spillet først! + + + + PKG ERROR + PKG FEJL + + + + Extracting PKG %1/%2 + Udvinding af PKG %1/%2 + + + + Extraction Finished + Udvinding afsluttet + + + + Game successfully installed at %1 + Spillet blev installeret succesfuldt på %1 + + + + File doesn't appear to be a valid PKG file + Filen ser ikke ud til at være en gyldig PKG-fil + + + + CheatsPatches + + + Cheats / Patches + Snyd / Patches + + + + defaultTextEdit_MSG + Cheats/Patches er eksperimentelle.\nBrug med forsigtighed.\n\nDownload cheats individuelt ved at vælge lageret og klikke på download-knappen.\nUnder fanen Patches kan du downloade alle patches på én gang, vælge hvilke du vil bruge og gemme valget.\n\nDa vi ikke udvikler cheats/patches,\nvenligst rapporter problemer til cheat-udvikleren.\n\nHar du lavet en ny cheat? Besøg:\nhttps://github.com/shadps4-emu/ps4_cheats + + + + No Image Available + Ingen billede tilgængelig + + + + Serial: + Serienummer: + + + + Version: + Version: + + + + Size: + Størrelse: + + + + Select Cheat File: + Vælg snyd-fil: + + + + Repository: + Repository: + + + + Download Cheats + Hent snyd + + + + Delete File + Slet fil + + + + No files selected. + Ingen filer valgt. + + + + You can delete the cheats you don't want after downloading them. + Du kan slette de snyd, du ikke ønsker, efter at have hentet dem. + + + + Do you want to delete the selected file?\n%1 + Ønsker du at slette den valgte fil?\n%1 + + + + Select Patch File: + Vælg patch-fil: + + + + Download Patches + Hent patches + + + + Save + Gem + + + + Cheats + Snyd + + + + Patches + Patches + + + + Error + Fejl + + + + No patch selected. + Ingen patch valgt. + + + + Unable to open files.json for reading. + Kan ikke åbne files.json til læsning. + + + + No patch file found for the current serial. + Ingen patch-fil fundet for det nuværende serienummer. + + + + Unable to open the file for reading. + Kan ikke åbne filen til læsning. + + + + Unable to open the file for writing. + Kan ikke åbne filen til skrivning. + + + + Failed to parse XML: + Kunne ikke analysere XML: + + + + Success + Succes + + + + Options saved successfully. + Indstillinger gemt med succes. + + + + Invalid Source + Ugyldig kilde + + + + The selected source is invalid. + Den valgte kilde er ugyldig. + + + + File Exists + Fil findes + + + + File already exists. Do you want to replace it? + Filen findes allerede. Vil du erstatte den? + + + + Failed to save file: + Kunne ikke gemme fil: + + + + Failed to download file: + Kunne ikke hente fil: + + + + Cheats Not Found + Snyd ikke fundet + + + + CheatsNotFound_MSG + Ingen snyd fundet til dette spil i denne version af det valgte repository, prøv et andet repository eller en anden version af spillet. + + + + Cheats Downloaded Successfully + Snyd hentet med succes + + + + CheatsDownloadedSuccessfully_MSG + Du har succesfuldt hentet snyd for denne version af spillet fra det valgte repository. Du kan prøve at hente fra et andet repository, hvis det er tilgængeligt, vil det også være muligt at bruge det ved at vælge filen fra listen. + + + + Failed to save: + Kunne ikke gemme: + + + + Failed to download: + Kunne ikke hente: + + + + Download Complete + Download fuldført + + + + DownloadComplete_MSG + Patcher hentet med succes! Alle patches til alle spil er blevet hentet, der er ikke behov for at hente dem individuelt for hvert spil, som det sker med snyd. + + + + Failed to parse JSON data from HTML. + Kunne ikke analysere JSON-data fra HTML. + + + + Failed to retrieve HTML page. + Kunne ikke hente HTML-side. + + + + Failed to open file: + Kunne ikke åbne fil: + + + + XML ERROR: + XML FEJL: + + + + Failed to open files.json for writing + Kunne ikke åbne files.json til skrivning + + + + Author: + Forfatter: + + + + Directory does not exist: + Mappe findes ikke: + + + + Failed to open files.json for reading. + Kunne ikke åbne files.json til læsning. + + + + Name: + Navn: + + + \ No newline at end of file diff --git a/src/qt_gui/translations/de.ts b/src/qt_gui/translations/de.ts new file mode 100644 index 000000000..c208ad441 --- /dev/null +++ b/src/qt_gui/translations/de.ts @@ -0,0 +1,902 @@ + + + + AboutDialog + + + About shadPS4 + Über shadPS4 + + + + shadPS4 + shadPS4 + + + + shadPS4 is an experimental open-source emulator for the PlayStation 4. + shadPS4 ist ein experimenteller Open-Source-Emulator für die Playstation 4. + + + + This software should not be used to play games you have not legally obtained. + Diese Software soll nicht dazu benutzt werden illegal kopierte Spiele zu spielen. + + + + ElfViewer + + + Open Folder + Ordner öffnen + + + + GameInfoClass + + + Loading game list, please wait :3 + Lade Spielliste, bitte warten :3 + + + + Cancel + Abbrechen + + + + Loading... + Lade... + + + + GameInstallDialog + + + shadPS4 - Choose directory + shadPS4 - Wähle Ordner + + + + Directory to install games + Installationsverzeichnis für Spiele + + + + Browse + Durchsuchen + + + + Error + Fehler + + + + The value for location to install games is not valid. + Der ausgewählte Ordner ist nicht gültig. + + + + GuiContextMenus + + + Create Shortcut + Verknüpfung erstellen + + + + Open Game Folder + Spieleordner öffnen + + + + Cheats / Patches + Cheats / Patches + + + + SFO Viewer + SFO anzeigen + + + + Trophy Viewer + Trophäen anzeigen + + + + Copy info + Infos kopieren + + + + Copy Name + Namen kopieren + + + + Copy Serial + Seriennummer kopieren + + + + Copy All + Alles kopieren + + + + Shortcut creation + Verknüpfungserstellung + + + + Shortcut created successfully!\n %1 + Verknüpfung erfolgreich erstellt!\n %1 + + + + Error + Fehler + + + + Error creating shortcut!\n %1 + Fehler beim Erstellen der Verknüpfung!\n %1 + + + + Install PKG + PKG installieren + + + + MainWindow + + + Open/Add Elf Folder + Elf-Ordner öffnen/hinzufügen + + + + Install Packages (PKG) + Pakete installieren (PKG) + + + + Boot Game + Spiel starten + + + + About shadPS4 + Über shadPS4 + + + + Configure... + Konfigurieren... + + + + Install application from a .pkg file + Installiere Anwendung aus .pkg-Datei + + + + Recent Games + Zuletzt gespielt + + + + Exit + Beenden + + + + Exit shadPS4 + shadPS4 beenden + + + + Exit the application. + Die Anwendung beenden. + + + + Show Game List + Spielliste anzeigen + + + + Game List Refresh + Spielliste aktualisieren + + + + Tiny + Winzig + + + + Small + Klein + + + + Medium + Mittel + + + + Large + Groß + + + + List View + Listenansicht + + + + Grid View + Gitteransicht + + + + Elf Viewer + Elf-Ansicht + + + + Game Install Directory + Installationsverzeichnis für Spiele + + + + Download Cheats/Patches + Cheats / Patches herunterladen + + + + Dump Game List + Spielliste ausgeben + + + + PKG Viewer + PKG-Ansicht + + + + Search... + Suchen... + + + + File + Datei + + + + View + Ansicht + + + + Game List Icons + Game List Icons + + + + Game List Mode + Spiellisten-Symoble + + + + Settings + Einstellungen + + + + Utils + Werkzeuge + + + + Themes + Stile + + + + About + Über + + + + Dark + Dunkel + + + + Light + Hell + + + + Green + Grün + + + + Blue + Blau + + + + Violet + Violett + + + + toolBar + toolBar + + + + PKGViewer + + + Open Folder + Ordner öffnen + + + + TrophyViewer + + + Trophy Viewer + Trophäenansicht + + + + SettingsDialog + + + Settings + Einstellungen + + + + General + Allgemein + + + + System + System + + + + Console Language + Konsolensprache + + + + Emulator Language + Emulatorsprache + + + + Emulator + Emulator + + + + Enable Fullscreen + Vollbild aktivieren + + + + Show Splash + Startbildschirm anzeigen + + + + Is PS4 Pro + Ist PS4 Pro + + + + Username + Benutzername + + + + Logger + Logger + + + + Log Type + Logtyp + + + + Log Filter + Log-Filter + + + + Graphics + Grafik + + + + Graphics Device + Grafikgerät + + + + Width + Breite + + + + Height + Höhe + + + + Vblank Divider + Vblank-Teiler + + + + Advanced + Erweitert + + + + Enable Shaders Dumping + Shader-Dumping aktivieren + + + + Enable NULL GPU + NULL GPU aktivieren + + + + Enable PM4 Dumping + PM4-Dumping aktivieren + + + + Debug + Debug + + + + Enable Debug Dumping + Debug-Dumping aktivieren + + + + Enable Vulkan Validation Layers + Vulkan Validations-Ebenen aktivieren + + + + Enable Vulkan Synchronization Validation + Vulkan Synchronisations-Validierung aktivieren + + + + Enable RenderDoc Debugging + RenderDoc-Debugging aktivieren + + + + MainWindow + + + * Unsupported Vulkan Version + * Nicht unterstützte Vulkan-Version + + + + Download Cheats For All Installed Games + Cheats für alle installierten Spiele herunterladen + + + + Download Patches For All Games + Patches für alle Spiele herunterladen + + + + Download Complete + Download abgeschlossen + + + + You have downloaded cheats for all the games you have installed. + Sie haben Cheats für alle installierten Spiele heruntergeladen. + + + + Patches Downloaded Successfully! + Patches erfolgreich heruntergeladen! + + + + All Patches available for all games have been downloaded. + Alle Patches für alle Spiele wurden heruntergeladen. + + + + Games: + Spiele: + + + + PKG File (*.PKG) + PKG-Datei (*.PKG) + + + + ELF files (*.bin *.elf *.oelf) + ELF-Dateien (*.bin *.elf *.oelf) + + + + Game Boot + Spiel-Start + + + + Only one file can be selected! + Es kann nur eine Datei ausgewählt werden! + + + + PKG Extraction + PKG-Extraktion + + + + Patch detected! + Patch erkannt! + + + + PKG and Game versions match: + PKG- und Spielversionen stimmen überein: + + + + Would you like to overwrite? + Willst du überschreiben? + + + + PKG Version %1 is older than installed version: + PKG-Version %1 ist älter als die installierte Version: + + + + Game is installed: + Spiel ist installiert: + + + + Would you like to install Patch: + Willst du den Patch installieren: + + + + DLC Installation + DLC-Installation + + + + Would you like to install DLC: %1? + Willst du den DLC installieren: %1? + + + + DLC already installed: + DLC bereits installiert: + + + + Game already installed + Spiel bereits installiert + + + + PKG is a patch, please install the game first! + PKG ist ein Patch, bitte installieren Sie zuerst das Spiel! + + + + PKG ERROR + PKG-FEHLER + + + + Extracting PKG %1/%2 + Extrahiere PKG %1/%2 + + + + Extraction Finished + Extraktion abgeschlossen + + + + Game successfully installed at %1 + Spiel erfolgreich installiert auf %1 + + + + File doesn't appear to be a valid PKG file + Die Datei scheint keine gültige PKG-Datei zu sein + + + + CheatsPatches + + + Cheats / Patches + Cheats / Patches + + + + defaultTextEdit_MSG + Cheats/Patches sind experimentell.\nVerwende sie mit Vorsicht.\n\nLade Cheats einzeln herunter, indem du das Repository auswählst und auf die Download-Schaltfläche klickst.\nAuf der Registerkarte Patches kannst du alle Patches auf einmal herunterladen, auswählen, welche du verwenden möchtest, und die Auswahl speichern.\n\nDa wir die Cheats/Patches nicht entwickeln,\nbitte melde Probleme an den Cheat-Autor.\n\nHast du einen neuen Cheat erstellt? Besuche:\nhttps://github.com/shadps4-emu/ps4_cheats + + + + No Image Available + Kein Bild verfügbar + + + + Serial: + Seriennummer: + + + + Version: + Version: + + + + Size: + Größe: + + + + Select Cheat File: + Cheat-Datei auswählen: + + + + Repository: + Repository: + + + + Download Cheats + Cheats herunterladen + + + + Delete File + Datei löschen + + + + No files selected. + Keine Dateien ausgewählt. + + + + You can delete the cheats you don't want after downloading them. + Du kannst die Cheats, die du nicht möchtest, nach dem Herunterladen löschen. + + + + Do you want to delete the selected file?\n%1 + Willst du die ausgewählte Datei löschen?\n%1 + + + + Select Patch File: + Patch-Datei auswählen: + + + + Download Patches + Patches herunterladen + + + + Save + Speichern + + + + Cheats + Cheats + + + + Patches + Patches + + + + Error + Fehler + + + + No patch selected. + Kein Patch ausgewählt. + + + + Unable to open files.json for reading. + Kann files.json nicht zum Lesen öffnen. + + + + No patch file found for the current serial. + Keine Patch-Datei für die aktuelle Seriennummer gefunden. + + + + Unable to open the file for reading. + Kann die Datei nicht zum Lesen öffnen. + + + + Unable to open the file for writing. + Kann die Datei nicht zum Schreiben öffnen. + + + + Failed to parse XML: + Fehler beim Parsen von XML: + + + + Success + Erfolg + + + + Options saved successfully. + Optionen erfolgreich gespeichert. + + + + Invalid Source + Ungültige Quelle + + + + The selected source is invalid. + Die ausgewählte Quelle ist ungültig. + + + + File Exists + Datei existiert + + + + File already exists. Do you want to replace it? + Datei existiert bereits. Möchtest du sie ersetzen? + + + + Failed to save file: + Fehler beim Speichern der Datei: + + + + Failed to download file: + Fehler beim Herunterladen der Datei: + + + + Cheats Not Found + Cheats nicht gefunden + + + + CheatsNotFound_MSG + Keine Cheats für dieses Spiel in dieser Version des gewählten Repositories gefunden. Versuche es mit einem anderen Repository oder einer anderen Version des Spiels. + + + + Cheats Downloaded Successfully + Cheats erfolgreich heruntergeladen + + + + CheatsDownloadedSuccessfully_MSG + Du hast erfolgreich Cheats für diese Version des Spiels aus dem gewählten Repository heruntergeladen. Du kannst auch versuchen, Cheats von einem anderen Repository herunterzuladen. Wenn verfügbar, kannst du sie auswählen, indem du die Datei aus der Liste auswählst. + + + + Failed to save: + Speichern fehlgeschlagen: + + + + Failed to download: + Herunterladen fehlgeschlagen: + + + + Download Complete + Download abgeschlossen + + + + DownloadComplete_MSG + Patches erfolgreich heruntergeladen! Alle Patches für alle Spiele wurden heruntergeladen, es ist nicht notwendig, sie einzeln für jedes Spiel herunterzuladen, wie es bei Cheats der Fall ist. + + + + Failed to parse JSON data from HTML. + Fehler beim Parsen der JSON-Daten aus HTML. + + + + Failed to retrieve HTML page. + Fehler beim Abrufen der HTML-Seite. + + + + Failed to open file: + Fehler beim Öffnen der Datei: + + + + XML ERROR: + XML-Fehler: + + + + Failed to open files.json for writing + Kann files.json nicht zum Schreiben öffnen + + + + Author: + Autor: + + + + Directory does not exist: + Verzeichnis existiert nicht: + + + + Failed to open files.json for reading. + Kann files.json nicht zum Lesen öffnen. + + + + Name: + Name: + + + \ No newline at end of file diff --git a/src/qt_gui/translations/el.ts b/src/qt_gui/translations/el.ts new file mode 100644 index 000000000..ef831fb09 --- /dev/null +++ b/src/qt_gui/translations/el.ts @@ -0,0 +1,902 @@ + + + + AboutDialog + + + About shadPS4 + About shadPS4 + + + + shadPS4 + shadPS4 + + + + shadPS4 is an experimental open-source emulator for the PlayStation 4. + shadPS4 is an experimental open-source emulator for the PlayStation 4. + + + + This software should not be used to play games you have not legally obtained. + This software should not be used to play games you have not legally obtained. + + + + ElfViewer + + + Open Folder + Open Folder + + + + GameInfoClass + + + Loading game list, please wait :3 + Loading game list, please wait :3 + + + + Cancel + Cancel + + + + Loading... + Loading... + + + + GameInstallDialog + + + shadPS4 - Choose directory + shadPS4 - Choose directory + + + + Directory to install games + Directory to install games + + + + Browse + Browse + + + + Error + Error + + + + The value for location to install games is not valid. + The value for location to install games is not valid. + + + + GuiContextMenus + + + Create Shortcut + Create Shortcut + + + + Open Game Folder + Open Game Folder + + + + Cheats / Patches + Kodikí / Enimeróseis + + + + SFO Viewer + SFO Viewer + + + + Trophy Viewer + Trophy Viewer + + + + Copy info + Copy info + + + + Copy Name + Copy Name + + + + Copy Serial + Copy Serial + + + + Copy All + Copy All + + + + Shortcut creation + Shortcut creation + + + + Shortcut created successfully!\n %1 + Shortcut created successfully!\n %1 + + + + Error + Error + + + + Error creating shortcut!\n %1 + Error creating shortcut!\n %1 + + + + Install PKG + Install PKG + + + + MainWindow + + + Open/Add Elf Folder + Open/Add Elf Folder + + + + Install Packages (PKG) + Install Packages (PKG) + + + + Boot Game + Boot Game + + + + About shadPS4 + About shadPS4 + + + + Configure... + Configure... + + + + Install application from a .pkg file + Install application from a .pkg file + + + + Recent Games + Recent Games + + + + Exit + Exit + + + + Exit shadPS4 + Exit shadPS4 + + + + Exit the application. + Exit the application. + + + + Show Game List + Show Game List + + + + Game List Refresh + Game List Refresh + + + + Tiny + Tiny + + + + Small + Small + + + + Medium + Medium + + + + Large + Large + + + + List View + List View + + + + Grid View + Grid View + + + + Elf Viewer + Elf Viewer + + + + Game Install Directory + Game Install Directory + + + + Download Cheats/Patches + Κατεβάστε Κωδικούς / Ενημερώσεις + + + + Dump Game List + Dump Game List + + + + PKG Viewer + PKG Viewer + + + + Search... + Search... + + + + File + File + + + + View + View + + + + Game List Icons + Game List Icons + + + + Game List Mode + Game List Mode + + + + Settings + Settings + + + + Utils + Utils + + + + Themes + Themes + + + + About + About + + + + Dark + Dark + + + + Light + Light + + + + Green + Green + + + + Blue + Blue + + + + Violet + Violet + + + + toolBar + toolBar + + + + PKGViewer + + + Open Folder + Open Folder + + + + TrophyViewer + + + Trophy Viewer + Trophy Viewer + + + + SettingsDialog + + + Settings + Settings + + + + General + General + + + + System + System + + + + Console Language + Console Language + + + + Emulator Language + Emulator Language + + + + Emulator + Emulator + + + + Enable Fullscreen + Enable Fullscreen + + + + Show Splash + Show Splash + + + + Is PS4 Pro + Is PS4 Pro + + + + Username + Username + + + + Logger + Logger + + + + Log Type + Log Type + + + + Log Filter + Log Filter + + + + Graphics + Graphics + + + + Graphics Device + Graphics Device + + + + Width + Width + + + + Height + Height + + + + Vblank Divider + Vblank Divider + + + + Advanced + Advanced + + + + Enable Shaders Dumping + Enable Shaders Dumping + + + + Enable NULL GPU + Enable NULL GPU + + + + Enable PM4 Dumping + Enable PM4 Dumping + + + + Debug + Debug + + + + Enable Debug Dumping + Enable Debug Dumping + + + + Enable Vulkan Validation Layers + Enable Vulkan Validation Layers + + + + Enable Vulkan Synchronization Validation + Enable Vulkan Synchronization Validation + + + + Enable RenderDoc Debugging + Enable RenderDoc Debugging + + + + MainWindow + + + * Unsupported Vulkan Version + * Μη υποστηριζόμενη έκδοση Vulkan + + + + Download Cheats For All Installed Games + Λήψη Cheats για όλα τα εγκατεστημένα παιχνίδια + + + + Download Patches For All Games + Λήψη Patches για όλα τα παιχνίδια + + + + Download Complete + Η λήψη ολοκληρώθηκε + + + + You have downloaded cheats for all the games you have installed. + Έχετε κατεβάσει cheats για όλα τα εγκατεστημένα παιχνίδια. + + + + Patches Downloaded Successfully! + Τα Patches κατέβηκαν επιτυχώς! + + + + All Patches available for all games have been downloaded. + Όλα τα διαθέσιμα Patches για όλα τα παιχνίδια έχουν κατέβει. + + + + Games: + Παιχνίδια: + + + + PKG File (*.PKG) + Αρχείο PKG (*.PKG) + + + + ELF files (*.bin *.elf *.oelf) + Αρχεία ELF (*.bin *.elf *.oelf) + + + + Game Boot + Εκκίνηση παιχνιδιού + + + + Only one file can be selected! + Μπορεί να επιλεγεί μόνο ένα αρχείο! + + + + PKG Extraction + Εξαγωγή PKG + + + + Patch detected! + Αναγνώριση ενημέρωσης! + + + + PKG and Game versions match: + Οι εκδόσεις PKG και παιχνιδιού ταιριάζουν: + + + + Would you like to overwrite? + Θέλετε να αντικαταστήσετε; + + + + PKG Version %1 is older than installed version: + Η έκδοση PKG %1 είναι παλαιότερη από την εγκατεστημένη έκδοση: + + + + Game is installed: + Το παιχνίδι είναι εγκατεστημένο: + + + + Would you like to install Patch: + Θέλετε να εγκαταστήσετε την ενημέρωση: + + + + DLC Installation + Εγκατάσταση DLC + + + + Would you like to install DLC: %1? + Θέλετε να εγκαταστήσετε το DLC: %1; + + + + DLC already installed: + DLC ήδη εγκατεστημένο: + + + + Game already installed + Παιχνίδι ήδη εγκατεστημένο + + + + PKG is a patch, please install the game first! + Το PKG είναι patch, παρακαλώ εγκαταστήστε πρώτα το παιχνίδι! + + + + PKG ERROR + ΣΦΑΛΜΑ PKG + + + + Extracting PKG %1/%2 + Εξαγωγή PKG %1/%2 + + + + Extraction Finished + Η εξαγωγή ολοκληρώθηκε + + + + Game successfully installed at %1 + Το παιχνίδι εγκαταστάθηκε επιτυχώς στο %1 + + + + File doesn't appear to be a valid PKG file + Η αρχείο δεν φαίνεται να είναι έγκυρο αρχείο PKG + + + + CheatsPatches + + + Cheats / Patches + Cheats / Patches + + + + defaultTextEdit_MSG + Οι cheats/patches είναι πειραματικά.\nΧρησιμοποιήστε τα με προσοχή.\n\nΚατεβάστε τους cheats μεμονωμένα επιλέγοντας το αποθετήριο και κάνοντας κλικ στο κουμπί λήψης.\nΣτην καρτέλα Patches, μπορείτε να κατεβάσετε όλα τα patches ταυτόχρονα, να επιλέξετε ποια θέλετε να χρησιμοποιήσετε και να αποθηκεύσετε την επιλογή.\n\nΔεδομένου ότι δεν αναπτύσσουμε τους cheats/patches,\nπαρακαλώ αναφέρετε προβλήματα στον δημιουργό του cheat.\n\nΔημιουργήσατε ένα νέο cheat; Επισκεφθείτε:\nhttps://github.com/shadps4-emu/ps4_cheats + + + + No Image Available + Δεν διατίθεται εικόνα + + + + Serial: + Σειριακός αριθμός: + + + + Version: + Έκδοση: + + + + Size: + Μέγεθος: + + + + Select Cheat File: + Επιλέξτε αρχείο Cheat: + + + + Repository: + Αποθετήριο: + + + + Download Cheats + Λήψη Cheats + + + + Delete File + Διαγραφή αρχείου + + + + No files selected. + Δεν έχουν επιλεγεί αρχεία. + + + + You can delete the cheats you don't want after downloading them. + Μπορείτε να διαγράψετε τα cheats που δεν θέλετε μετά τη λήψη τους. + + + + Do you want to delete the selected file?\n%1 + Θέλετε να διαγράψετε το επιλεγμένο αρχείο;\n%1 + + + + Select Patch File: + Επιλέξτε αρχείο Patch: + + + + Download Patches + Λήψη Patches + + + + Save + Αποθήκευση + + + + Cheats + Cheats + + + + Patches + Patches + + + + Error + Σφάλμα + + + + No patch selected. + Δεν έχει επιλεγεί κανένα patch. + + + + Unable to open files.json for reading. + Αδυναμία ανοίγματος του files.json για ανάγνωση. + + + + No patch file found for the current serial. + Δεν βρέθηκε αρχείο patch για τον τρέχοντα σειριακό αριθμό. + + + + Unable to open the file for reading. + Αδυναμία ανοίγματος του αρχείου για ανάγνωση. + + + + Unable to open the file for writing. + Αδυναμία ανοίγματος του αρχείου για εγγραφή. + + + + Failed to parse XML: + Αποτυχία ανάλυσης XML: + + + + Success + Επιτυχία + + + + Options saved successfully. + Οι ρυθμίσεις αποθηκεύτηκαν επιτυχώς. + + + + Invalid Source + Μη έγκυρη Πηγή + + + + The selected source is invalid. + Η επιλεγμένη πηγή είναι μη έγκυρη. + + + + File Exists + Η αρχείο υπάρχει + + + + File already exists. Do you want to replace it? + Η αρχείο υπάρχει ήδη. Θέλετε να την αντικαταστήσετε; + + + + Failed to save file: + Αποτυχία αποθήκευσης αρχείου: + + + + Failed to download file: + Αποτυχία λήψης αρχείου: + + + + Cheats Not Found + Δεν βρέθηκαν Cheats + + + + CheatsNotFound_MSG + Δεν βρέθηκαν cheats για αυτό το παιχνίδι στην τρέχουσα έκδοση του επιλεγμένου αποθετηρίου. Δοκιμάστε να κατεβάσετε από άλλο αποθετήριο ή άλλη έκδοση του παιχνιδιού. + + + + Cheats Downloaded Successfully + Cheats κατεβάστηκαν επιτυχώς + + + + CheatsDownloadedSuccessfully_MSG + Κατεβάσατε επιτυχώς cheats για αυτή την έκδοση του παιχνιδιού από το επιλεγμένο αποθετήριο. Μπορείτε να δοκιμάσετε να κατεβάσετε από άλλο αποθετήριο. Αν είναι διαθέσιμο, μπορείτε να το επιλέξετε επιλέγοντας το αρχείο από τη λίστα. + + + + Failed to save: + Αποτυχία αποθήκευσης: + + + + Failed to download: + Αποτυχία λήψης: + + + + Download Complete + Η λήψη ολοκληρώθηκε + + + + DownloadComplete_MSG + Τα Patches κατεβάστηκαν επιτυχώς! Όλα τα Patches για όλα τα παιχνίδια έχουν κατέβει, δεν είναι απαραίτητο να τα κατεβάσετε ένα-ένα για κάθε παιχνίδι, όπως με τα Cheats. + + + + Failed to parse JSON data from HTML. + Αποτυχία ανάλυσης δεδομένων JSON από HTML. + + + + Failed to retrieve HTML page. + Αποτυχία ανάκτησης σελίδας HTML. + + + + Failed to open file: + Αποτυχία ανοίγματος αρχείου: + + + + XML ERROR: + ΣΦΑΛΜΑ XML: + + + + Failed to open files.json for writing + Αποτυχία ανοίγματος του files.json για εγγραφή + + + + Author: + Συγγραφέας: + + + + Directory does not exist: + Ο φάκελος δεν υπάρχει: + + + + Failed to open files.json for reading. + Αποτυχία ανοίγματος του files.json για ανάγνωση. + + + + Name: + Όνομα: + + + \ No newline at end of file diff --git a/src/qt_gui/translations/en.ts b/src/qt_gui/translations/en.ts new file mode 100644 index 000000000..b3c3b699b --- /dev/null +++ b/src/qt_gui/translations/en.ts @@ -0,0 +1,902 @@ + + + + AboutDialog + + + About shadPS4 + About shadPS4 + + + + shadPS4 + shadPS4 + + + + shadPS4 is an experimental open-source emulator for the PlayStation 4. + shadPS4 is an experimental open-source emulator for the PlayStation 4. + + + + This software should not be used to play games you have not legally obtained. + This software should not be used to play games you have not legally obtained. + + + + ElfViewer + + + Open Folder + Open Folder + + + + GameInfoClass + + + Loading game list, please wait :3 + Loading game list, please wait :3 + + + + Cancel + Cancel + + + + Loading... + Loading... + + + + GameInstallDialog + + + shadPS4 - Choose directory + shadPS4 - Choose directory + + + + Directory to install games + Directory to install games + + + + Browse + Browse + + + + Error + Error + + + + The value for location to install games is not valid. + The value for location to install games is not valid. + + + + GuiContextMenus + + + Create Shortcut + Create Shortcut + + + + Open Game Folder + Open Game Folder + + + + Cheats / Patches + Cheats / Patches + + + + SFO Viewer + SFO Viewer + + + + Trophy Viewer + Trophy Viewer + + + + Copy info + Copy info + + + + Copy Name + Copy Name + + + + Copy Serial + Copy Serial + + + + Copy All + Copy All + + + + Shortcut creation + Shortcut creation + + + + Shortcut created successfully!\n %1 + Shortcut created successfully!\n %1 + + + + Error + Error + + + + Error creating shortcut!\n %1 + Error creating shortcut!\n %1 + + + + Install PKG + Install PKG + + + + MainWindow + + + Open/Add Elf Folder + Open/Add Elf Folder + + + + Install Packages (PKG) + Install Packages (PKG) + + + + Boot Game + Boot Game + + + + About shadPS4 + About shadPS4 + + + + Configure... + Configure... + + + + Install application from a .pkg file + Install application from a .pkg file + + + + Recent Games + Recent Games + + + + Exit + Exit + + + + Exit shadPS4 + Exit shadPS4 + + + + Exit the application. + Exit the application. + + + + Show Game List + Show Game List + + + + Game List Refresh + Game List Refresh + + + + Tiny + Tiny + + + + Small + Small + + + + Medium + Medium + + + + Large + Large + + + + List View + List View + + + + Grid View + Grid View + + + + Elf Viewer + Elf Viewer + + + + Game Install Directory + Game Install Directory + + + + Download Cheats/Patches + Download Cheats / Patches + + + + Dump Game List + Dump Game List + + + + PKG Viewer + PKG Viewer + + + + Search... + Search... + + + + File + File + + + + View + View + + + + Game List Icons + Game List Icons + + + + Game List Mode + Game List Mode + + + + Settings + Settings + + + + Utils + Utils + + + + Themes + Themes + + + + About + About + + + + Dark + Dark + + + + Light + Light + + + + Green + Green + + + + Blue + Blue + + + + Violet + Violet + + + + toolBar + toolBar + + + + PKGViewer + + + Open Folder + Open Folder + + + + TrophyViewer + + + Trophy Viewer + Trophy Viewer + + + + SettingsDialog + + + Settings + Settings + + + + General + General + + + + System + System + + + + Console Language + Console Language + + + + Emulator Language + Emulator Language + + + + Emulator + Emulator + + + + Enable Fullscreen + Enable Fullscreen + + + + Show Splash + Show Splash + + + + Is PS4 Pro + Is PS4 Pro + + + + Username + Username + + + + Logger + Logger + + + + Log Type + Log Type + + + + Log Filter + Log Filter + + + + Graphics + Graphics + + + + Graphics Device + Graphics Device + + + + Width + Width + + + + Height + Height + + + + Vblank Divider + Vblank Divider + + + + Advanced + Advanced + + + + Enable Shaders Dumping + Enable Shaders Dumping + + + + Enable NULL GPU + Enable NULL GPU + + + + Enable PM4 Dumping + Enable PM4 Dumping + + + + Debug + Debug + + + + Enable Debug Dumping + Enable Debug Dumping + + + + Enable Vulkan Validation Layers + Enable Vulkan Validation Layers + + + + Enable Vulkan Synchronization Validation + Enable Vulkan Synchronization Validation + + + + Enable RenderDoc Debugging + Enable RenderDoc Debugging + + + + MainWindow + + + * Unsupported Vulkan Version + * Unsupported Vulkan Version + + + + Download Cheats For All Installed Games + Download Cheats For All Installed Games + + + + Download Patches For All Games + Download Patches For All Games + + + + Download Complete + Download Complete + + + + You have downloaded cheats for all the games you have installed. + You have downloaded cheats for all the games you have installed. + + + + Patches Downloaded Successfully! + Patches Downloaded Successfully! + + + + All Patches available for all games have been downloaded. + All Patches available for all games have been downloaded. + + + + Games: + Games: + + + + PKG File (*.PKG) + PKG File (*.PKG) + + + + ELF files (*.bin *.elf *.oelf) + ELF files (*.bin *.elf *.oelf) + + + + Game Boot + Game Boot + + + + Only one file can be selected! + Only one file can be selected! + + + + PKG Extraction + PKG Extraction + + + + Patch detected! + Patch detected! + + + + PKG and Game versions match: + PKG and Game versions match: + + + + Would you like to overwrite? + Would you like to overwrite? + + + + PKG Version %1 is older than installed version: + PKG Version %1 is older than installed version: + + + + Game is installed: + Game is installed: + + + + Would you like to install Patch: + Would you like to install Patch: + + + + DLC Installation + DLC Installation + + + + Would you like to install DLC: %1? + Would you like to install DLC: %1? + + + + DLC already installed: + DLC already installed: + + + + Game already installed + Game already installed + + + + PKG is a patch, please install the game first! + PKG is a patch, please install the game first! + + + + PKG ERROR + PKG ERROR + + + + Extracting PKG %1/%2 + Extracting PKG %1/%2 + + + + Extraction Finished + Extraction Finished + + + + Game successfully installed at %1 + Game successfully installed at %1 + + + + File doesn't appear to be a valid PKG file + File doesn't appear to be a valid PKG file + + + + CheatsPatches + + + Cheats / Patches + Cheats / Patches + + + + defaultTextEdit_MSG + Cheats/Patches are experimental.\nUse with caution.\n\nDownload cheats individually by selecting the repository and clicking the download button.\nIn the Patches tab, you can download all patches at once, choose which ones you want to use, and save your selection.\n\nSince we do not develop the Cheats/Patches,\nplease report issues to the cheat author.\n\nCreated a new cheat? Visit:\nhttps://github.com/shadps4-emu/ps4_cheats + + + + No Image Available + No Image Available + + + + Serial: + Serial: + + + + Version: + Version: + + + + Size: + Size: + + + + Select Cheat File: + Select Cheat File: + + + + Repository: + Repository: + + + + Download Cheats + Download Cheats + + + + Delete File + Delete File + + + + No files selected. + No files selected. + + + + You can delete the cheats you don't want after downloading them. + You can delete the cheats you don't want after downloading them. + + + + Do you want to delete the selected file?\n%1 + Do you want to delete the selected file?\n%1 + + + + Select Patch File: + Select Patch File: + + + + Download Patches + Download Patches + + + + Save + Save + + + + Cheats + Cheats + + + + Patches + Patches + + + + Error + Error + + + + No patch selected. + No patch selected. + + + + Unable to open files.json for reading. + Unable to open files.json for reading. + + + + No patch file found for the current serial. + No patch file found for the current serial. + + + + Unable to open the file for reading. + Unable to open the file for reading. + + + + Unable to open the file for writing. + Unable to open the file for writing. + + + + Failed to parse XML: + Failed to parse XML: + + + + Success + Success + + + + Options saved successfully. + Options saved successfully. + + + + Invalid Source + Invalid Source + + + + The selected source is invalid. + The selected source is invalid. + + + + File Exists + File Exists + + + + File already exists. Do you want to replace it? + File already exists. Do you want to replace it? + + + + Failed to save file: + Failed to save file: + + + + Failed to download file: + Failed to download file: + + + + Cheats Not Found + Cheats Not Found + + + + CheatsNotFound_MSG + No Cheats found for this game in this version of the selected repository,try another repository or a different version of the game. + + + + Cheats Downloaded Successfully + Cheats Downloaded Successfully + + + + CheatsDownloadedSuccessfully_MSG + You have successfully downloaded the cheats for this version of the game from the selected repository. You can try downloading from another repository, if it is available it will also be possible to use it by selecting the file from the list. + + + + Failed to save: + Failed to save: + + + + Failed to download: + Failed to download: + + + + Download Complete + Download Complete + + + + DownloadComplete_MSG + Patches Downloaded Successfully! All Patches available for all games have been downloaded, there is no need to download them individually for each game as happens in Cheats. + + + + Failed to parse JSON data from HTML. + Failed to parse JSON data from HTML. + + + + Failed to retrieve HTML page. + Failed to retrieve HTML page. + + + + Failed to open file: + Failed to open file: + + + + XML ERROR: + XML ERROR: + + + + Failed to open files.json for writing + Failed to open files.json for writing + + + + Author: + Author: + + + + Directory does not exist: + Directory does not exist: + + + + Failed to open files.json for reading. + Failed to open files.json for reading. + + + + Name: + Name: + + + \ No newline at end of file diff --git a/src/qt_gui/translations/es_ES.ts b/src/qt_gui/translations/es_ES.ts new file mode 100644 index 000000000..c34dc3d44 --- /dev/null +++ b/src/qt_gui/translations/es_ES.ts @@ -0,0 +1,902 @@ + + + + AboutDialog + + + About shadPS4 + Acerca de shadPS4 + + + + shadPS4 + shadPS4 + + + + shadPS4 is an experimental open-source emulator for the PlayStation 4. + shadPS4 es un emulador experimental de código abierto para la PlayStation 4. + + + + This software should not be used to play games you have not legally obtained. + Este software no debe utilizarse para jugar juegos que no hayas obtenido legalmente. + + + + ElfViewer + + + Open Folder + Abrir carpeta + + + + GameInfoClass + + + Loading game list, please wait :3 + Cargando lista de juegos, por favor espera :3 + + + + Cancel + Cancelar + + + + Loading... + Cargando... + + + + GameInstallDialog + + + shadPS4 - Choose directory + shadPS4 - Elegir carpeta + + + + Directory to install games + Carpeta para instalar juegos + + + + Browse + Buscar + + + + Error + Error + + + + The value for location to install games is not valid. + El valor para la ubicación de instalación de los juegos no es válido. + + + + GuiContextMenus + + + Create Shortcut + Crear acceso directo + + + + Open Game Folder + Abrir carpeta del juego + + + + Cheats / Patches + Trucos / Parches + + + + SFO Viewer + Vista SFO + + + + Trophy Viewer + Ver trofeos + + + + Copy info + Copiar información + + + + Copy Name + Copiar nombre + + + + Copy Serial + Copiar serial + + + + Copy All + Copiar todo + + + + Shortcut creation + Acceso directo creado + + + + Shortcut created successfully!\n %1 + ¡Acceso directo creado con éxito!\n %1 + + + + Error + Error + + + + Error creating shortcut!\n %1 + ¡Error al crear el acceso directo!\n %1 + + + + Install PKG + Instalar PKG + + + + MainWindow + + + Open/Add Elf Folder + Abrir/Agregar carpeta Elf + + + + Install Packages (PKG) + Instalar paquetes (PKG) + + + + Boot Game + Iniciar juego + + + + About shadPS4 + Acerca de shadPS4 + + + + Configure... + Configurar... + + + + Install application from a .pkg file + Instalar aplicación desde un archivo .pkg + + + + Recent Games + Juegos recientes + + + + Exit + Salir + + + + Exit shadPS4 + Salir de shadPS4 + + + + Exit the application. + Salir de la aplicación. + + + + Show Game List + Mostrar lista de juegos + + + + Game List Refresh + Actualizar lista de juegos + + + + Tiny + Muy pequeño + + + + Small + Pequeño + + + + Medium + Mediano + + + + Large + Grande + + + + List View + Vista de lista + + + + Grid View + Vista de cuadrícula + + + + Elf Viewer + Vista Elf + + + + Game Install Directory + Carpeta de instalación de los juegos + + + + Download Cheats/Patches + Descargar Trucos / Parches + + + + Dump Game List + Volcar lista de juegos + + + + PKG Viewer + Vista PKG + + + + Search... + Buscar... + + + + File + Archivo + + + + View + Vista + + + + Game List Icons + Iconos de los juegos + + + + Game List Mode + Tipo de lista + + + + Settings + Configuraciones + + + + Utils + Utilidades + + + + Themes + Temas + + + + About + Ayuda + + + + Dark + Oscuro + + + + Light + Claro + + + + Green + Verde + + + + Blue + Azul + + + + Violet + Violeta + + + + toolBar + barra de herramientas + + + + PKGViewer + + + Open Folder + Abrir carpeta + + + + TrophyViewer + + + Trophy Viewer + Vista de trofeos + + + + SettingsDialog + + + Settings + Configuraciones + + + + General + General + + + + System + Sistema + + + + Console Language + Idioma de la consola + + + + Emulator Language + Idioma del emulador + + + + Emulator + Emulador + + + + Enable Fullscreen + Habilitar pantalla completa + + + + Show Splash + Mostrar splash + + + + Is PS4 Pro + Modo PS4 Pro + + + + Username + Nombre de usuario + + + + Logger + Registro + + + + Log Type + Tipo de registro + + + + Log Filter + Filtro de registro + + + + Graphics + Gráficos + + + + Graphics Device + Dispositivo gráfico + + + + Width + Ancho + + + + Height + Alto + + + + Vblank Divider + Divisor de Vblank + + + + Advanced + Avanzado + + + + Enable Shaders Dumping + Habilitar volcado de shaders + + + + Enable NULL GPU + Habilitar GPU NULL + + + + Enable PM4 Dumping + Habilitar volcado de PM4 + + + + Debug + Depuración + + + + Enable Debug Dumping + Habilitar volcado de depuración + + + + Enable Vulkan Validation Layers + Habilitar capas de validación de Vulkan + + + + Enable Vulkan Synchronization Validation + Habilitar validación de sincronización de Vulkan + + + + Enable RenderDoc Debugging + Habilitar depuración de RenderDoc + + + + MainWindow + + + * Unsupported Vulkan Version + * Versión de Vulkan no soportada + + + + Download Cheats For All Installed Games + Descargar trucos para todos los juegos instalados + + + + Download Patches For All Games + Descargar parches para todos los juegos + + + + Download Complete + Descarga completa + + + + You have downloaded cheats for all the games you have installed. + Has descargado trucos para todos los juegos que tienes instalados. + + + + Patches Downloaded Successfully! + ¡Parches descargados exitosamente! + + + + All Patches available for all games have been downloaded. + Todos los parches disponibles para todos los juegos han sido descargados. + + + + Games: + Juegos: + + + + PKG File (*.PKG) + Archivo PKG (*.PKG) + + + + ELF files (*.bin *.elf *.oelf) + Archivos ELF (*.bin *.elf *.oelf) + + + + Game Boot + Inicio del juego + + + + Only one file can be selected! + ¡Solo se puede seleccionar un archivo! + + + + PKG Extraction + Extracción de PKG + + + + Patch detected! + ¡Actualización detectada! + + + + PKG and Game versions match: + Las versiones de PKG y del juego coinciden: + + + + Would you like to overwrite? + ¿Desea sobrescribir? + + + + PKG Version %1 is older than installed version: + La versión de PKG %1 es más antigua que la versión instalada: + + + + Game is installed: + El juego está instalado: + + + + Would you like to install Patch: + ¿Desea instalar la actualización: + + + + DLC Installation + Instalación de DLC + + + + Would you like to install DLC: %1? + ¿Desea instalar el DLC: %1? + + + + DLC already installed: + DLC ya instalado: + + + + Game already installed + Juego ya instalado + + + + PKG is a patch, please install the game first! + PKG es un parche, ¡por favor instala el juego primero! + + + + PKG ERROR + ERROR PKG + + + + Extracting PKG %1/%2 + Extrayendo PKG %1/%2 + + + + Extraction Finished + Extracción terminada + + + + Game successfully installed at %1 + Juego instalado exitosamente en %1 + + + + File doesn't appear to be a valid PKG file + El archivo no parece ser un archivo PKG válido + + + + CheatsPatches + + + Cheats / Patches + Trucos / Parches + + + + defaultTextEdit_MSG + Los cheats/patches son experimentales.\nÚselos con precaución.\n\nDescargue los cheats individualmente seleccionando el repositorio y haciendo clic en el botón de descarga.\nEn la pestaña Patches, puede descargar todos los patches a la vez, elegir cuáles desea usar y guardar la selección.\n\nComo no desarrollamos los Cheats/Patches,\npor favor informe los problemas al autor del cheat.\n\n¿Creaste un nuevo cheat? Visita:\nhttps://github.com/shadps4-emu/ps4_cheats + + + + No Image Available + No hay imagen disponible + + + + Serial: + Serie: + + + + Version: + Versión: + + + + Size: + Tamaño: + + + + Select Cheat File: + Seleccionar archivo de trucos: + + + + Repository: + Repositorio: + + + + Download Cheats + Descargar trucos + + + + Delete File + Eliminar archivo + + + + No files selected. + No se han seleccionado archivos. + + + + You can delete the cheats you don't want after downloading them. + Puedes eliminar los trucos que no quieras después de descargarlos. + + + + Do you want to delete the selected file?\n%1 + ¿Deseas eliminar el archivo seleccionado?\n%1 + + + + Select Patch File: + Seleccionar archivo de parche: + + + + Download Patches + Descargar parches + + + + Save + Guardar + + + + Cheats + Trucos + + + + Patches + Parche + + + + Error + Error + + + + No patch selected. + No se ha seleccionado ningún parche. + + + + Unable to open files.json for reading. + No se puede abrir files.json para lectura. + + + + No patch file found for the current serial. + No se encontró ningún archivo de parche para la serie actual. + + + + Unable to open the file for reading. + No se puede abrir el archivo para lectura. + + + + Unable to open the file for writing. + No se puede abrir el archivo para escritura. + + + + Failed to parse XML: + Error al analizar XML: + + + + Success + Éxito + + + + Options saved successfully. + Opciones guardadas exitosamente. + + + + Invalid Source + Fuente inválida + + + + The selected source is invalid. + La fuente seleccionada es inválida. + + + + File Exists + El archivo ya existe + + + + File already exists. Do you want to replace it? + El archivo ya existe. ¿Deseas reemplazarlo? + + + + Failed to save file: + Error al guardar el archivo: + + + + Failed to download file: + Error al descargar el archivo: + + + + Cheats Not Found + Trucos no encontrados + + + + CheatsNotFound_MSG + No se encontraron trucos para este juego en esta versión del repositorio seleccionado,intenta con otro repositorio o con una versión diferente del juego. + + + + Cheats Downloaded Successfully + Trucos descargados exitosamente + + + + CheatsDownloadedSuccessfully_MSG + Has descargado exitosamente los trucos para esta versión del juego desde el repositorio seleccionado. Puedes intentar descargar desde otro repositorio; si está disponible, también será posible usarlo seleccionando el archivo de la lista. + + + + Failed to save: + Error al guardar: + + + + Failed to download: + Error al descargar: + + + + Download Complete + Descarga completa + + + + DownloadComplete_MSG + ¡Parches descargados exitosamente! Todos los parches disponibles para todos los juegos han sido descargados, no es necesario descargarlos individualmente para cada juego como ocurre con los trucos. + + + + Failed to parse JSON data from HTML. + Error al analizar los datos JSON del HTML. + + + + Failed to retrieve HTML page. + Error al recuperar la página HTML. + + + + Failed to open file: + Error al abrir el archivo: + + + + XML ERROR: + ERROR XML: + + + + Failed to open files.json for writing + Error al abrir files.json para escritura + + + + Author: + Autor: + + + + Directory does not exist: + El directorio no existe: + + + + Failed to open files.json for reading. + Error al abrir files.json para lectura. + + + + Name: + Nombre: + + + \ No newline at end of file diff --git a/src/qt_gui/translations/fa_IR.ts b/src/qt_gui/translations/fa_IR.ts new file mode 100644 index 000000000..129d54792 --- /dev/null +++ b/src/qt_gui/translations/fa_IR.ts @@ -0,0 +1,899 @@ + + + + AboutDialog + + + About shadPS4 + درباره ShadPS4 + + + + shadPS4 + ShadPS4 + + + + shadPS4 is an experimental open-source emulator for the PlayStation 4. + یک شبیه ساز متن باز برای پلی استیشن 4 است. + + + + This software should not be used to play games you have not legally obtained. + این برنامه نباید برای بازی هایی که شما به صورت غیرقانونی به دست آوردید استفاده شود. + + + + ElfViewer + + + Open Folder + فولدر را بازکن + + + + GameInfoClass + + + Loading game list, please wait :3 + درحال بارگیری لیست بازی ها,لطفا کمی صبرکنید :3 + + + + Cancel + لغو + + + + Loading... + ...درحال بارگیری + + + + GameInstallDialog + + + shadPS4 - Choose directory + ShadPS4 - انتخاب محل نصب بازی + + + + Directory to install games + محل نصب بازی ها + + + + Browse + انتخاب دستی + + + + Error + ارور + + + + The value for location to install games is not valid. + .مکان داده شده برای نصب بازی درست نمی باشد + + + + GuiContextMenus + + + Create Shortcut + ساخت شورتکات + + + + Open Game Folder + بازکردن محل نصب بازی + + + + Cheats / Patches + چیت/پچ ها + + + + SFO Viewer + SFO مشاهده + + + + Trophy Viewer + مشاهده تروفی ها + + + + Copy info + کپی کردن اطلاعات + + + + Copy Name + کپی کردن نام + + + + Copy Serial + کپی کردن سریال + + + + Copy All + کپی کردن تمامی مقادیر + + + + Shortcut creation + سازنده شورتکات + + + + Shortcut created successfully!\n %1 + شورتکات با موفقیت ساخته شد! \n %1 + + + + Error + ارور + + + + Error creating shortcut!\n %1 + مشکلی در هنگام ساخت شورتکات بوجود آمد!\n %1 + + + + Install PKG + نصب PKG + + + + MainWindow + + + Open/Add Elf Folder + ELF بازکردن/ساختن پوشه + + + + Install Packages (PKG) + نصب بسته (PKG) + + + + Boot Game + اجرای بازی + + + + About shadPS4 + ShadPS4 درباره + + + + Configure... + ...تنظیمات + + + + Install application from a .pkg file + .PKG نصب بازی از فایل + + + + Recent Games + بازی های اخیر + + + + Exit + خروج + + + + Exit shadPS4 + ShadPS4 بستن + + + + Exit the application. + بستن برنامه + + + + Show Game List + نشان دادن بازی ها + + + + Game List Refresh + رفرش لیست بازی ها + + + + Tiny + کوچک ترین + + + + Small + کوچک + + + + Medium + متوسط + + + + Large + بزرگ + + + + List View + لیستی + + + + Grid View + شبکه ای (چهارخونه) + + + + Elf Viewer + Elf Viewer + + + + Game Install Directory + محل نصب بازی + + + + Download Cheats/Patches + دانلود چیت/پچ + + + + Dump Game List + استخراج لیست بازی ها + + + + PKG Viewer + PKG مشاهده گر + + + + Search... + جست و جو... + + + + File + فایل + + + + View + شخصی سازی + + + + Game List Icons + آیکون ها + + + + Game List Mode + حالت نمایش لیست بازی ها + + + + Settings + تنظیمات + + + + Utils + ابزارها + + + + Themes + تم ها + + + + About + درباره ما + + + + Dark + تیره + + + + Light + روشن + + + + Green + سبز + + + + Blue + آبی + + + + Violet + بنفش + + + + toolBar + نوار ابزار + + + + * Unsupported Vulkan Version + شما پشتیبانی نمیشود Vulkan ورژن* + + + + Download Cheats For All Installed Games + دانلود چیت برای همه بازی ها + + + + Download Patches For All Games + دانلود پچ برای همه بازی ها + + + + Download Complete + دانلود کامل شد✅ + + + + You have downloaded cheats for all the games you have installed. + چیت برای همه بازی های شما دانلودشد✅ + + + + Patches Downloaded Successfully! + پچ ها با موفقیت دانلود شد✅ + + + + All Patches available for all games have been downloaded. + ✅تمام پچ های موجود برای همه بازی های شما دانلود شد + + + + Games: + بازی ها: + + + + PKG File (*.PKG) + PKG فایل (*.PKG) + + + + ELF files (*.bin *.elf *.oelf) + ELF فایل های (*.bin *.elf *.oelf) + + + + Game Boot + اجرای بازی + + + + Only one file can be selected! + فقط یک فایل انتخاب کنید! + + + + PKG Extraction + PKG استخراج فایل + + + + Patch detected! + پچ شناسایی شد! + + + + PKG and Game versions match: + و نسخه بازی همخوانی دارد PKG فایل: + + + + Would you like to overwrite? + آیا مایل به جایگزینی فایل هستید؟ + + + + PKG Version %1 is older than installed version: + نسخه فایل PKG %1 قدیمی تر از نسخه نصب شده است: + + + + Game is installed: + بازی نصب شد: + + + + Would you like to install Patch: + آیا مایل به نصب پچ هستید: + + + + DLC Installation + نصب DLC + + + + Would you like to install DLC: %1? + آیا مایل به نصب DLC هستید: %1 + + + + DLC already installed: + قبلا نصب شده DLC این: + + + + Game already installed + این بازی قبلا نصب شده + + + + PKG is a patch, please install the game first! + فایل انتخاب شده یک پچ است, لطفا اول بازی را نصب کنید + + + + PKG ERROR + PKG ارور فایل + + + + Extracting PKG %1/%2 + درحال استخراج PKG %1/%2 + + + + Extraction Finished + استخراج به پایان رسید + + + + Game successfully installed at %1 + بازی با موفقیت در %1 نصب شد + + + + File doesn't appear to be a valid PKG file + این فایل یک PKG درست به نظر نمی آید + + + + PKGViewer + + + Open Folder + بازکردن پوشه + + + + TrophyViewer + + + Trophy Viewer + تروفی ها + + + + SettingsDialog + + + Settings + تنظیمات + + + + General + عمومی + + + + System + سیستم + + + + Console Language + زبان کنسول + + + + Emulator Language + زبان شبیه ساز + + + + Emulator + شبیه ساز + + + + Enable Fullscreen + تمام صفحه + + + + Show Splash + Splash نمایش + + + + Is PS4 Pro + PS4 Pro حالت + + + + Username + نام کاربری + + + + Logger + Logger + + + + Log Type + Log نوع + + + + Log Filter + Log فیلتر + + + + Graphics + گرافیک + + + + Graphics Device + کارت گرافیک مورداستفاده + + + + Width + عرض + + + + Height + طول + + + + Vblank Divider + Vblank Divider + + + + Advanced + ...بیشتر + + + + Enable Shaders Dumping + Shaders Dumping فعال کردن + + + + Enable NULL GPU + NULL GPU فعال کردن + + + + Enable PM4 Dumping + PM4 Dumping فعال کردن + + + + Debug + Debug + + + + Enable Debug Dumping + Debug Dumping + + + + Enable Vulkan Validation Layers + Vulkan Validation Layers + + + + Enable Vulkan Synchronization Validation + Vulkan Synchronization Validation + + + + Enable RenderDoc Debugging + RenderDoc Debugging + + + + CheatsPatches + + + Cheats / Patches + چیت / پچ ها + + + + defaultTextEdit_MSG + defaultTextEdit_MSG + + + + No Image Available + تصویری موجود نمی باشد + + + + Serial: + سریال: + + + + Version: + ورژن: + + + + Size: + حجم: + + + + Select Cheat File: + فایل چیت را انتخاب کنید: + + + + Repository: + :منبع + + + + Download Cheats + دانلود چیت ها + + + + Delete File + پاک کردن فایل + + + + No files selected. + فایلی انتخاب نشده. + + + + You can delete the cheats you don't want after downloading them. + شما میتوانید بعد از دانلود چیت هایی که نمیخواهید را پاک کنید + + + + Do you want to delete the selected file?\n%1 + آیا میخواهید فایل های انتخاب شده را پاک کنید؟ \n%1 + + + + Select Patch File: + فایل پچ را انتخاب کنید + + + + Download Patches + دانلود کردن پچ ها + + + + Save + ذخیره + + + + Cheats + چیت ها + + + + Patches + پچ ها + + + + Error + ارور + + + + No patch selected. + هیچ پچ انتخاب نشده + + + + Unable to open files.json for reading. + .json مشکل در خواندن فایل + + + + No patch file found for the current serial. + هیچ فایل پچ برای سریال بازی شما پیدا نشد. + + + + Unable to open the file for reading. + خطا در خواندن فایل + + + + Unable to open the file for writing. + خطا در نوشتن فایل + + + + Failed to parse XML: + انجام نشد XML تجزیه فایل: + + + + Success + عملیات موفق بود + + + + Options saved successfully. + تغییرات با موفقیت ذخیره شد✅ + + + + Invalid Source + منبع نامعتبر❌ + + + + The selected source is invalid. + منبع انتخاب شده نامعتبر است + + + + File Exists + فایل وجود دارد + + + + File already exists. Do you want to replace it? + فایل از قبل وجود دارد. آیا می خواهید آن را جایگزین کنید؟ + + + + Failed to save file: + ذخیره فایل موفقیت آمیز نبود: + + + + Failed to download file: + خطا در دانلود فایل: + + + + Cheats Not Found + چیت یافت نشد + + + + CheatsNotFound_MSG + متاسفانه هیچ چیتی از منبع انتخاب شده پیدا نشد! شما میتوانید منابع دیگری را برای دانلود انتخاب و یا چیت های خود را به صورت دستی واردکنید. + + + + Cheats Downloaded Successfully + دانلود چیت ها موفقیت آمیز بود✅ + + + + CheatsDownloadedSuccessfully_MSG + تمامی چیت های موجود برای این بازی از منبع انتخاب شده دانلود شد! شما همچنان میتوانید چیت های دیگری را ازمنابع مختلف دانلود کنید و درصورت موجود بودن از آنها استفاده کنید. + + + + Failed to save: + خطا در ذخیره اطلاعات: + + + + Failed to download: + خطا در دانلود❌ + + + + Download Complete + دانلود موفقیت آمیز بود✅ + + + + DownloadComplete_MSG + دانلود با موفقیت به اتمام رسید✅ + + + + Failed to parse JSON data from HTML. + HTML از JSON خطا در تجزیه اطلاعات. + + + + Failed to retrieve HTML page. + HTML خطا دربازیابی صفحه + + + + Failed to open file: + خطا در اجرای فایل: + + + + XML ERROR: + XML ERROR: + + + + Failed to open files.json for writing + .json خطا در نوشتن فایل + + + + Author: + تولید کننده: + + + + Directory does not exist: + پوشه وجود ندارد: + + + + Failed to open files.json for reading. + .json خطا در خواندن فایل + + + + Name: + نام: + + + diff --git a/src/qt_gui/translations/fi.ts b/src/qt_gui/translations/fi.ts new file mode 100644 index 000000000..d667dd379 --- /dev/null +++ b/src/qt_gui/translations/fi.ts @@ -0,0 +1,902 @@ + + + + AboutDialog + + + About shadPS4 + About shadPS4 + + + + shadPS4 + shadPS4 + + + + shadPS4 is an experimental open-source emulator for the PlayStation 4. + shadPS4 is an experimental open-source emulator for the PlayStation 4. + + + + This software should not be used to play games you have not legally obtained. + This software should not be used to play games you have not legally obtained. + + + + ElfViewer + + + Open Folder + Open Folder + + + + GameInfoClass + + + Loading game list, please wait :3 + Loading game list, please wait :3 + + + + Cancel + Cancel + + + + Loading... + Loading... + + + + GameInstallDialog + + + shadPS4 - Choose directory + shadPS4 - Choose directory + + + + Directory to install games + Directory to install games + + + + Browse + Browse + + + + Error + Error + + + + The value for location to install games is not valid. + The value for location to install games is not valid. + + + + GuiContextMenus + + + Create Shortcut + Create Shortcut + + + + Open Game Folder + Open Game Folder + + + + Cheats / Patches + Huijaukset / Korjaukset + + + + SFO Viewer + SFO Viewer + + + + Trophy Viewer + Trophy Viewer + + + + Copy info + Copy info + + + + Copy Name + Copy Name + + + + Copy Serial + Copy Serial + + + + Copy All + Copy All + + + + Shortcut creation + Shortcut creation + + + + Shortcut created successfully!\n %1 + Shortcut created successfully!\n %1 + + + + Error + Error + + + + Error creating shortcut!\n %1 + Error creating shortcut!\n %1 + + + + Install PKG + Install PKG + + + + MainWindow + + + Open/Add Elf Folder + Open/Add Elf Folder + + + + Install Packages (PKG) + Install Packages (PKG) + + + + Boot Game + Boot Game + + + + About shadPS4 + About shadPS4 + + + + Configure... + Configure... + + + + Install application from a .pkg file + Install application from a .pkg file + + + + Recent Games + Recent Games + + + + Exit + Exit + + + + Exit shadPS4 + Exit shadPS4 + + + + Exit the application. + Exit the application. + + + + Show Game List + Show Game List + + + + Game List Refresh + Game List Refresh + + + + Tiny + Tiny + + + + Small + Small + + + + Medium + Medium + + + + Large + Large + + + + List View + List View + + + + Grid View + Grid View + + + + Elf Viewer + Elf Viewer + + + + Game Install Directory + Game Install Directory + + + + Download Cheats/Patches + Lataa Huijaukset / Korjaukset + + + + Dump Game List + Dump Game List + + + + PKG Viewer + PKG Viewer + + + + Search... + Search... + + + + File + File + + + + View + View + + + + Game List Icons + Game List Icons + + + + Game List Mode + Game List Mode + + + + Settings + Settings + + + + Utils + Utils + + + + Themes + Themes + + + + About + About + + + + Dark + Dark + + + + Light + Light + + + + Green + Green + + + + Blue + Blue + + + + Violet + Violet + + + + toolBar + toolBar + + + + PKGViewer + + + Open Folder + Open Folder + + + + TrophyViewer + + + Trophy Viewer + Trophy Viewer + + + + SettingsDialog + + + Settings + Settings + + + + General + General + + + + System + System + + + + Console Language + Console Language + + + + Emulator Language + Emulator Language + + + + Emulator + Emulator + + + + Enable Fullscreen + Enable Fullscreen + + + + Show Splash + Show Splash + + + + Is PS4 Pro + Is PS4 Pro + + + + Username + Username + + + + Logger + Logger + + + + Log Type + Log Type + + + + Log Filter + Log Filter + + + + Graphics + Graphics + + + + Graphics Device + Graphics Device + + + + Width + Width + + + + Height + Height + + + + Vblank Divider + Vblank Divider + + + + Advanced + Advanced + + + + Enable Shaders Dumping + Enable Shaders Dumping + + + + Enable NULL GPU + Enable NULL GPU + + + + Enable PM4 Dumping + Enable PM4 Dumping + + + + Debug + Debug + + + + Enable Debug Dumping + Enable Debug Dumping + + + + Enable Vulkan Validation Layers + Enable Vulkan Validation Layers + + + + Enable Vulkan Synchronization Validation + Enable Vulkan Synchronization Validation + + + + Enable RenderDoc Debugging + Enable RenderDoc Debugging + + + + MainWindow + + + * Unsupported Vulkan Version + * Tuettu Vulkan-versio + + + + Download Cheats For All Installed Games + Lataa huijaukset kaikille asennetuille peleille + + + + Download Patches For All Games + Lataa korjaukset kaikille peleille + + + + Download Complete + Lataus valmis + + + + You have downloaded cheats for all the games you have installed. + Olet ladannut huijaukset kaikkiin asennettuihin peleihin. + + + + Patches Downloaded Successfully! + Korjaukset ladattu onnistuneesti! + + + + All Patches available for all games have been downloaded. + Kaikki saatavilla olevat korjaukset kaikille peleille on ladattu. + + + + Games: + Pelit: + + + + PKG File (*.PKG) + PKG-tiedosto (*.PKG) + + + + ELF files (*.bin *.elf *.oelf) + ELF-tiedostot (*.bin *.elf *.oelf) + + + + Game Boot + Pelin käynnistys + + + + Only one file can be selected! + Vain yksi tiedosto voidaan valita! + + + + PKG Extraction + PKG:n purku + + + + Patch detected! + Päivitys havaittu! + + + + PKG and Game versions match: + PKG- ja peliversiot vastaavat: + + + + Would you like to overwrite? + Haluatko korvata? + + + + PKG Version %1 is older than installed version: + PKG-versio %1 on vanhempi kuin asennettu versio: + + + + Game is installed: + Peli on asennettu: + + + + Would you like to install Patch: + Haluatko asentaa päivityksen: + + + + DLC Installation + DLC-asennus + + + + Would you like to install DLC: %1? + Haluatko asentaa DLC:n: %1? + + + + DLC already installed: + DLC on jo asennettu: + + + + Game already installed + Peli on jo asennettu + + + + PKG is a patch, please install the game first! + PKG on korjaus, asenna peli ensin! + + + + PKG ERROR + PKG VIRHE + + + + Extracting PKG %1/%2 + Purkaminen PKG %1/%2 + + + + Extraction Finished + Purku valmis + + + + Game successfully installed at %1 + Peli asennettu onnistuneesti kohtaan %1 + + + + File doesn't appear to be a valid PKG file + Tiedosto ei vaikuta olevan kelvollinen PKG-tiedosto + + + + CheatsPatches + + + Cheats / Patches + Huijaukset / Korjaukset + + + + defaultTextEdit_MSG + Cheats/Patches ovat kokeellisia.\nKäytä varoen.\n\nLataa cheats yksitellen valitsemalla repositorio ja napsauttamalla latauspainiketta.\nPatches-välilehdellä voit ladata kaikki patchit kerralla, valita, mitä haluat käyttää, ja tallentaa valinnan.\n\nKoska emme kehitä Cheats/Patches,\nilmoita ongelmista cheatin tekijälle.\n\nLuo uusi cheat? Käy osoitteessa:\nhttps://github.com/shadps4-emu/ps4_cheats + + + + No Image Available + Kuvaa ei saatavilla + + + + Serial: + Sarjanumero: + + + + Version: + Versio: + + + + Size: + Koko: + + + + Select Cheat File: + Valitse huijaustiedosto: + + + + Repository: + Repo: + + + + Download Cheats + Lataa huijaukset + + + + Delete File + Poista tiedosto + + + + No files selected. + Ei tiedostoja valittu. + + + + You can delete the cheats you don't want after downloading them. + Voit poistaa ei-toivomasi huijaukset lataamisen jälkeen. + + + + Do you want to delete the selected file?\n%1 + Haluatko poistaa valitun tiedoston?\n%1 + + + + Select Patch File: + Valitse korjaustiedosto: + + + + Download Patches + Lataa korjaukset + + + + Save + Tallenna + + + + Cheats + Huijaukset + + + + Patches + Korjaukset + + + + Error + Virhe + + + + No patch selected. + Ei korjausta valittu. + + + + Unable to open files.json for reading. + Tiedostoa files.json ei voitu avata lukemista varten. + + + + No patch file found for the current serial. + Nykyiselle sarjanumerolle ei löytynyt korjaustiedostoa. + + + + Unable to open the file for reading. + Tiedostoa ei voitu avata lukemista varten. + + + + Unable to open the file for writing. + Tiedostoa ei voitu avata kirjoittamista varten. + + + + Failed to parse XML: + XML:n jäsentäminen epäonnistui: + + + + Success + Onnistui + + + + Options saved successfully. + Vaihtoehdot tallennettu onnistuneesti. + + + + Invalid Source + Virheellinen lähde + + + + The selected source is invalid. + Valittu lähde on virheellinen. + + + + File Exists + Tiedosto on olemassa + + + + File already exists. Do you want to replace it? + Tiedosto on jo olemassa. Haluatko korvata sen? + + + + Failed to save file: + Tiedoston tallentaminen epäonnistui: + + + + Failed to download file: + Tiedoston lataaminen epäonnistui: + + + + Cheats Not Found + Huijauksia ei löytynyt + + + + CheatsNotFound_MSG + Huijauksia ei löytynyt tälle pelille tämän version valitusta repositoriosta, yritä toista repositoriota tai pelin eri versiota. + + + + Cheats Downloaded Successfully + Huijaukset ladattu onnistuneesti + + + + CheatsDownloadedSuccessfully_MSG + Olet ladannut huijaukset onnistuneesti valitusta repositoriosta tälle pelin versiolle. Voit yrittää ladata toisesta repositoriosta, jos se on saatavilla, voit myös käyttää sitä valitsemalla tiedoston luettelosta. + + + + Failed to save: + Tallentaminen epäonnistui: + + + + Failed to download: + Lataaminen epäonnistui: + + + + Download Complete + Lataus valmis + + + + DownloadComplete_MSG + Korjaukset ladattu onnistuneesti! Kaikki saatavilla olevat korjaukset kaikille peleille on ladattu, eikä niitä tarvitse ladata yksittäin jokaiselle pelille kuten huijauksissa. + + + + Failed to parse JSON data from HTML. + JSON-tietojen jäsentäminen HTML:stä epäonnistui. + + + + Failed to retrieve HTML page. + HTML-sivun hakeminen epäonnistui. + + + + Failed to open file: + Tiedoston avaaminen epäonnistui: + + + + XML ERROR: + XML VIRHE: + + + + Failed to open files.json for writing + Tiedostoa files.json ei voitu avata kirjoittamista varten + + + + Author: + Tekijä: + + + + Directory does not exist: + Kansiota ei ole olemassa: + + + + Failed to open files.json for reading. + Tiedostoa files.json ei voitu avata lukemista varten. + + + + Name: + Nimi: + + + \ No newline at end of file diff --git a/src/qt_gui/translations/fr.ts b/src/qt_gui/translations/fr.ts new file mode 100644 index 000000000..388912d23 --- /dev/null +++ b/src/qt_gui/translations/fr.ts @@ -0,0 +1,902 @@ + + + + AboutDialog + + + About shadPS4 + À propos de shadPS4 + + + + shadPS4 + shadPS4 + + + + shadPS4 is an experimental open-source emulator for the PlayStation 4. + shadPS4 est un émulateur open-source expérimental de la PlayStation 4. + + + + This software should not be used to play games you have not legally obtained. + Ce logiciel ne doit pas être utilisé pour jouer à des jeux que vous n'avez pas obtenus légalement. + + + + ElfViewer + + + Open Folder + Ouvrir un dossier + + + + GameInfoClass + + + Loading game list, please wait :3 + Chargement de la liste de jeu, veuillez patienter... + + + + Cancel + Annuler + + + + Loading... + Chargement... + + + + GameInstallDialog + + + shadPS4 - Choose directory + shadPS4 - Choisir un répertoire + + + + Directory to install games + Répertoire d'installation des jeux + + + + Browse + Parcourir + + + + Error + Erreur + + + + The value for location to install games is not valid. + Le répertoire d'installation des jeux n'est pas valide. + + + + GuiContextMenus + + + Create Shortcut + Créer un raccourci + + + + Open Game Folder + Ouvrir le dossier du jeu + + + + Cheats / Patches + Cheats/Patchs + + + + SFO Viewer + Visionneuse SFO + + + + Trophy Viewer + Visionneuse de trophées + + + + Copy info + Copier infos + + + + Copy Name + Copier le nom + + + + Copy Serial + Copier le numéro de série + + + + Copy All + Copier tout + + + + Shortcut creation + Création du raccourci + + + + Shortcut created successfully!\n %1 + Raccourci créé avec succès !\n %1 + + + + Error + Erreur + + + + Error creating shortcut!\n %1 + Erreur lors de la création du raccourci !\n %1 + + + + Install PKG + Installer un PKG + + + + MainWindow + + + Open/Add Elf Folder + Ouvrir/Ajouter un dossier ELF + + + + Install Packages (PKG) + Installer des packages (PKG) + + + + Boot Game + Démarrer un jeu + + + + About shadPS4 + À propos de shadPS4 + + + + Configure... + Configurer... + + + + Install application from a .pkg file + Installer une application depuis un fichier .pkg + + + + Recent Games + Jeux récents + + + + Exit + Fermer + + + + Exit shadPS4 + Fermer shadPS4 + + + + Exit the application. + Fermer l'application. + + + + Show Game List + Afficher la liste de jeux + + + + Game List Refresh + Rafraîchir la liste de jeux + + + + Tiny + Très Petit + + + + Small + Petit + + + + Medium + Moyen + + + + Large + Grand + + + + List View + Mode liste + + + + Grid View + Mode grille + + + + Elf Viewer + Visionneuse ELF + + + + Game Install Directory + Répertoire des jeux + + + + Download Cheats/Patches + Télécharger Cheats/Patchs + + + + Dump Game List + Dumper la liste des jeux + + + + PKG Viewer + Visionneuse PKG + + + + Search... + Chercher... + + + + File + Fichier + + + + View + Affichage + + + + Game List Icons + Icônes des jeux + + + + Game List Mode + Mode d'affichage + + + + Settings + Paramètres + + + + Utils + Utilitaire + + + + Themes + Thèmes + + + + About + À propos + + + + Dark + Noir + + + + Light + Blanc + + + + Green + Vert + + + + Blue + Bleu + + + + Violet + Violet + + + + toolBar + Bare d'outils + + + + PKGViewer + + + Open Folder + Ouvrir un dossier + + + + TrophyViewer + + + Trophy Viewer + Visionneuse de trophées + + + + SettingsDialog + + + Settings + Paramètres + + + + General + Général + + + + System + Système + + + + Console Language + Langage de la console + + + + Emulator Language + Langage de l'émulateur + + + + Emulator + Émulateur + + + + Enable Fullscreen + Plein écran + + + + Show Splash + Afficher l'image du jeu + + + + Is PS4 Pro + Mode PS4 Pro + + + + Username + Nom d'utilisateur + + + + Logger + Journalisation + + + + Log Type + Type + + + + Log Filter + Filtre + + + + Graphics + Graphismes + + + + Graphics Device + Carte graphique + + + + Width + Largeur + + + + Height + Hauteur + + + + Vblank Divider + Vblank + + + + Advanced + Avancé + + + + Enable Shaders Dumping + Dumper les shaders + + + + Enable NULL GPU + NULL GPU + + + + Enable PM4 Dumping + Dumper le PM4 + + + + Debug + Débogage + + + + Enable Debug Dumping + Activer le débogage + + + + Enable Vulkan Validation Layers + Activer la couche de validation Vulkan + + + + Enable Vulkan Synchronization Validation + Activer la synchronisation de la validation Vulkan + + + + Enable RenderDoc Debugging + Activer le débogage RenderDoc + + + + MainWindow + + + * Unsupported Vulkan Version + * Version de Vulkan non prise en charge + + + + Download Cheats For All Installed Games + Télécharger les Cheats pour tous les jeux installés + + + + Download Patches For All Games + Télécharger les patchs pour tous les jeux + + + + Download Complete + Téléchargement terminé + + + + You have downloaded cheats for all the games you have installed. + Vous avez téléchargé des Cheats pour tous les jeux installés. + + + + Patches Downloaded Successfully! + Patchs téléchargés avec succès ! + + + + All Patches available for all games have been downloaded. + Tous les patchs disponibles pour les jeux ont été téléchargés. + + + + Games: + Jeux : + + + + PKG File (*.PKG) + Fichiers PKG (*.PKG) + + + + ELF files (*.bin *.elf *.oelf) + Fichiers ELF (*.bin *.elf *.oelf) + + + + Game Boot + Démarrer un jeu + + + + Only one file can be selected! + Un seul fichier peut être sélectionné ! + + + + PKG Extraction + Extraction du PKG + + + + Patch detected! + Patch détecté ! + + + + PKG and Game versions match: + Les versions PKG et jeu correspondent : + + + + Would you like to overwrite? + Souhaitez-vous remplacer ? + + + + PKG Version %1 is older than installed version: + La version PKG %1 est plus ancienne que la version installée : + + + + Game is installed: + Jeu installé : + + + + Would you like to install Patch: + Souhaitez-vous installer le patch : + + + + DLC Installation + Installation du DLC + + + + Would you like to install DLC: %1? + Souhaitez-vous installer le DLC : %1 ? + + + + DLC already installed: + DLC déjà installé : + + + + Game already installed + Jeu déjà installé + + + + PKG is a patch, please install the game first! + Le PKG est un patch, veuillez d'abord installer le jeu ! + + + + PKG ERROR + Erreur PKG + + + + Extracting PKG %1/%2 + Extraction PKG %1/%2 + + + + Extraction Finished + Extraction terminée + + + + Game successfully installed at %1 + Jeu installé avec succès à %1 + + + + File doesn't appear to be a valid PKG file + Le fichier ne semble pas être un PKG valide + + + + CheatsPatches + + + Cheats / Patches + Cheats/Patches + + + + defaultTextEdit_MSG + Les Cheats/Patchs sont expérimentaux.\nUtilisez-les avec précaution.\n\nTéléchargez les Cheats individuellement en sélectionnant le dépôt et en cliquant sur le bouton de téléchargement.\nDans l'onglet Patchs, vous pouvez télécharger tous les patchs en une seule fois, choisir lesquels vous souhaitez utiliser et enregistrer votre sélection.\n\nComme nous ne développons pas les Cheats/Patches,\nmerci de signaler les problèmes à l'auteur du Cheat.\n\nVous avez créé un nouveau cheat ? Visitez :\nhttps://github.com/shadps4-emu/ps4_cheats + + + + No Image Available + Aucune image disponible + + + + Serial: + Série : + + + + Version: + Version : + + + + Size: + Taille : + + + + Select Cheat File: + Sélectionner le fichier de Cheat : + + + + Repository: + Dépôt : + + + + Download Cheats + Télécharger les Cheats + + + + Delete File + Supprimer le fichier + + + + No files selected. + Aucun fichier sélectionné. + + + + You can delete the cheats you don't want after downloading them. + Vous pouvez supprimer les Cheats que vous ne souhaitez pas après les avoir téléchargés. + + + + Do you want to delete the selected file?\n%1 + Voulez-vous supprimer le fichier sélectionné ?\n%1 + + + + Select Patch File: + Sélectionner le fichier de patch : + + + + Download Patches + Télécharger les patchs + + + + Save + Enregistrer + + + + Cheats + Cheats + + + + Patches + Patchs + + + + Error + Erreur + + + + No patch selected. + Aucun patch sélectionné. + + + + Unable to open files.json for reading. + Impossible d'ouvrir files.json pour la lecture. + + + + No patch file found for the current serial. + Aucun fichier de patch trouvé pour la série actuelle. + + + + Unable to open the file for reading. + Impossible d'ouvrir le fichier pour la lecture. + + + + Unable to open the file for writing. + Impossible d'ouvrir le fichier pour l'écriture. + + + + Failed to parse XML: + Échec de l'analyse XML : + + + + Success + Succès + + + + Options saved successfully. + Options enregistrées avec succès. + + + + Invalid Source + Source invalide + + + + The selected source is invalid. + La source sélectionnée est invalide. + + + + File Exists + Le fichier existe + + + + File already exists. Do you want to replace it? + Le fichier existe déjà. Voulez-vous le remplacer ? + + + + Failed to save file: + Échec de l'enregistrement du fichier : + + + + Failed to download file: + Échec du téléchargement du fichier : + + + + Cheats Not Found + Cheats non trouvés + + + + CheatsNotFound_MSG + Aucun Cheat trouvé pour ce jeu dans cette version du dépôt sélectionné, essayez un autre dépôt ou une version différente du jeu. + + + + Cheats Downloaded Successfully + Cheats téléchargés avec succès + + + + CheatsDownloadedSuccessfully_MSG + Vous avez téléchargé les cheats avec succès pour cette version du jeu depuis le dépôt sélectionné. Vous pouvez essayer de télécharger depuis un autre dépôt, si disponible, il sera également possible de l'utiliser en sélectionnant le fichier dans la liste. + + + + Failed to save: + Échec de l'enregistrement : + + + + Failed to download: + Échec du téléchargement : + + + + Download Complete + Téléchargement terminé + + + + DownloadComplete_MSG + Patchs téléchargés avec succès ! Tous les patches disponibles pour tous les jeux ont été téléchargés, il n'est pas nécessaire de les télécharger individuellement pour chaque jeu comme c'est le cas pour les Cheats. + + + + Failed to parse JSON data from HTML. + Échec de l'analyse des données JSON à partir du HTML. + + + + Failed to retrieve HTML page. + Échec de la récupération de la page HTML. + + + + Failed to open file: + Échec de l'ouverture du fichier : + + + + XML ERROR: + Erreur XML : + + + + Failed to open files.json for writing + Échec de l'ouverture de files.json pour l'écriture + + + + Author: + Auteur : + + + + Directory does not exist: + Répertoire n'existe pas : + + + + Failed to open files.json for reading. + Échec de l'ouverture de files.json pour la lecture. + + + + Name: + Nom : + + + diff --git a/src/qt_gui/translations/hu_HU.ts b/src/qt_gui/translations/hu_HU.ts new file mode 100644 index 000000000..e5fb25a5d --- /dev/null +++ b/src/qt_gui/translations/hu_HU.ts @@ -0,0 +1,902 @@ + + + + AboutDialog + + + About shadPS4 + A shadPS4-ről + + + + shadPS4 + shadPS4 + + + + shadPS4 is an experimental open-source emulator for the PlayStation 4. + A shadPS4 egy kezdetleges, open-source PlayStation 4 emulátor. + + + + This software should not be used to play games you have not legally obtained. + Ne használja ezt a szoftvert olyan játékokkal, amelyeket nem legális módon szerzett be. + + + + ElfViewer + + + Open Folder + Mappa megnyitása + + + + GameInfoClass + + + Loading game list, please wait :3 + Játék könyvtár betöltése, kérjük várjon :3 + + + + Cancel + Megszakítás + + + + Loading... + Betöltés.. + + + + GameInstallDialog + + + shadPS4 - Choose directory + shadPS4 - Mappa kiválasztása + + + + Directory to install games + Mappa a játékok telepítésére + + + + Browse + Böngészés + + + + Error + Hiba + + + + The value for location to install games is not valid. + A játékok telepítéséhez megadott érték nem érvényes. + + + + GuiContextMenus + + + Create Shortcut + Parancsikon Létrehozása + + + + Open Game Folder + Játék Mappa Megnyitása + + + + Cheats / Patches + Csalások / Javítások + + + + SFO Viewer + SFO Néző + + + + Trophy Viewer + Trófeák Megtekintése + + + + Copy info + Információ Másolása + + + + Copy Name + Név Másolása + + + + Copy Serial + Széria Másolása + + + + Copy All + Összes Másolása + + + + Shortcut creation + Parancsikon létrehozása + + + + Shortcut created successfully!\n %1 + Parancsikon sikeresen létrehozva!\n %1 + + + + Error + Hiba + + + + Error creating shortcut!\n %1 + Hiba a parancsikon létrehozásával!\n %1 + + + + Install PKG + PKG telepítése + + + + MainWindow + + + Open/Add Elf Folder + Efl Mappa Megnyitása/Hozzáadása + + + + Install Packages (PKG) + PKG-k Telepítése (PKG) + + + + Boot Game + Játék Bootolása + + + + About shadPS4 + A shadPS4-ről + + + + Configure... + Konfigurálás... + + + + Install application from a .pkg file + Program telepítése egy .pkg fájlból + + + + Recent Games + Legutóbbi Játékok + + + + Exit + Kilépés + + + + Exit shadPS4 + Kilépés a shadPS4 + + + + Exit the application. + Lépjen ki az programból. + + + + Show Game List + Játék Könyvtár Megjelenítése + + + + Game List Refresh + Játék Könyvtár Újratöltése + + + + Tiny + Apró + + + + Small + Kicsi + + + + Medium + Közepes + + + + Large + Nagy + + + + List View + Lista Nézet + + + + Grid View + Rács Nézet + + + + Elf Viewer + Elf Néző + + + + Game Install Directory + Játék Telepítési Mappa + + + + Download Cheats/Patches + Csalások / Javítások letöltése + + + + Dump Game List + Játék Lista Dumpolása + + + + PKG Viewer + PKG Néző + + + + Search... + Keresés... + + + + File + Fájl + + + + View + Megnézés + + + + Game List Icons + Játék Könyvtár Ikonok + + + + Game List Mode + Játék Könyvtár Mód + + + + Settings + Beállítások + + + + Utils + Segédeszközök + + + + Themes + Témák + + + + About + Segítség + + + + Dark + Sötét + + + + Light + Világos + + + + Green + Zöld + + + + Blue + Kék + + + + Violet + Ibolya + + + + toolBar + Eszköztár + + + + PKGViewer + + + Open Folder + Mappa Megnyitása + + + + TrophyViewer + + + Trophy Viewer + Trófeák Megtekintése + + + + SettingsDialog + + + Settings + Beállítások + + + + General + Általános + + + + System + Rendszer + + + + Console Language + A Konzol Nyelvezete + + + + Emulator Language + Az Emulátor Nyelvezete + + + + Emulator + Emulátor + + + + Enable Fullscreen + Teljesképernyő Engedélyezése + + + + Show Splash + Indítóképernyő Mutatása + + + + Is PS4 Pro + PS4 Pro + + + + Username + Felhasználónév + + + + Logger + Naplózó + + + + Log Type + Naplózási Típus + + + + Log Filter + Naplózási Filter + + + + Graphics + Grafika + + + + Graphics Device + Grafikai Eszköz + + + + Width + Szélesség + + + + Height + Magasság + + + + Vblank Divider + Vblank Elosztó + + + + Advanced + Haladó + + + + Enable Shaders Dumping + Shader Dumpolás Engedélyezése + + + + Enable NULL GPU + NULL GPU Engedélyezése + + + + Enable PM4 Dumping + PM4 Dumpolás Engedélyezése + + + + Debug + Debugolás + + + + Enable Debug Dumping + Debug Dumpolás Engedélyezése + + + + Enable Vulkan Validation Layers + Vulkan Validációs Rétegek Engedélyezése + + + + Enable Vulkan Synchronization Validation + Vulkan Szinkronizálás Validáció + + + + Enable RenderDoc Debugging + RenderDoc Debugolás Engedélyezése + + + + MainWindow + + + * Unsupported Vulkan Version + * Támogatott Vulkan verzió hiányzik + + + + Download Cheats For All Installed Games + Letöltés csalások minden telepített játékhoz + + + + Download Patches For All Games + Frissítések letöltése minden játékhoz + + + + Download Complete + Letöltés befejezve + + + + You have downloaded cheats for all the games you have installed. + Csalásokat töltöttél le az összes telepített játékhoz. + + + + Patches Downloaded Successfully! + Frissítések sikeresen letöltve! + + + + All Patches available for all games have been downloaded. + Az összes játékhoz elérhető frissítés letöltésre került. + + + + Games: + Játékok: + + + + PKG File (*.PKG) + PKG fájl (*.PKG) + + + + ELF files (*.bin *.elf *.oelf) + ELF fájlok (*.bin *.elf *.oelf) + + + + Game Boot + Játék indító + + + + Only one file can be selected! + Csak egy fájl választható ki! + + + + PKG Extraction + PKG kicsomagolás + + + + Patch detected! + Frissítés észlelve! + + + + PKG and Game versions match: + A PKG és a játék verziói egyeznek: + + + + Would you like to overwrite? + Szeretné felülírni? + + + + PKG Version %1 is older than installed version: + A %1-es PKG verzió régebbi, mint a telepített verzió: + + + + Game is installed: + A játék telepítve van: + + + + Would you like to install Patch: + Szeretné telepíteni a frissítést: + + + + DLC Installation + DLC Telepítés + + + + Would you like to install DLC: %1? + Szeretné telepíteni a DLC-t: %1? + + + + DLC already installed: + DLC már telepítve: + + + + Game already installed + A játék már telepítve van + + + + PKG is a patch, please install the game first! + A PKG egy javítás, először telepítsd a játékot! + + + + PKG ERROR + PKG HIBA + + + + Extracting PKG %1/%2 + PKG kicsomagolása %1/%2 + + + + Extraction Finished + Kicsomagolás befejezve + + + + Game successfully installed at %1 + A játék sikeresen telepítve itt: %1 + + + + File doesn't appear to be a valid PKG file + A fájl nem tűnik érvényes PKG fájlnak + + + + CheatsPatches + + + Cheats / Patches + Csalások / Javítások + + + + defaultTextEdit_MSG + A csalások/patchek kísérleti jellegűek.\nHasználja őket óvatosan.\n\nTöltse le a csalásokat egyesével a repository kiválasztásával és a letöltés gombra kattintással.\nA Patches fül alatt egyszerre letöltheti az összes patchet, választhat, melyeket szeretné használni, és elmentheti a választását.\n\nMivel nem fejlesztjük a csalásokat/patch-eket,\nkérjük, jelentse a problémákat a csalás szerzőjének.\n\nKészített egy új csalást? Látogasson el ide:\nhttps://github.com/shadps4-emu/ps4_cheats + + + + No Image Available + Nincs elérhető kép + + + + Serial: + Sorozatszám: + + + + Version: + Verzió: + + + + Size: + Méret: + + + + Select Cheat File: + Válaszd ki a csalás fájlt: + + + + Repository: + Tároló: + + + + Download Cheats + Csalások letöltése + + + + Delete File + Fájl törlése + + + + No files selected. + Nincsenek kiválasztott fájlok. + + + + You can delete the cheats you don't want after downloading them. + Törölheted a nem kívánt csalásokat a letöltés után. + + + + Do you want to delete the selected file?\n%1 + Szeretnéd törölni a kiválasztott fájlt?\n%1 + + + + Select Patch File: + Válaszd ki a javítás fájlt: + + + + Download Patches + Javítások letöltése + + + + Save + Mentés + + + + Cheats + Csalások + + + + Patches + Javítások + + + + Error + Hiba + + + + No patch selected. + Nincs kiválasztva javítás. + + + + Unable to open files.json for reading. + Nem sikerült megnyitni a files.json fájlt olvasásra. + + + + No patch file found for the current serial. + Nincs található javítási fájl a jelenlegi sorozatszámhoz. + + + + Unable to open the file for reading. + Nem sikerült megnyitni a fájlt olvasásra. + + + + Unable to open the file for writing. + Nem sikerült megnyitni a fájlt írásra. + + + + Failed to parse XML: + XML elemzési hiba: + + + + Success + Siker + + + + Options saved successfully. + A beállítások sikeresen elmentve. + + + + Invalid Source + Érvénytelen forrás + + + + The selected source is invalid. + A kiválasztott forrás érvénytelen. + + + + File Exists + A fájl létezik + + + + File already exists. Do you want to replace it? + A fájl már létezik. Szeretnéd helyettesíteni? + + + + Failed to save file: + Nem sikerült elmenteni a fájlt: + + + + Failed to download file: + Nem sikerült letölteni a fájlt: + + + + Cheats Not Found + Csalások nem találhatóak + + + + CheatsNotFound_MSG + Nincs található csalás ezen a játékverzión ebben a kiválasztott tárolóban,próbálj meg egy másik tárolót vagy a játék egy másik verzióját. + + + + Cheats Downloaded Successfully + Csalások sikeresen letöltve + + + + CheatsDownloadedSuccessfully_MSG + Sikeresen letöltötted a csalásokat ennek a játéknak a verziójához a kiválasztott tárolóból. Próbálhatsz letölteni egy másik tárolóból is, ha az elérhető, akkor a fájl kiválasztásával az is használható lesz. + + + + Failed to save: + Nem sikerült menteni: + + + + Failed to download: + Nem sikerült letölteni: + + + + Download Complete + Letöltés befejezve + + + + DownloadComplete_MSG + Frissítések sikeresen letöltve! Minden elérhető frissítés letöltésre került, nem szükséges egyesével letölteni őket minden játékhoz, mint a csalások esetében. + + + + Failed to parse JSON data from HTML. + Nem sikerült az JSON adatok elemzése HTML-ből. + + + + Failed to retrieve HTML page. + Nem sikerült HTML oldal lekérése. + + + + Failed to open file: + Nem sikerült megnyitni a fájlt: + + + + XML ERROR: + XML HIBA: + + + + Failed to open files.json for writing + Nem sikerült megnyitni a files.json fájlt írásra + + + + Author: + Szerző: + + + + Directory does not exist: + A könyvtár nem létezik: + + + + Failed to open files.json for reading. + Nem sikerült megnyitni a files.json fájlt olvasásra. + + + + Name: + Név: + + + diff --git a/src/qt_gui/translations/id.ts b/src/qt_gui/translations/id.ts new file mode 100644 index 000000000..b8ce27cde --- /dev/null +++ b/src/qt_gui/translations/id.ts @@ -0,0 +1,902 @@ + + + + AboutDialog + + + About shadPS4 + About shadPS4 + + + + shadPS4 + shadPS4 + + + + shadPS4 is an experimental open-source emulator for the PlayStation 4. + shadPS4 is an experimental open-source emulator for the PlayStation 4. + + + + This software should not be used to play games you have not legally obtained. + This software should not be used to play games you have not legally obtained. + + + + ElfViewer + + + Open Folder + Open Folder + + + + GameInfoClass + + + Loading game list, please wait :3 + Loading game list, please wait :3 + + + + Cancel + Cancel + + + + Loading... + Loading... + + + + GameInstallDialog + + + shadPS4 - Choose directory + shadPS4 - Choose directory + + + + Directory to install games + Directory to install games + + + + Browse + Browse + + + + Error + Error + + + + The value for location to install games is not valid. + The value for location to install games is not valid. + + + + GuiContextMenus + + + Create Shortcut + Create Shortcut + + + + Open Game Folder + Open Game Folder + + + + Cheats / Patches + Cheat / Patch + + + + SFO Viewer + SFO Viewer + + + + Trophy Viewer + Trophy Viewer + + + + Copy info + Copy info + + + + Copy Name + Copy Name + + + + Copy Serial + Copy Serial + + + + Copy All + Copy All + + + + Shortcut creation + Shortcut creation + + + + Shortcut created successfully!\n %1 + Shortcut created successfully!\n %1 + + + + Error + Error + + + + Error creating shortcut!\n %1 + Error creating shortcut!\n %1 + + + + Install PKG + Install PKG + + + + MainWindow + + + Open/Add Elf Folder + Open/Add Elf Folder + + + + Install Packages (PKG) + Install Packages (PKG) + + + + Boot Game + Boot Game + + + + About shadPS4 + About shadPS4 + + + + Configure... + Configure... + + + + Install application from a .pkg file + Install application from a .pkg file + + + + Recent Games + Recent Games + + + + Exit + Exit + + + + Exit shadPS4 + Exit shadPS4 + + + + Exit the application. + Exit the application. + + + + Show Game List + Show Game List + + + + Game List Refresh + Game List Refresh + + + + Tiny + Tiny + + + + Small + Small + + + + Medium + Medium + + + + Large + Large + + + + List View + List View + + + + Grid View + Grid View + + + + Elf Viewer + Elf Viewer + + + + Game Install Directory + Game Install Directory + + + + Download Cheats/Patches + Unduh Cheat / Patch + + + + Dump Game List + Dump Game List + + + + PKG Viewer + PKG Viewer + + + + Search... + Search... + + + + File + File + + + + View + View + + + + Game List Icons + Game List Icons + + + + Game List Mode + Game List Mode + + + + Settings + Settings + + + + Utils + Utils + + + + Themes + Themes + + + + About + About + + + + Dark + Dark + + + + Light + Light + + + + Green + Green + + + + Blue + Blue + + + + Violet + Violet + + + + toolBar + toolBar + + + + PKGViewer + + + Open Folder + Open Folder + + + + TrophyViewer + + + Trophy Viewer + Trophy Viewer + + + + SettingsDialog + + + Settings + Settings + + + + General + General + + + + System + System + + + + Console Language + Console Language + + + + Emulator Language + Emulator Language + + + + Emulator + Emulator + + + + Enable Fullscreen + Enable Fullscreen + + + + Show Splash + Show Splash + + + + Is PS4 Pro + Is PS4 Pro + + + + Username + Username + + + + Logger + Logger + + + + Log Type + Log Type + + + + Log Filter + Log Filter + + + + Graphics + Graphics + + + + Graphics Device + Graphics Device + + + + Width + Width + + + + Height + Height + + + + Vblank Divider + Vblank Divider + + + + Advanced + Advanced + + + + Enable Shaders Dumping + Enable Shaders Dumping + + + + Enable NULL GPU + Enable NULL GPU + + + + Enable PM4 Dumping + Enable PM4 Dumping + + + + Debug + Debug + + + + Enable Debug Dumping + Enable Debug Dumping + + + + Enable Vulkan Validation Layers + Enable Vulkan Validation Layers + + + + Enable Vulkan Synchronization Validation + Enable Vulkan Synchronization Validation + + + + Enable RenderDoc Debugging + Enable RenderDoc Debugging + + + + MainWindow + + + * Unsupported Vulkan Version + * Versi Vulkan Tidak Didukung + + + + Download Cheats For All Installed Games + Unduh Cheat Untuk Semua Game Yang Terpasang + + + + Download Patches For All Games + Unduh Patch Untuk Semua Game + + + + Download Complete + Unduhan Selesai + + + + You have downloaded cheats for all the games you have installed. + Anda telah mengunduh cheat untuk semua game yang terpasang. + + + + Patches Downloaded Successfully! + Patch Berhasil Diunduh! + + + + All Patches available for all games have been downloaded. + Semua Patch yang tersedia untuk semua game telah diunduh. + + + + Games: + Game: + + + + PKG File (*.PKG) + File PKG (*.PKG) + + + + ELF files (*.bin *.elf *.oelf) + File ELF (*.bin *.elf *.oelf) + + + + Game Boot + Boot Game + + + + Only one file can be selected! + Hanya satu file yang bisa dipilih! + + + + PKG Extraction + Ekstraksi PKG + + + + Patch detected! + Patch terdeteksi! + + + + PKG and Game versions match: + Versi PKG dan Game cocok: + + + + Would you like to overwrite? + Apakah Anda ingin menimpa? + + + + PKG Version %1 is older than installed version: + Versi PKG %1 lebih lama dari versi yang terpasang: + + + + Game is installed: + Game telah terpasang: + + + + Would you like to install Patch: + Apakah Anda ingin menginstal patch: + + + + DLC Installation + Instalasi DLC + + + + Would you like to install DLC: %1? + Apakah Anda ingin menginstal DLC: %1? + + + + DLC already installed: + DLC sudah terpasang: + + + + Game already installed + Game sudah terpasang + + + + PKG is a patch, please install the game first! + PKG adalah patch, harap pasang game terlebih dahulu! + + + + PKG ERROR + KESALAHAN PKG + + + + Extracting PKG %1/%2 + Mengekstrak PKG %1/%2 + + + + Extraction Finished + Ekstraksi Selesai + + + + Game successfully installed at %1 + Game berhasil dipasang di %1 + + + + File doesn't appear to be a valid PKG file + File tampaknya bukan file PKG yang valid + + + + CheatsPatches + + + Cheats / Patches + Cheat / Patch + + + + defaultTextEdit_MSG + Cheats/Patches bersifat eksperimental.\nGunakan dengan hati-hati.\n\nUnduh cheats satu per satu dengan memilih repositori dan mengklik tombol unduh.\nDi tab Patches, Anda dapat mengunduh semua patch sekaligus, memilih yang ingin digunakan, dan menyimpan pilihan Anda.\n\nKarena kami tidak mengembangkan Cheats/Patches,\nharap laporkan masalah kepada pembuat cheat.\n\nMembuat cheat baru? Kunjungi:\nhttps://github.com/shadps4-emu/ps4_cheats + + + + No Image Available + Tidak Ada Gambar Tersedia + + + + Serial: + Serial: + + + + Version: + Versi: + + + + Size: + Ukuran: + + + + Select Cheat File: + Pilih File Cheat: + + + + Repository: + Repositori: + + + + Download Cheats + Unduh Cheat + + + + Delete File + Hapus File + + + + No files selected. + Tidak ada file yang dipilih. + + + + You can delete the cheats you don't want after downloading them. + Anda dapat menghapus cheat yang tidak Anda inginkan setelah mengunduhnya. + + + + Do you want to delete the selected file?\n%1 + Apakah Anda ingin menghapus berkas yang dipilih?\n%1 + + + + Select Patch File: + Pilih File Patch: + + + + Download Patches + Unduh Patch + + + + Save + Simpan + + + + Cheats + Cheat + + + + Patches + Patch + + + + Error + Kesalahan + + + + No patch selected. + Tidak ada patch yang dipilih. + + + + Unable to open files.json for reading. + Tidak dapat membuka files.json untuk dibaca. + + + + No patch file found for the current serial. + Tidak ada file patch ditemukan untuk serial saat ini. + + + + Unable to open the file for reading. + Tidak dapat membuka file untuk dibaca. + + + + Unable to open the file for writing. + Tidak dapat membuka file untuk menulis. + + + + Failed to parse XML: + Gagal menganalisis XML: + + + + Success + Sukses + + + + Options saved successfully. + Opsi berhasil disimpan. + + + + Invalid Source + Sumber Tidak Valid + + + + The selected source is invalid. + Sumber yang dipilih tidak valid. + + + + File Exists + File Ada + + + + File already exists. Do you want to replace it? + File sudah ada. Apakah Anda ingin menggantinya? + + + + Failed to save file: + Gagal menyimpan file: + + + + Failed to download file: + Gagal mengunduh file: + + + + Cheats Not Found + Cheat Tidak Ditemukan + + + + CheatsNotFound_MSG + Cheat tidak ditemukan untuk game ini dalam versi repositori yang dipilih,cobalah repositori lain atau versi game yang berbeda. + + + + Cheats Downloaded Successfully + Cheat Berhasil Diunduh + + + + CheatsDownloadedSuccessfully_MSG + Anda telah berhasil mengunduh cheat untuk versi game ini dari repositori yang dipilih. Anda bisa mencoba mengunduh dari repositori lain, jika tersedia akan juga memungkinkan untuk menggunakannya dengan memilih file dari daftar. + + + + Failed to save: + Gagal menyimpan: + + + + Failed to download: + Gagal mengunduh: + + + + Download Complete + Unduhan Selesai + + + + DownloadComplete_MSG + Patch Berhasil Diunduh! Semua Patch yang tersedia untuk semua game telah diunduh, tidak perlu mengunduhnya satu per satu seperti yang terjadi pada Cheat. + + + + Failed to parse JSON data from HTML. + Gagal menganalisis data JSON dari HTML. + + + + Failed to retrieve HTML page. + Gagal mengambil halaman HTML. + + + + Failed to open file: + Gagal membuka file: + + + + XML ERROR: + KESALAHAN XML: + + + + Failed to open files.json for writing + Gagal membuka files.json untuk menulis + + + + Author: + Penulis: + + + + Directory does not exist: + Direktori tidak ada: + + + + Failed to open files.json for reading. + Gagal membuka files.json untuk dibaca. + + + + Name: + Nama: + + + \ No newline at end of file diff --git a/src/qt_gui/translations/it.ts b/src/qt_gui/translations/it.ts new file mode 100644 index 000000000..380a8e43b --- /dev/null +++ b/src/qt_gui/translations/it.ts @@ -0,0 +1,902 @@ + + + + AboutDialog + + + About shadPS4 + Riguardo shadPS4 + + + + shadPS4 + shadPS4 + + + + shadPS4 is an experimental open-source emulator for the PlayStation 4. + shadPS4 è un emulatore sperimentale open source per PlayStation 4. + + + + This software should not be used to play games you have not legally obtained. + Questo programma non dovrebbe essere utilizzato per riprodurre giochi che non vengono ottenuti legalmente. + + + + ElfViewer + + + Open Folder + Apri Cartella + + + + GameInfoClass + + + Loading game list, please wait :3 + Caricamento lista giochi, attendere :3 + + + + Cancel + Annulla + + + + Loading... + Caricamento... + + + + GameInstallDialog + + + shadPS4 - Choose directory + shadPS4 - Scegli cartella + + + + Directory to install games + Cartella di installazione dei giochi + + + + Browse + Sfoglia + + + + Error + Errore + + + + The value for location to install games is not valid. + Il valore del percorso di installazione dei giochi non è valido. + + + + GuiContextMenus + + + Create Shortcut + Crea scorciatoia + + + + Open Game Folder + Apri cartella del gioco + + + + Cheats / Patches + Trucchi / Patch + + + + SFO Viewer + Visualizzatore SFO + + + + Trophy Viewer + Visualizzatore Trofei + + + + Copy info + Copia informazioni + + + + Copy Name + Copia Nome + + + + Copy Serial + Copia Seriale + + + + Copy All + Copia Tutto + + + + Shortcut creation + Creazione scorciatoia + + + + Shortcut created successfully!\n %1 + Scorciatoia creata con successo!\n %1 + + + + Error + Errore + + + + Error creating shortcut!\n %1 + Errore nella creazione della scorciatoia!\n %1 + + + + Install PKG + Installa PKG + + + + MainWindow + + + Open/Add Elf Folder + Apri/Aggiungi cartella Elf + + + + Install Packages (PKG) + Installa Pacchetti (PKG) + + + + Boot Game + Avvia Gioco + + + + About shadPS4 + Riguardo a shadPS4 + + + + Configure... + Configura... + + + + Install application from a .pkg file + Installa applicazione da un file .pkg + + + + Recent Games + Giochi Recenti + + + + Exit + Uscita + + + + Exit shadPS4 + Esci da shadPS4 + + + + Exit the application. + Esci dall'applicazione. + + + + Show Game List + Mostra Lista Giochi + + + + Game List Refresh + Aggiorna Lista Giochi + + + + Tiny + Minuscolo + + + + Small + Piccolo + + + + Medium + Medio + + + + Large + Grande + + + + List View + Visualizzazione Lista + + + + Grid View + Visualizzazione Griglia + + + + Elf Viewer + Visualizzatore Elf + + + + Game Install Directory + Cartella Installazione Giochi + + + + Download Cheats/Patches + Scarica Trucchi / Patch + + + + Dump Game List + Scarica Lista Giochi + + + + PKG Viewer + Visualizzatore PKG + + + + Search... + Cerca... + + + + File + File + + + + View + Visualizza + + + + Game List Icons + Icone Lista Giochi + + + + Game List Mode + Modalità Lista Giochi + + + + Settings + Impostazioni + + + + Utils + Utilità + + + + Themes + Temi + + + + About + Info su + + + + Dark + Scuro + + + + Light + Chiaro + + + + Green + Verde + + + + Blue + Blu + + + + Violet + Viola + + + + toolBar + Barra strumenti + + + + PKGViewer + + + Open Folder + Apri Cartella + + + + TrophyViewer + + + Trophy Viewer + Visualizzatore Trofei + + + + SettingsDialog + + + Settings + Impostazioni + + + + General + Generale + + + + System + Sistema + + + + Console Language + Lingua della console + + + + Emulator Language + Lingua dell'emulatore + + + + Emulator + Emulatore + + + + Enable Fullscreen + Abilita Schermo Intero + + + + Show Splash + Mostra Schermata Iniziale + + + + Is PS4 Pro + Modalità Ps4Pro + + + + Username + Nome Utente + + + + Logger + Logger + + + + Log Type + Tipo di Log + + + + Log Filter + Filtro Log + + + + Graphics + Grafica + + + + Graphics Device + Adattatore grafico + + + + Width + Larghezza + + + + Height + Altezza + + + + Vblank Divider + Divisore Vblank + + + + Advanced + Avanzato + + + + Enable Shaders Dumping + Abilita Dump Shader + + + + Enable NULL GPU + Abilita NULL GPU + + + + Enable PM4 Dumping + Abilita Dump PM4 + + + + Debug + Debug + + + + Enable Debug Dumping + Abilita Dump Debug + + + + Enable Vulkan Validation Layers + Abilita Vulkan Validation Layers + + + + Enable Vulkan Synchronization Validation + Abilita Vulkan Synchronization Validation + + + + Enable RenderDoc Debugging + Abilita Debugging RenderDoc + + + + MainWindow + + + * Unsupported Vulkan Version + * Versione Vulkan non supportata + + + + Download Cheats For All Installed Games + Scarica Trucchi per tutti i giochi installati + + + + Download Patches For All Games + Scarica Patch per tutti i giochi + + + + Download Complete + Scaricamento completato + + + + You have downloaded cheats for all the games you have installed. + Hai scaricato trucchi per tutti i giochi installati. + + + + Patches Downloaded Successfully! + Patch scaricate con successo! + + + + All Patches available for all games have been downloaded. + Tutte le patch disponibili per tutti i giochi sono state scaricate. + + + + Games: + Giochi: + + + + PKG File (*.PKG) + File PKG (*.PKG) + + + + ELF files (*.bin *.elf *.oelf) + File ELF (*.bin *.elf *.oelf) + + + + Game Boot + Avvia Gioco + + + + Only one file can be selected! + Si può selezionare solo un file! + + + + PKG Extraction + Estrazione file PKG + + + + Patch detected! + Patch rilevata! + + + + PKG and Game versions match: + Le versioni di PKG e del gioco corrispondono: + + + + Would you like to overwrite? + Vuoi sovrascrivere? + + + + PKG Version %1 is older than installed version: + La versione PKG %1 è più vecchia rispetto alla versione installata: + + + + Game is installed: + Gioco installato: + + + + Would you like to install Patch: + Vuoi installare la patch: + + + + DLC Installation + Installazione DLC + + + + Would you like to install DLC: %1? + Vuoi installare il DLC: %1? + + + + DLC already installed: + DLC già installato: + + + + Game already installed + Gioco già installato + + + + PKG is a patch, please install the game first! + Questo file PKG contiene una patch. Per favore, installa prima il gioco! + + + + PKG ERROR + ERRORE PKG + + + + Extracting PKG %1/%2 + Estrazione file PKG %1/%2 + + + + Extraction Finished + Estrazione Completata + + + + Game successfully installed at %1 + Gioco installato correttamente in %1 + + + + File doesn't appear to be a valid PKG file + Il file sembra non essere un file PKG valido + + + + CheatsPatches + + + Cheats / Patches + Trucchi / Patch + + + + defaultTextEdit_MSG + I trucchi e le patch sono sperimentali.\nUtilizzali con cautela.\n\nScarica i trucchi singolarmente selezionando l'archivio e cliccando sul pulsante di download.\nNella scheda Patch, puoi scaricare tutte le patch in una volta sola, scegliere quali vuoi utilizzare e salvare la tua selezione.\n\nPoiché non sviluppiamo i trucchi e le patch,\nper favore segnala i problemi all'autore dei trucchi.\n\nHai creato un nuovo trucco? Visita:\nhttps://github.com/shadps4-emu/ps4_cheats + + + + No Image Available + Nessuna immagine disponibile + + + + Serial: + Seriale: + + + + Version: + Versione: + + + + Size: + Dimensione: + + + + Select Cheat File: + Seleziona File Trucchi: + + + + Repository: + Archivio: + + + + Download Cheats + Scarica trucchi + + + + Delete File + Cancella File + + + + No files selected. + Nessun file selezionato. + + + + You can delete the cheats you don't want after downloading them. + Puoi cancellare i trucchi che non vuoi utilizzare dopo averli scaricati. + + + + Do you want to delete the selected file?\n%1 + Vuoi cancellare il file selezionato?\n%1 + + + + Select Patch File: + Seleziona File Patch: + + + + Download Patches + Scarica Patch + + + + Save + Salva + + + + Cheats + Trucchi + + + + Patches + Patch + + + + Error + Errore + + + + No patch selected. + Nessuna patch selezionata. + + + + Unable to open files.json for reading. + Impossibile aprire il file .json per la lettura. + + + + No patch file found for the current serial. + Nessun file patch trovato per il seriale selezionato. + + + + Unable to open the file for reading. + Impossibile aprire il file per la lettura. + + + + Unable to open the file for writing. + Impossibile aprire il file per la scrittura. + + + + Failed to parse XML: + Analisi XML fallita: + + + + Success + Successo + + + + Options saved successfully. + Opzioni salvate con successo. + + + + Invalid Source + Fonte non valida + + + + The selected source is invalid. + La fonte selezionata non è valida. + + + + File Exists + Il file è presente + + + + File already exists. Do you want to replace it? + Il file è già presente. Vuoi sostituirlo? + + + + Failed to save file: + Salvataggio file fallito: + + + + Failed to download file: + Scaricamento file fallito: + + + + Cheats Not Found + Trucchi non trovati + + + + CheatsNotFound_MSG + Non sono stati trovati trucchi per questa versione del gioco nell'archivio selezionato, prova un altro archivio o una versione diversa del gioco. + + + + Cheats Downloaded Successfully + Trucchi scaricati con successo! + + + + CheatsDownloadedSuccessfully_MSG + Hai scaricato con successo i trucchi per questa versione del gioco dall'archivio selezionato. Puoi provare a scaricare da un altro archivio, se disponibile, puoi anche utilizzarlo selezionando il file dall'elenco. + + + + Failed to save: + Salvataggio fallito: + + + + Failed to download: + Impossibile scaricare: + + + + Download Complete + Scaricamento completo + + + + DownloadComplete_MSG + Patch scaricata con successo! Vengono scaricate tutte le patch disponibili per tutti i giochi, non è necessario scaricarle singolarmente per ogni gioco come nel caso dei trucchi. + + + + Failed to parse JSON data from HTML. + Impossibile analizzare i dati JSON dall'HTML. + + + + Failed to retrieve HTML page. + Impossibile recuperare la pagina HTML. + + + + Failed to open file: + Impossibile aprire file: + + + + XML ERROR: + ERRORE XML: + + + + Failed to open files.json for writing + Impossibile aprire i file .json per la scrittura + + + + Author: + Autore: + + + + Directory does not exist: + La cartella non esiste: + + + + Failed to open files.json for reading. + Impossibile aprire i file .json per la lettura. + + + + Name: + Nome: + + + diff --git a/src/qt_gui/translations/ja_JP.ts b/src/qt_gui/translations/ja_JP.ts new file mode 100644 index 000000000..3d62de0de --- /dev/null +++ b/src/qt_gui/translations/ja_JP.ts @@ -0,0 +1,902 @@ + + + + AboutDialog + + + About shadPS4 + shadPS4について + + + + shadPS4 + shadPS4 + + + + shadPS4 is an experimental open-source emulator for the PlayStation 4. + shadPS4は、PlayStation 4の実験的なオープンソースエミュレーターです。 + + + + This software should not be used to play games you have not legally obtained. + このソフトウェアは、合法的に入手していないゲームをプレイするために使用するものではありません。 + + + + ElfViewer + + + Open Folder + フォルダを開く + + + + GameInfoClass + + + Loading game list, please wait :3 + ゲームリストを読み込み中です。お待ちください :3 + + + + Cancel + キャンセル + + + + Loading... + 読み込み中... + + + + GameInstallDialog + + + shadPS4 - Choose directory + shadPS4 - ディレクトリを選択 + + + + Directory to install games + ゲームをインストールするディレクトリ + + + + Browse + 参照 + + + + Error + エラー + + + + The value for location to install games is not valid. + ゲームをインストールする場所が無効です。 + + + + GuiContextMenus + + + Create Shortcut + ショートカットを作成 + + + + Open Game Folder + ゲームフォルダを開く + + + + Cheats / Patches + チート / パッチ + + + + SFO Viewer + SFOビューワー + + + + Trophy Viewer + トロフィービューワー + + + + Copy info + 情報をコピー + + + + Copy Name + 名前をコピー + + + + Copy Serial + シリアルをコピー + + + + Copy All + すべてコピー + + + + Shortcut creation + ショートカットの作成 + + + + Shortcut created successfully!\n %1 + ショートカットが正常に作成されました!\n %1 + + + + Error + エラー + + + + Error creating shortcut!\n %1 + ショートカットの作成に失敗しました!\n %1 + + + + Install PKG + PKGをインストール + + + + MainWindow + + + Open/Add Elf Folder + Elfフォルダを開く/追加する + + + + Install Packages (PKG) + パッケージをインストール (PKG) + + + + Boot Game + ゲームを起動 + + + + About shadPS4 + shadPS4について + + + + Configure... + 設定... + + + + Install application from a .pkg file + .pkgファイルからアプリケーションをインストールする + + + + Recent Games + 最近のゲーム + + + + Exit + 終了 + + + + Exit shadPS4 + shadPS4を終了 + + + + Exit the application. + アプリケーションを終了します。 + + + + Show Game List + ゲームリストを表示 + + + + Game List Refresh + ゲームリストの更新 + + + + Tiny + 極小 + + + + Small + + + + + Medium + + + + + Large + + + + + List View + リストビュー + + + + Grid View + グリッドビュー + + + + Elf Viewer + Elfビュワー + + + + Game Install Directory + ゲームインストールディレクトリ + + + + Download Cheats/Patches + チート / パッチをダウンロード + + + + Dump Game List + ゲームリストをダンプ + + + + PKG Viewer + PKGビューアー + + + + Search... + 検索... + + + + File + ファイル + + + + View + 表示 + + + + Game List Icons + ゲームリストアイコン + + + + Game List Mode + ゲームリストモード + + + + Settings + 設定 + + + + Utils + ユーティリティ + + + + Themes + テーマ + + + + About + 情報 + + + + Dark + ダーク + + + + Light + ライト + + + + Green + グリーン + + + + Blue + ブルー + + + + Violet + バイオレット + + + + toolBar + ツールバー + + + + PKGViewer + + + Open Folder + フォルダーを開く + + + + TrophyViewer + + + Trophy Viewer + トロフィービューアー + + + + SettingsDialog + + + Settings + 設定 + + + + General + 一般 + + + + System + システム + + + + Console Language + コンソール言語 + + + + Emulator Language + エミュレーター言語 + + + + Emulator + エミュレーター + + + + Enable Fullscreen + フルスクリーンを有効にする + + + + Show Splash + スプラッシュを表示する + + + + Is PS4 Pro + PS4 Proモード + + + + Username + ユーザー名 + + + + Logger + ロガー + + + + Log Type + ログタイプ + + + + Log Filter + ログフィルター + + + + Graphics + グラフィックス + + + + Graphics Device + グラフィックスデバイス + + + + Width + + + + + Height + 高さ + + + + Vblank Divider + Vblankディバイダー + + + + Advanced + 高度な設定 + + + + Enable Shaders Dumping + シェーダーのダンプを有効にする + + + + Enable NULL GPU + NULL GPUを有効にする + + + + Enable PM4 Dumping + PM4ダンプを有効にする + + + + Debug + デバッグ + + + + Enable Debug Dumping + デバッグダンプを有効にする + + + + Enable Vulkan Validation Layers + Vulkan検証レイヤーを有効にする + + + + Enable Vulkan Synchronization Validation + Vulkan同期検証を有効にする + + + + Enable RenderDoc Debugging + RenderDocデバッグを有効にする + + + + MainWindow + + + * Unsupported Vulkan Version + * サポートされていないVulkanバージョン + + + + Download Cheats For All Installed Games + すべてのインストール済みゲームのチートをダウンロード + + + + Download Patches For All Games + すべてのゲームのパッチをダウンロード + + + + Download Complete + ダウンロード完了 + + + + You have downloaded cheats for all the games you have installed. + インストールしたすべてのゲームのチートをダウンロードしました。 + + + + Patches Downloaded Successfully! + パッチが正常にダウンロードされました! + + + + All Patches available for all games have been downloaded. + すべてのゲームに利用可能なパッチがダウンロードされました。 + + + + Games: + ゲーム: + + + + PKG File (*.PKG) + PKGファイル (*.PKG) + + + + ELF files (*.bin *.elf *.oelf) + ELFファイル (*.bin *.elf *.oelf) + + + + Game Boot + ゲームブート + + + + Only one file can be selected! + 1つのファイルしか選択できません! + + + + PKG Extraction + PKG抽出 + + + + Patch detected! + パッチが検出されました! + + + + PKG and Game versions match: + PKGとゲームのバージョンが一致しています: + + + + Would you like to overwrite? + 上書きしてもよろしいですか? + + + + PKG Version %1 is older than installed version: + PKGバージョン %1 はインストールされているバージョンよりも古いです: + + + + Game is installed: + ゲームはインストール済みです: + + + + Would you like to install Patch: + パッチをインストールしてもよろしいですか: + + + + DLC Installation + DLCのインストール + + + + Would you like to install DLC: %1? + DLCをインストールしてもよろしいですか: %1? + + + + DLC already installed: + DLCはすでにインストールされています: + + + + Game already installed + ゲームはすでにインストールされています + + + + PKG is a patch, please install the game first! + PKGはパッチです。ゲームを先にインストールしてください! + + + + PKG ERROR + PKGエラー + + + + Extracting PKG %1/%2 + PKGを抽出中 %1/%2 + + + + Extraction Finished + 抽出完了 + + + + Game successfully installed at %1 + ゲームが %1 に正常にインストールされました + + + + File doesn't appear to be a valid PKG file + ファイルが有効なPKGファイルでないようです + + + + CheatsPatches + + + Cheats / Patches + チート / パッチ + + + + defaultTextEdit_MSG + チート/パッチは実験的です。\n使用には注意してください。\n\nリポジトリを選択し、ダウンロードボタンをクリックしてチートを個別にダウンロードします。\n「Patches」タブでは、すべてのパッチを一度にダウンロードし、使用したいものを選択して選択を保存できます。\n\nチート/パッチを開発していないため、\n問題があればチートの作者に報告してください。\n\n新しいチートを作成しましたか?\nhttps://github.com/shadps4-emu/ps4_cheats を訪問してください。 + + + + No Image Available + 画像は利用できません + + + + Serial: + シリアル: + + + + Version: + バージョン: + + + + Size: + サイズ: + + + + Select Cheat File: + チートファイルを選択: + + + + Repository: + リポジトリ: + + + + Download Cheats + チートをダウンロード + + + + Delete File + ファイルを削除 + + + + No files selected. + ファイルが選択されていません。 + + + + You can delete the cheats you don't want after downloading them. + ダウンロード後に不要なチートを削除できます。 + + + + Do you want to delete the selected file?\n%1 + 選択したファイルを削除しますか?\n%1 + + + + Select Patch File: + パッチファイルを選択: + + + + Download Patches + パッチをダウンロード + + + + Save + 保存 + + + + Cheats + チート + + + + Patches + パッチ + + + + Error + エラー + + + + No patch selected. + パッチが選択されていません。 + + + + Unable to open files.json for reading. + files.jsonを読み込み用に開けません。 + + + + No patch file found for the current serial. + 現在のシリアルに対するパッチファイルが見つかりません。 + + + + Unable to open the file for reading. + ファイルを読み込み用に開けません。 + + + + Unable to open the file for writing. + ファイルを記録用に開けません。 + + + + Failed to parse XML: + XMLの解析に失敗しました: + + + + Success + 成功 + + + + Options saved successfully. + オプションが正常に保存されました。 + + + + Invalid Source + 無効なソース + + + + The selected source is invalid. + 選択したソースは無効です。 + + + + File Exists + ファイルが存在します + + + + File already exists. Do you want to replace it? + ファイルはすでに存在します。置き換えますか? + + + + Failed to save file: + ファイルの保存に失敗しました: + + + + Failed to download file: + ファイルのダウンロードに失敗しました: + + + + Cheats Not Found + チートが見つかりません + + + + CheatsNotFound_MSG + このゲームのこのバージョンのチートが選択されたリポジトリに見つかりませんでした。別のリポジトリまたはゲームの別のバージョンを試してください。 + + + + Cheats Downloaded Successfully + チートが正常にダウンロードされました + + + + CheatsDownloadedSuccessfully_MSG + このゲームのこのバージョンのチートをリポジトリから正常にダウンロードしました。 別のリポジトリからのダウンロードも試せます。利用可能であれば、リストからファイルを選択して使用することも可能です。 + + + + Failed to save: + 保存に失敗しました: + + + + Failed to download: + ダウンロードに失敗しました: + + + + Download Complete + ダウンロード完了 + + + + DownloadComplete_MSG + パッチが正常にダウンロードされました! すべてのゲームに利用可能なパッチがダウンロードされました。チートとは異なり、各ゲームごとに個別にダウンロードする必要はありません。 + + + + Failed to parse JSON data from HTML. + HTMLからJSONデータの解析に失敗しました。 + + + + Failed to retrieve HTML page. + HTMLページの取得に失敗しました。 + + + + Failed to open file: + ファイルを開くのに失敗しました: + + + + XML ERROR: + XMLエラー: + + + + Failed to open files.json for writing + files.jsonを記録用に開けません + + + + Author: + 著者: + + + + Directory does not exist: + ディレクトリが存在しません: + + + + Failed to open files.json for reading. + files.jsonを読み込み用に開けません。 + + + + Name: + 名前: + + + \ No newline at end of file diff --git a/src/qt_gui/translations/ko_KR.ts b/src/qt_gui/translations/ko_KR.ts new file mode 100644 index 000000000..f7f171dc6 --- /dev/null +++ b/src/qt_gui/translations/ko_KR.ts @@ -0,0 +1,902 @@ + + + + AboutDialog + + + About shadPS4 + About shadPS4 + + + + shadPS4 + shadPS4 + + + + shadPS4 is an experimental open-source emulator for the PlayStation 4. + shadPS4 is an experimental open-source emulator for the PlayStation 4. + + + + This software should not be used to play games you have not legally obtained. + This software should not be used to play games you have not legally obtained. + + + + ElfViewer + + + Open Folder + Open Folder + + + + GameInfoClass + + + Loading game list, please wait :3 + Loading game list, please wait :3 + + + + Cancel + Cancel + + + + Loading... + Loading... + + + + GameInstallDialog + + + shadPS4 - Choose directory + shadPS4 - Choose directory + + + + Directory to install games + Directory to install games + + + + Browse + Browse + + + + Error + Error + + + + The value for location to install games is not valid. + The value for location to install games is not valid. + + + + GuiContextMenus + + + Create Shortcut + Create Shortcut + + + + Open Game Folder + Open Game Folder + + + + Cheats / Patches + 치트 / 패치 + + + + SFO Viewer + SFO Viewer + + + + Trophy Viewer + Trophy Viewer + + + + Copy info + Copy info + + + + Copy Name + Copy Name + + + + Copy Serial + Copy Serial + + + + Copy All + Copy All + + + + Shortcut creation + Shortcut creation + + + + Shortcut created successfully!\n %1 + Shortcut created successfully!\n %1 + + + + Error + Error + + + + Error creating shortcut!\n %1 + Error creating shortcut!\n %1 + + + + Install PKG + Install PKG + + + + MainWindow + + + Open/Add Elf Folder + Open/Add Elf Folder + + + + Install Packages (PKG) + Install Packages (PKG) + + + + Boot Game + Boot Game + + + + About shadPS4 + About shadPS4 + + + + Configure... + Configure... + + + + Install application from a .pkg file + Install application from a .pkg file + + + + Recent Games + Recent Games + + + + Exit + Exit + + + + Exit shadPS4 + Exit shadPS4 + + + + Exit the application. + Exit the application. + + + + Show Game List + Show Game List + + + + Game List Refresh + Game List Refresh + + + + Tiny + Tiny + + + + Small + Small + + + + Medium + Medium + + + + Large + Large + + + + List View + List View + + + + Grid View + Grid View + + + + Elf Viewer + Elf Viewer + + + + Game Install Directory + Game Install Directory + + + + Download Cheats/Patches + 치트 / 패치 다운로드 + + + + Dump Game List + Dump Game List + + + + PKG Viewer + PKG Viewer + + + + Search... + Search... + + + + File + File + + + + View + View + + + + Game List Icons + Game List Icons + + + + Game List Mode + Game List Mode + + + + Settings + Settings + + + + Utils + Utils + + + + Themes + Themes + + + + About + About + + + + Dark + Dark + + + + Light + Light + + + + Green + Green + + + + Blue + Blue + + + + Violet + Violet + + + + toolBar + toolBar + + + + PKGViewer + + + Open Folder + Open Folder + + + + TrophyViewer + + + Trophy Viewer + Trophy Viewer + + + + SettingsDialog + + + Settings + Settings + + + + General + General + + + + System + System + + + + Console Language + Console Language + + + + Emulator Language + Emulator Language + + + + Emulator + Emulator + + + + Enable Fullscreen + Enable Fullscreen + + + + Show Splash + Show Splash + + + + Is PS4 Pro + Is PS4 Pro + + + + Username + Username + + + + Logger + Logger + + + + Log Type + Log Type + + + + Log Filter + Log Filter + + + + Graphics + Graphics + + + + Graphics Device + Graphics Device + + + + Width + Width + + + + Height + Height + + + + Vblank Divider + Vblank Divider + + + + Advanced + Advanced + + + + Enable Shaders Dumping + Enable Shaders Dumping + + + + Enable NULL GPU + Enable NULL GPU + + + + Enable PM4 Dumping + Enable PM4 Dumping + + + + Debug + Debug + + + + Enable Debug Dumping + Enable Debug Dumping + + + + Enable Vulkan Validation Layers + Enable Vulkan Validation Layers + + + + Enable Vulkan Synchronization Validation + Enable Vulkan Synchronization Validation + + + + Enable RenderDoc Debugging + Enable RenderDoc Debugging + + + + MainWindow + + + * Unsupported Vulkan Version + * Unsupported Vulkan Version + + + + Download Cheats For All Installed Games + Download Cheats For All Installed Games + + + + Download Patches For All Games + Download Patches For All Games + + + + Download Complete + Download Complete + + + + You have downloaded cheats for all the games you have installed. + You have downloaded cheats for all the games you have installed. + + + + Patches Downloaded Successfully! + Patches Downloaded Successfully! + + + + All Patches available for all games have been downloaded. + All Patches available for all games have been downloaded. + + + + Games: + Games: + + + + PKG File (*.PKG) + PKG File (*.PKG) + + + + ELF files (*.bin *.elf *.oelf) + ELF files (*.bin *.elf *.oelf) + + + + Game Boot + Game Boot + + + + Only one file can be selected! + Only one file can be selected! + + + + PKG Extraction + PKG Extraction + + + + Patch detected! + Patch detected! + + + + PKG and Game versions match: + PKG and Game versions match: + + + + Would you like to overwrite? + Would you like to overwrite? + + + + PKG Version %1 is older than installed version: + PKG Version %1 is older than installed version: + + + + Game is installed: + Game is installed: + + + + Would you like to install Patch: + Would you like to install Patch: + + + + DLC Installation + DLC Installation + + + + Would you like to install DLC: %1? + Would you like to install DLC: %1? + + + + DLC already installed: + DLC already installed: + + + + Game already installed + Game already installed + + + + PKG is a patch, please install the game first! + PKG is a patch, please install the game first! + + + + PKG ERROR + PKG ERROR + + + + Extracting PKG %1/%2 + Extracting PKG %1/%2 + + + + Extraction Finished + Extraction Finished + + + + Game successfully installed at %1 + Game successfully installed at %1 + + + + File doesn't appear to be a valid PKG file + File doesn't appear to be a valid PKG file + + + + CheatsPatches + + + Cheats / Patches + Cheats / Patches + + + + defaultTextEdit_MSG + Cheats/Patches are experimental.\nUse with caution.\n\nDownload cheats individually by selecting the repository and clicking the download button.\nIn the Patches tab, you can download all patches at once, choose which ones you want to use, and save your selection.\n\nSince we do not develop the Cheats/Patches,\nplease report issues to the cheat author.\n\nCreated a new cheat? Visit:\nhttps://github.com/shadps4-emu/ps4_cheats + + + + No Image Available + No Image Available + + + + Serial: + Serial: + + + + Version: + Version: + + + + Size: + Size: + + + + Select Cheat File: + Select Cheat File: + + + + Repository: + Repository: + + + + Download Cheats + Download Cheats + + + + Delete File + Delete File + + + + No files selected. + No files selected. + + + + You can delete the cheats you don't want after downloading them. + You can delete the cheats you don't want after downloading them. + + + + Do you want to delete the selected file?\n%1 + Do you want to delete the selected file?\n%1 + + + + Select Patch File: + Select Patch File: + + + + Download Patches + Download Patches + + + + Save + Save + + + + Cheats + Cheats + + + + Patches + Patches + + + + Error + Error + + + + No patch selected. + No patch selected. + + + + Unable to open files.json for reading. + Unable to open files.json for reading. + + + + No patch file found for the current serial. + No patch file found for the current serial. + + + + Unable to open the file for reading. + Unable to open the file for reading. + + + + Unable to open the file for writing. + Unable to open the file for writing. + + + + Failed to parse XML: + Failed to parse XML: + + + + Success + Success + + + + Options saved successfully. + Options saved successfully. + + + + Invalid Source + Invalid Source + + + + The selected source is invalid. + The selected source is invalid. + + + + File Exists + File Exists + + + + File already exists. Do you want to replace it? + File already exists. Do you want to replace it? + + + + Failed to save file: + Failed to save file: + + + + Failed to download file: + Failed to download file: + + + + Cheats Not Found + Cheats Not Found + + + + CheatsNotFound_MSG + No Cheats found for this game in this version of the selected repository,try another repository or a different version of the game. + + + + Cheats Downloaded Successfully + Cheats Downloaded Successfully + + + + CheatsDownloadedSuccessfully_MSG + You have successfully downloaded the cheats for this version of the game from the selected repository. You can try downloading from another repository, if it is available it will also be possible to use it by selecting the file from the list. + + + + Failed to save: + Failed to save: + + + + Failed to download: + Failed to download: + + + + Download Complete + Download Complete + + + + DownloadComplete_MSG + Patches Downloaded Successfully! All Patches available for all games have been downloaded, there is no need to download them individually for each game as happens in Cheats. + + + + Failed to parse JSON data from HTML. + Failed to parse JSON data from HTML. + + + + Failed to retrieve HTML page. + Failed to retrieve HTML page. + + + + Failed to open file: + Failed to open file: + + + + XML ERROR: + XML ERROR: + + + + Failed to open files.json for writing + Failed to open files.json for writing + + + + Author: + Author: + + + + Directory does not exist: + Directory does not exist: + + + + Failed to open files.json for reading. + Failed to open files.json for reading. + + + + Name: + Name: + + + \ No newline at end of file diff --git a/src/qt_gui/translations/lt_LT.ts b/src/qt_gui/translations/lt_LT.ts new file mode 100644 index 000000000..7aa4402e6 --- /dev/null +++ b/src/qt_gui/translations/lt_LT.ts @@ -0,0 +1,902 @@ + + + + AboutDialog + + + About shadPS4 + About shadPS4 + + + + shadPS4 + shadPS4 + + + + shadPS4 is an experimental open-source emulator for the PlayStation 4. + shadPS4 is an experimental open-source emulator for the PlayStation 4. + + + + This software should not be used to play games you have not legally obtained. + This software should not be used to play games you have not legally obtained. + + + + ElfViewer + + + Open Folder + Open Folder + + + + GameInfoClass + + + Loading game list, please wait :3 + Loading game list, please wait :3 + + + + Cancel + Cancel + + + + Loading... + Loading... + + + + GameInstallDialog + + + shadPS4 - Choose directory + shadPS4 - Choose directory + + + + Directory to install games + Directory to install games + + + + Browse + Browse + + + + Error + Error + + + + The value for location to install games is not valid. + The value for location to install games is not valid. + + + + GuiContextMenus + + + Create Shortcut + Create Shortcut + + + + Open Game Folder + Open Game Folder + + + + Apgaulės / Pleistrai + Cheats / Patches + + + + SFO Viewer + SFO Viewer + + + + Trophy Viewer + Trophy Viewer + + + + Copy info + Copy info + + + + Copy Name + Copy Name + + + + Copy Serial + Copy Serial + + + + Copy All + Copy All + + + + Shortcut creation + Shortcut creation + + + + Shortcut created successfully!\n %1 + Shortcut created successfully!\n %1 + + + + Error + Error + + + + Error creating shortcut!\n %1 + Error creating shortcut!\n %1 + + + + Install PKG + Install PKG + + + + MainWindow + + + Open/Add Elf Folder + Open/Add Elf Folder + + + + Install Packages (PKG) + Install Packages (PKG) + + + + Boot Game + Boot Game + + + + About shadPS4 + About shadPS4 + + + + Configure... + Configure... + + + + Install application from a .pkg file + Install application from a .pkg file + + + + Recent Games + Recent Games + + + + Exit + Exit + + + + Exit shadPS4 + Exit shadPS4 + + + + Exit the application. + Exit the application. + + + + Show Game List + Show Game List + + + + Game List Refresh + Game List Refresh + + + + Tiny + Tiny + + + + Small + Small + + + + Medium + Medium + + + + Large + Large + + + + List View + List View + + + + Grid View + Grid View + + + + Elf Viewer + Elf Viewer + + + + Game Install Directory + Game Install Directory + + + + Download Cheats/Patches + Atsisiųsti Apgaules / Pleistrus + + + + Dump Game List + Dump Game List + + + + PKG Viewer + PKG Viewer + + + + Search... + Search... + + + + File + File + + + + View + View + + + + Game List Icons + Game List Icons + + + + Game List Mode + Game List Mode + + + + Settings + Settings + + + + Utils + Utils + + + + Themes + Themes + + + + About + About + + + + Dark + Dark + + + + Light + Light + + + + Green + Green + + + + Blue + Blue + + + + Violet + Violet + + + + toolBar + toolBar + + + + PKGViewer + + + Open Folder + Open Folder + + + + TrophyViewer + + + Trophy Viewer + Trophy Viewer + + + + SettingsDialog + + + Settings + Settings + + + + General + General + + + + System + System + + + + Console Language + Console Language + + + + Emulator Language + Emulator Language + + + + Emulator + Emulator + + + + Enable Fullscreen + Enable Fullscreen + + + + Show Splash + Show Splash + + + + Is PS4 Pro + Is PS4 Pro + + + + Username + Username + + + + Logger + Logger + + + + Log Type + Log Type + + + + Log Filter + Log Filter + + + + Graphics + Graphics + + + + Graphics Device + Graphics Device + + + + Width + Width + + + + Height + Height + + + + Vblank Divider + Vblank Divider + + + + Advanced + Advanced + + + + Enable Shaders Dumping + Enable Shaders Dumping + + + + Enable NULL GPU + Enable NULL GPU + + + + Enable PM4 Dumping + Enable PM4 Dumping + + + + Debug + Debug + + + + Enable Debug Dumping + Enable Debug Dumping + + + + Enable Vulkan Validation Layers + Enable Vulkan Validation Layers + + + + Enable Vulkan Synchronization Validation + Enable Vulkan Synchronization Validation + + + + Enable RenderDoc Debugging + Enable RenderDoc Debugging + + + + MainWindow + + + * Unsupported Vulkan Version + * Nepalaikoma Vulkan versija + + + + Download Cheats For All Installed Games + Atsisiųsti sukčiavimus visiems įdiegtiems žaidimams + + + + Download Patches For All Games + Atsisiųsti pataisas visiems žaidimams + + + + Download Complete + Atsisiuntimas baigtas + + + + You have downloaded cheats for all the games you have installed. + Jūs atsisiuntėte sukčiavimus visiems jūsų įdiegtiesiems žaidimams. + + + + Patches Downloaded Successfully! + Pataisos sėkmingai atsisiųstos! + + + + All Patches available for all games have been downloaded. + Visos pataisos visiems žaidimams buvo atsisiųstos. + + + + Games: + Žaidimai: + + + + PKG File (*.PKG) + PKG failas (*.PKG) + + + + ELF files (*.bin *.elf *.oelf) + ELF failai (*.bin *.elf *.oelf) + + + + Game Boot + Žaidimo paleidimas + + + + Only one file can be selected! + Galite pasirinkti tik vieną failą! + + + + PKG Extraction + PKG ištraukimas + + + + Patch detected! + Rasta atnaujinimą! + + + + PKG and Game versions match: + PKG ir žaidimo versijos sutampa: + + + + Would you like to overwrite? + Ar norite perrašyti? + + + + PKG Version %1 is older than installed version: + PKG versija %1 yra senesnė nei įdiegta versija: + + + + Game is installed: + Žaidimas įdiegtas: + + + + Would you like to install Patch: + Ar norite įdiegti atnaujinimą: + + + + DLC Installation + DLC diegimas + + + + Would you like to install DLC: %1? + Ar norite įdiegti DLC: %1? + + + + DLC already installed: + DLC jau įdiegtas: + + + + Game already installed + Žaidimas jau įdiegtas + + + + PKG is a patch, please install the game first! + PKG yra pataisa, prašome pirmiausia įdiegti žaidimą! + + + + PKG ERROR + PKG KLAIDA + + + + Extracting PKG %1/%2 + Ekstrakcinis PKG %1/%2 + + + + Extraction Finished + Ekstrakcija baigta + + + + Game successfully installed at %1 + Žaidimas sėkmingai įdiegtas %1 + + + + File doesn't appear to be a valid PKG file + Failas atrodo, kad nėra galiojantis PKG failas + + + + CheatsPatches + + + Cheats / Patches + Sukčiavimai / Pataisos + + + + defaultTextEdit_MSG + Cheats/Patches yra eksperimentiniai.\nNaudokite atsargiai.\n\nAtsisiųskite cheats atskirai pasirinkdami saugyklą ir paspausdami atsisiuntimo mygtuką.\nPatches skirtuke galite atsisiųsti visus patch’us vienu metu, pasirinkti, kuriuos norite naudoti, ir išsaugoti pasirinkimą.\n\nKadangi mes nekurime Cheats/Patches,\npraneškite problemas cheat autoriui.\n\nSukūrėte naują cheat? Apsilankykite:\nhttps://github.com/shadps4-emu/ps4_cheats + + + + No Image Available + Nuotrauka neprieinama + + + + Serial: + Seriinis numeris: + + + + Version: + Versija: + + + + Size: + Dydis: + + + + Select Cheat File: + Pasirinkite sukčiavimo failą: + + + + Repository: + Saugykla: + + + + Download Cheats + Atsisiųsti sukčiavimus + + + + Delete File + Pašalinti failą + + + + No files selected. + Failai nepasirinkti. + + + + You can delete the cheats you don't want after downloading them. + Galite pašalinti sukčiavimus, kurių nenorite, juos atsisiuntę. + + + + Do you want to delete the selected file?\n%1 + Ar norite ištrinti pasirinktą failą?\n%1 + + + + Select Patch File: + Pasirinkite pataisos failą: + + + + Download Patches + Atsisiųsti pataisas + + + + Save + Įrašyti + + + + Cheats + Sukčiavimai + + + + Patches + Pataisos + + + + Error + Klaida + + + + No patch selected. + Nieko nepataisyta. + + + + Unable to open files.json for reading. + Neįmanoma atidaryti files.json skaitymui. + + + + No patch file found for the current serial. + Nepavyko rasti pataisos failo dabartiniam serijiniam numeriui. + + + + Unable to open the file for reading. + Neįmanoma atidaryti failo skaitymui. + + + + Unable to open the file for writing. + Neįmanoma atidaryti failo rašymui. + + + + Failed to parse XML: + Nepavyko išanalizuoti XML: + + + + Success + Sėkmė + + + + Options saved successfully. + Nustatymai sėkmingai išsaugoti. + + + + Invalid Source + Netinkamas šaltinis + + + + The selected source is invalid. + Pasirinktas šaltinis yra netinkamas. + + + + File Exists + Failas egzistuoja + + + + File already exists. Do you want to replace it? + Failas jau egzistuoja. Ar norite jį pakeisti? + + + + Failed to save file: + Nepavyko išsaugoti failo: + + + + Failed to download file: + Nepavyko atsisiųsti failo: + + + + Cheats Not Found + Sukčiavimai nerasti + + + + CheatsNotFound_MSG + Nerasta sukčiavimų šiam žaidimui šioje pasirinktos saugyklos versijoje,bandykite kitą saugyklą arba skirtingą žaidimo versiją. + + + + Cheats Downloaded Successfully + Sukčiavimai sėkmingai atsisiųsti + + + + CheatsDownloadedSuccessfully_MSG + Sėkmingai atsisiuntėte sukčiavimus šios žaidimo versijos iš pasirinktos saugyklos. Galite pabandyti atsisiųsti iš kitos saugyklos, jei ji yra prieinama, taip pat bus galima ją naudoti pasirinkus failą iš sąrašo. + + + + Failed to save: + Nepavyko išsaugoti: + + + + Failed to download: + Nepavyko atsisiųsti: + + + + Download Complete + Atsisiuntimas baigtas + + + + DownloadComplete_MSG + Pataisos sėkmingai atsisiųstos! Visos pataisos visiems žaidimams buvo atsisiųstos, nebėra reikalo jas atsisiųsti atskirai kiekvienam žaidimui, kaip tai vyksta su sukčiavimais. + + + + Failed to parse JSON data from HTML. + Nepavyko išanalizuoti JSON duomenų iš HTML. + + + + Failed to retrieve HTML page. + Nepavyko gauti HTML puslapio. + + + + Failed to open file: + Nepavyko atidaryti failo: + + + + XML ERROR: + XML KLAIDA: + + + + Failed to open files.json for writing + Nepavyko atidaryti files.json rašymui + + + + Author: + Autorius: + + + + Directory does not exist: + Katalogas neegzistuoja: + + + + Failed to open files.json for reading. + Nepavyko atidaryti files.json skaitymui. + + + + Name: + Pavadinimas: + + + \ No newline at end of file diff --git a/src/qt_gui/translations/nb.ts b/src/qt_gui/translations/nb.ts new file mode 100644 index 000000000..76cad45b2 --- /dev/null +++ b/src/qt_gui/translations/nb.ts @@ -0,0 +1,902 @@ + + + + AboutDialog + + + About shadPS4 + About shadPS4 + + + + shadPS4 + shadPS4 + + + + shadPS4 is an experimental open-source emulator for the PlayStation 4. + shadPS4 is an experimental open-source emulator for the PlayStation 4. + + + + This software should not be used to play games you have not legally obtained. + This software should not be used to play games you have not legally obtained. + + + + ElfViewer + + + Open Folder + Open Folder + + + + GameInfoClass + + + Loading game list, please wait :3 + Loading game list, please wait :3 + + + + Cancel + Cancel + + + + Loading... + Loading... + + + + GameInstallDialog + + + shadPS4 - Choose directory + shadPS4 - Choose directory + + + + Directory to install games + Directory to install games + + + + Browse + Browse + + + + Error + Error + + + + The value for location to install games is not valid. + The value for location to install games is not valid. + + + + GuiContextMenus + + + Create Shortcut + Create Shortcut + + + + Open Game Folder + Open Game Folder + + + + Cheats / Patches + Juks / Oppdateringer + + + + SFO Viewer + SFO Viewer + + + + Trophy Viewer + Trophy Viewer + + + + Copy info + Copy info + + + + Copy Name + Copy Name + + + + Copy Serial + Copy Serial + + + + Copy All + Copy All + + + + Shortcut creation + Shortcut creation + + + + Shortcut created successfully!\n %1 + Shortcut created successfully!\n %1 + + + + Error + Error + + + + Error creating shortcut!\n %1 + Error creating shortcut!\n %1 + + + + Install PKG + Install PKG + + + + MainWindow + + + Open/Add Elf Folder + Open/Add Elf Folder + + + + Install Packages (PKG) + Install Packages (PKG) + + + + Boot Game + Boot Game + + + + About shadPS4 + About shadPS4 + + + + Configure... + Configure... + + + + Install application from a .pkg file + Install application from a .pkg file + + + + Recent Games + Recent Games + + + + Exit + Exit + + + + Exit shadPS4 + Exit shadPS4 + + + + Exit the application. + Exit the application. + + + + Show Game List + Show Game List + + + + Game List Refresh + Game List Refresh + + + + Tiny + Tiny + + + + Small + Small + + + + Medium + Medium + + + + Large + Large + + + + List View + List View + + + + Grid View + Grid View + + + + Elf Viewer + Elf Viewer + + + + Game Install Directory + Game Install Directory + + + + Download Cheats/Patches + Last ned Juks / Oppdateringer + + + + Dump Game List + Dump Game List + + + + PKG Viewer + PKG Viewer + + + + Search... + Search... + + + + File + File + + + + View + View + + + + Game List Icons + Game List Icons + + + + Game List Mode + Game List Mode + + + + Settings + Settings + + + + Utils + Utils + + + + Themes + Themes + + + + About + About + + + + Dark + Dark + + + + Light + Light + + + + Green + Green + + + + Blue + Blue + + + + Violet + Violet + + + + toolBar + toolBar + + + + PKGViewer + + + Open Folder + Open Folder + + + + TrophyViewer + + + Trophy Viewer + Trophy Viewer + + + + SettingsDialog + + + Settings + Settings + + + + General + General + + + + System + System + + + + Console Language + Console Language + + + + Emulator Language + Emulator Language + + + + Emulator + Emulator + + + + Enable Fullscreen + Enable Fullscreen + + + + Show Splash + Show Splash + + + + Is PS4 Pro + Is PS4 Pro + + + + Username + Username + + + + Logger + Logger + + + + Log Type + Log Type + + + + Log Filter + Log Filter + + + + Graphics + Graphics + + + + Graphics Device + Graphics Device + + + + Width + Width + + + + Height + Height + + + + Vblank Divider + Vblank Divider + + + + Advanced + Advanced + + + + Enable Shaders Dumping + Enable Shaders Dumping + + + + Enable NULL GPU + Enable NULL GPU + + + + Enable PM4 Dumping + Enable PM4 Dumping + + + + Debug + Debug + + + + Enable Debug Dumping + Enable Debug Dumping + + + + Enable Vulkan Validation Layers + Enable Vulkan Validation Layers + + + + Enable Vulkan Synchronization Validation + Enable Vulkan Synchronization Validation + + + + Enable RenderDoc Debugging + Enable RenderDoc Debugging + + + + MainWindow + + + * Unsupported Vulkan Version + * Ikke støttet Vulkan-versjon + + + + Download Cheats For All Installed Games + Last ned jukser for alle installerte spill + + + + Download Patches For All Games + Last ned oppdateringer for alle spill + + + + Download Complete + Nedlasting fullført + + + + You have downloaded cheats for all the games you have installed. + Du har lastet ned jukser for alle spillene du har installert. + + + + Patches Downloaded Successfully! + Oppdateringer lastet ned vellykket! + + + + All Patches available for all games have been downloaded. + Alle oppdateringer tilgjengelige for alle spillene har blitt lastet ned. + + + + Games: + Spill: + + + + PKG File (*.PKG) + PKG-fil (*.PKG) + + + + ELF files (*.bin *.elf *.oelf) + ELF-filer (*.bin *.elf *.oelf) + + + + Game Boot + Spilloppstart + + + + Only one file can be selected! + Kun én fil kan velges! + + + + PKG Extraction + PKG-ekstraksjon + + + + Patch detected! + Oppdatering oppdaget! + + + + PKG and Game versions match: + PKG- og spillversjoner stemmer overens: + + + + Would you like to overwrite? + Ønsker du å overskrive? + + + + PKG Version %1 is older than installed version: + PKG-versjon %1 er eldre enn installert versjon: + + + + Game is installed: + Spillet er installert: + + + + Would you like to install Patch: + Ønsker du å installere oppdateringen: + + + + DLC Installation + DLC-installasjon + + + + Would you like to install DLC: %1? + Ønsker du å installere DLC: %1? + + + + DLC already installed: + DLC allerede installert: + + + + Game already installed + Spillet er allerede installert + + + + PKG is a patch, please install the game first! + PKG er en oppdatering, vennligst installer spillet først! + + + + PKG ERROR + PKG FEIL + + + + Extracting PKG %1/%2 + Ekstraherer PKG %1/%2 + + + + Extraction Finished + Ekstrahering fullført + + + + Game successfully installed at %1 + Spillet ble installert vellykket på %1 + + + + File doesn't appear to be a valid PKG file + Fil ser ikke ut til å være en gyldig PKG-fil + + + + CheatsPatches + + + Cheats / Patches + Jukser / Oppdateringer + + + + defaultTextEdit_MSG + Cheats/Patches er eksperimentelle.\nBruk med forsiktighet.\n\nLast ned cheats individuelt ved å velge depotet og klikke på nedlastingsknappen.\nPå fanen Patches kan du laste ned alle patches samtidig, velge hvilke du ønsker å bruke, og lagre valget ditt.\n\nSiden vi ikke utvikler Cheats/Patches,\nvær vennlig å rapportere problemer til cheat-utvikleren.\n\nHar du laget en ny cheat? Besøk:\nhttps://github.com/shadps4-emu/ps4_cheats + + + + No Image Available + Ingen bilde tilgjengelig + + + + Serial: + Serienummer: + + + + Version: + Versjon: + + + + Size: + Størrelse: + + + + Select Cheat File: + Velg juksfil: + + + + Repository: + Depot: + + + + Download Cheats + Last ned jukser + + + + Delete File + Slett fil + + + + No files selected. + Ingen filer valgt. + + + + You can delete the cheats you don't want after downloading them. + Du kan slette jukser du ikke ønsker etter å ha lastet dem ned. + + + + Do you want to delete the selected file?\n%1 + Ønsker du å slette den valgte filen?\n%1 + + + + Select Patch File: + Velg oppdateringsfil: + + + + Download Patches + Last ned oppdateringer + + + + Save + Lagre + + + + Cheats + Jukser + + + + Patches + Oppdateringer + + + + Error + Feil + + + + No patch selected. + Ingen oppdatering valgt. + + + + Unable to open files.json for reading. + Kan ikke åpne files.json for lesing. + + + + No patch file found for the current serial. + Ingen oppdateringsfil funnet for det aktuelle serienummeret. + + + + Unable to open the file for reading. + Kan ikke åpne filen for lesing. + + + + Unable to open the file for writing. + Kan ikke åpne filen for skriving. + + + + Failed to parse XML: + Feil ved parsing av XML: + + + + Success + Vellykket + + + + Options saved successfully. + Alternativer lagret vellykket. + + + + Invalid Source + Ugyldig kilde + + + + The selected source is invalid. + Den valgte kilden er ugyldig. + + + + File Exists + Fil eksisterer + + + + File already exists. Do you want to replace it? + Fil eksisterer allerede. Ønsker du å erstatte den? + + + + Failed to save file: + Kunne ikke lagre fil: + + + + Failed to download file: + Kunne ikke laste ned fil: + + + + Cheats Not Found + Jukser ikke funnet + + + + CheatsNotFound_MSG + Ingen jukser funnet for dette spillet i denne versjonen av det valgte depotet,prøv et annet depot eller en annen versjon av spillet. + + + + Cheats Downloaded Successfully + Jukser lastet ned vellykket + + + + CheatsDownloadedSuccessfully_MSG + Du har lastet ned jukser vellykket for denne versjonen av spillet fra det valgte depotet. Du kan prøve å laste ned fra et annet depot, hvis det er tilgjengelig, vil det også være mulig å bruke det ved å velge filen fra listen. + + + + Failed to save: + Kunne ikke lagre: + + + + Failed to download: + Kunne ikke laste ned: + + + + Download Complete + Nedlasting fullført + + + + DownloadComplete_MSG + Oppdateringer lastet ned vellykket! Alle oppdateringer tilgjengelige for alle spill har blitt lastet ned, det er ikke nødvendig å laste dem ned individuelt for hvert spill som skjer med jukser. + + + + Failed to parse JSON data from HTML. + Kunne ikke analysere JSON-data fra HTML. + + + + Failed to retrieve HTML page. + Kunne ikke hente HTML-side. + + + + Failed to open file: + Kunne ikke åpne fil: + + + + XML ERROR: + XML FEIL: + + + + Failed to open files.json for writing + Kunne ikke åpne files.json for skriving + + + + Author: + Forfatter: + + + + Directory does not exist: + Direktory eksisterer ikke: + + + + Failed to open files.json for reading. + Kunne ikke åpne files.json for lesing. + + + + Name: + Navn: + + + \ No newline at end of file diff --git a/src/qt_gui/translations/nl.ts b/src/qt_gui/translations/nl.ts new file mode 100644 index 000000000..b00460479 --- /dev/null +++ b/src/qt_gui/translations/nl.ts @@ -0,0 +1,902 @@ + + + + AboutDialog + + + About shadPS4 + About shadPS4 + + + + shadPS4 + shadPS4 + + + + shadPS4 is an experimental open-source emulator for the PlayStation 4. + shadPS4 is an experimental open-source emulator for the PlayStation 4. + + + + This software should not be used to play games you have not legally obtained. + This software should not be used to play games you have not legally obtained. + + + + ElfViewer + + + Open Folder + Open Folder + + + + GameInfoClass + + + Loading game list, please wait :3 + Loading game list, please wait :3 + + + + Cancel + Cancel + + + + Loading... + Loading... + + + + GameInstallDialog + + + shadPS4 - Choose directory + shadPS4 - Choose directory + + + + Directory to install games + Directory to install games + + + + Browse + Browse + + + + Error + Error + + + + The value for location to install games is not valid. + The value for location to install games is not valid. + + + + GuiContextMenus + + + Create Shortcut + Create Shortcut + + + + Open Game Folder + Open Game Folder + + + + Cheats / Patches + Cheats / Patches + + + + SFO Viewer + SFO Viewer + + + + Trophy Viewer + Trophy Viewer + + + + Copy info + Copy info + + + + Copy Name + Copy Name + + + + Copy Serial + Copy Serial + + + + Copy All + Copy All + + + + Shortcut creation + Shortcut creation + + + + Shortcut created successfully!\n %1 + Shortcut created successfully!\n %1 + + + + Error + Error + + + + Error creating shortcut!\n %1 + Error creating shortcut!\n %1 + + + + Install PKG + Install PKG + + + + MainWindow + + + Open/Add Elf Folder + Open/Add Elf Folder + + + + Install Packages (PKG) + Install Packages (PKG) + + + + Boot Game + Boot Game + + + + About shadPS4 + About shadPS4 + + + + Configure... + Configure... + + + + Install application from a .pkg file + Install application from a .pkg file + + + + Recent Games + Recent Games + + + + Exit + Exit + + + + Exit shadPS4 + Exit shadPS4 + + + + Exit the application. + Exit the application. + + + + Show Game List + Show Game List + + + + Game List Refresh + Game List Refresh + + + + Tiny + Tiny + + + + Small + Small + + + + Medium + Medium + + + + Large + Large + + + + List View + List View + + + + Grid View + Grid View + + + + Elf Viewer + Elf Viewer + + + + Game Install Directory + Game Install Directory + + + + Download Cheats/Patches + Download Cheats/Patches + + + + Dump Game List + Dump Game List + + + + PKG Viewer + PKG Viewer + + + + Search... + Search... + + + + File + File + + + + View + View + + + + Game List Icons + Game List Icons + + + + Game List Mode + Game List Mode + + + + Settings + Settings + + + + Utils + Utils + + + + Themes + Themes + + + + About + About + + + + Dark + Dark + + + + Light + Light + + + + Green + Green + + + + Blue + Blue + + + + Violet + Violet + + + + toolBar + toolBar + + + + PKGViewer + + + Open Folder + Open Folder + + + + TrophyViewer + + + Trophy Viewer + Trophy Viewer + + + + SettingsDialog + + + Settings + Settings + + + + General + General + + + + System + System + + + + Console Language + Console Language + + + + Emulator Language + Emulator Language + + + + Emulator + Emulator + + + + Enable Fullscreen + Enable Fullscreen + + + + Show Splash + Show Splash + + + + Is PS4 Pro + Is PS4 Pro + + + + Username + Username + + + + Logger + Logger + + + + Log Type + Log Type + + + + Log Filter + Log Filter + + + + Graphics + Graphics + + + + Graphics Device + Graphics Device + + + + Width + Width + + + + Height + Height + + + + Vblank Divider + Vblank Divider + + + + Advanced + Advanced + + + + Enable Shaders Dumping + Enable Shaders Dumping + + + + Enable NULL GPU + Enable NULL GPU + + + + Enable PM4 Dumping + Enable PM4 Dumping + + + + Debug + Debug + + + + Enable Debug Dumping + Enable Debug Dumping + + + + Enable Vulkan Validation Layers + Enable Vulkan Validation Layers + + + + Enable Vulkan Synchronization Validation + Enable Vulkan Synchronization Validation + + + + Enable RenderDoc Debugging + Enable RenderDoc Debugging + + + + MainWindow + + + * Unsupported Vulkan Version + * Niet ondersteunde Vulkan-versie + + + + Download Cheats For All Installed Games + Download cheats voor alle geïnstalleerde spellen + + + + Download Patches For All Games + Download patches voor alle spellen + + + + Download Complete + Download voltooid + + + + You have downloaded cheats for all the games you have installed. + Je hebt cheats gedownload voor alle spellen die je hebt geïnstalleerd. + + + + Patches Downloaded Successfully! + Patches succesvol gedownload! + + + + All Patches available for all games have been downloaded. + Alle patches voor alle spellen zijn gedownload. + + + + Games: + Spellen: + + + + PKG File (*.PKG) + PKG-bestand (*.PKG) + + + + ELF files (*.bin *.elf *.oelf) + ELF-bestanden (*.bin *.elf *.oelf) + + + + Game Boot + Spelopstart + + + + Only one file can be selected! + Je kunt slechts één bestand selecteren! + + + + PKG Extraction + PKG-extractie + + + + Patch detected! + Patch gedetecteerd! + + + + PKG and Game versions match: + PKG- en gameversies komen overeen: + + + + Would you like to overwrite? + Wilt u overschrijven? + + + + PKG Version %1 is older than installed version: + PKG-versie %1 is ouder dan de geïnstalleerde versie: + + + + Game is installed: + Game is geïnstalleerd: + + + + Would you like to install Patch: + Wilt u de patch installeren: + + + + DLC Installation + DLC-installatie + + + + Would you like to install DLC: %1? + Wilt u DLC installeren: %1? + + + + DLC already installed: + DLC al geïnstalleerd: + + + + Game already installed + Game al geïnstalleerd + + + + PKG is a patch, please install the game first! + PKG is een patch, installeer eerst het spel! + + + + PKG ERROR + PKG FOUT + + + + Extracting PKG %1/%2 + PKG %1/%2 aan het extraheren + + + + Extraction Finished + Extractie voltooid + + + + Game successfully installed at %1 + Spel succesvol geïnstalleerd op %1 + + + + File doesn't appear to be a valid PKG file + Het bestand lijkt geen geldig PKG-bestand te zijn + + + + CheatsPatches + + + Cheats / Patches + Cheats / Patches + + + + defaultTextEdit_MSG + Cheats/Patches zijn experimenteel.\nGebruik met voorzichtigheid.\n\nDownload cheats afzonderlijk door het repository te selecteren en op de downloadknop te klikken.\nOp het tabblad Patches kun je alle patches tegelijk downloaden, kiezen welke je wilt gebruiken en je selectie opslaan.\n\nAangezien wij de Cheats/Patches niet ontwikkelen,\nmeld problemen bij de auteur van de cheat.\n\nHeb je een nieuwe cheat gemaakt? Bezoek:\nhttps://github.com/shadps4-emu/ps4_cheats + + + + No Image Available + Geen afbeelding beschikbaar + + + + Serial: + Serie: + + + + Version: + Versie: + + + + Size: + Grootte: + + + + Select Cheat File: + Selecteer cheatbestand: + + + + Repository: + Repository: + + + + Download Cheats + Download cheats + + + + Delete File + Bestand verwijderen + + + + No files selected. + Geen bestanden geselecteerd. + + + + You can delete the cheats you don't want after downloading them. + Je kunt de cheats die je niet wilt verwijderen nadat je ze hebt gedownload. + + + + Do you want to delete the selected file?\n%1 + Wil je het geselecteerde bestand verwijderen?\n%1 + + + + Select Patch File: + Selecteer patchbestand: + + + + Download Patches + Download patches + + + + Save + Opslaan + + + + Cheats + Cheats + + + + Patches + Patches + + + + Error + Fout + + + + No patch selected. + Geen patch geselecteerd. + + + + Unable to open files.json for reading. + Kan files.json niet openen voor lezen. + + + + No patch file found for the current serial. + Geen patchbestand gevonden voor het huidige serienummer. + + + + Unable to open the file for reading. + Kan het bestand niet openen voor lezen. + + + + Unable to open the file for writing. + Kan het bestand niet openen voor schrijven. + + + + Failed to parse XML: + XML parsing mislukt: + + + + Success + Succes + + + + Options saved successfully. + Opties succesvol opgeslagen. + + + + Invalid Source + Ongeldige bron + + + + The selected source is invalid. + De geselecteerde bron is ongeldig. + + + + File Exists + Bestand bestaat + + + + File already exists. Do you want to replace it? + Bestand bestaat al. Wil je het vervangen? + + + + Failed to save file: + Kan bestand niet opslaan: + + + + Failed to download file: + Kan bestand niet downloaden: + + + + Cheats Not Found + Cheats niet gevonden + + + + CheatsNotFound_MSG + Geen cheats gevonden voor deze game in deze versie van de geselecteerde repository.Probeer een andere repository of een andere versie van het spel. + + + + Cheats Downloaded Successfully + Cheats succesvol gedownload + + + + CheatsDownloadedSuccessfully_MSG + Je hebt cheats succesvol gedownload voor deze versie van het spel uit de geselecteerde repository. Je kunt proberen te downloaden van een andere repository. Als deze beschikbaar is, kan het ook worden gebruikt door het bestand uit de lijst te selecteren. + + + + Failed to save: + Opslaan mislukt: + + + + Failed to download: + Downloaden mislukt: + + + + Download Complete + Download voltooid + + + + DownloadComplete_MSG + Patches succesvol gedownload! Alle beschikbare patches voor alle spellen zijn gedownload. Het is niet nodig om ze afzonderlijk te downloaden voor elk spel dat cheats heeft. + + + + Failed to parse JSON data from HTML. + Kan JSON-gegevens uit HTML niet parseren. + + + + Failed to retrieve HTML page. + Kan HTML-pagina niet ophalen. + + + + Failed to open file: + Kan bestand niet openen: + + + + XML ERROR: + XML FOUT: + + + + Failed to open files.json for writing + Kan files.json niet openen voor schrijven + + + + Author: + Auteur: + + + + Directory does not exist: + Map bestaat niet: + + + + Failed to open files.json for reading. + Kan files.json niet openen voor lezen. + + + + Name: + Naam: + + + \ No newline at end of file diff --git a/src/qt_gui/translations/pl_PL.ts b/src/qt_gui/translations/pl_PL.ts new file mode 100644 index 000000000..deaab42fb --- /dev/null +++ b/src/qt_gui/translations/pl_PL.ts @@ -0,0 +1,902 @@ + + + + AboutDialog + + + About shadPS4 + O programie + + + + shadPS4 + shadPS4 + + + + shadPS4 is an experimental open-source emulator for the PlayStation 4. + shadPS4 to eksperymentalny otwartoźródłowy emulator konsoli PlayStation 4. + + + + This software should not be used to play games you have not legally obtained. + To oprogramowanie nie służy do grania w gry pochodzące z nielegalnego źródła. + + + + ElfViewer + + + Open Folder + Otwórz folder + + + + GameInfoClass + + + Loading game list, please wait :3 + Ładowanie listy gier, proszę poczekaj :3 + + + + Cancel + Anuluj + + + + Loading... + Ładowanie... + + + + GameInstallDialog + + + shadPS4 - Choose directory + shadPS4 - Wybierz katalog + + + + Directory to install games + Katalog do instalacji gier + + + + Browse + Przeglądaj + + + + Error + Błąd + + + + The value for location to install games is not valid. + Podana ścieżka do instalacji gier nie jest prawidłowa. + + + + GuiContextMenus + + + Create Shortcut + Utwórz skrót + + + + Open Game Folder + Otwórz katalog gry + + + + Cheats / Patches + Kody / poprawki + + + + SFO Viewer + Menedżer plików SFO + + + + Trophy Viewer + Menedżer trofeów + + + + Copy info + Kopiuj informacje + + + + Copy Name + Kopiuj nazwę + + + + Copy Serial + Kopiuj numer seryjny + + + + Copy All + Kopiuj wszystko + + + + Shortcut creation + Tworzenie skrótu + + + + Shortcut created successfully!\n %1 + Utworzenie skrótu zakończone pomyślnie!\n %1 + + + + Error + Błąd + + + + Error creating shortcut!\n %1 + Utworzenie skrótu zakończone niepowodzeniem!\n %1 + + + + Install PKG + Zainstaluj PKG + + + + MainWindow + + + Open/Add Elf Folder + Otwórz/Dodaj folder Elf + + + + Install Packages (PKG) + Zainstaluj paczkę (PKG) + + + + Boot Game + Uruchom grę + + + + About shadPS4 + O programie + + + + Configure... + Konfiguruj... + + + + Install application from a .pkg file + Zainstaluj aplikacje z pliku .pkg + + + + Recent Games + Ostatnie gry + + + + Exit + Wyjdź + + + + Exit shadPS4 + Wyjdź z shadPS4 + + + + Exit the application. + Wyjdź z aplikacji. + + + + Show Game List + Pokaż listę gier + + + + Game List Refresh + Odśwież listę gier + + + + Tiny + Malutkie + + + + Small + Małe + + + + Medium + Średnie + + + + Large + Wielkie + + + + List View + Widok listy + + + + Grid View + Widok siatki + + + + Elf Viewer + Menedżer plików ELF + + + + Game Install Directory + Katalog zainstalowanych gry + + + + Download Cheats/Patches + Pobierz kody / poprawki + + + + Dump Game List + Zgraj listę gier + + + + PKG Viewer + Menedżer plików PKG + + + + Search... + Szukaj... + + + + File + Plik + + + + View + Widok + + + + Game List Icons + Ikony w widoku listy + + + + Game List Mode + Tryb listy gier + + + + Settings + Ustawienia + + + + Utils + Narzędzia + + + + Themes + Motywy + + + + About + O programie + + + + Dark + Ciemny + + + + Light + Jasny + + + + Green + Zielony + + + + Blue + Niebieski + + + + Violet + Fioletowy + + + + toolBar + Pasek narzędzi + + + + PKGViewer + + + Open Folder + Otwórz folder + + + + TrophyViewer + + + Trophy Viewer + Menedżer trofeów + + + + SettingsDialog + + + Settings + Ustawienia + + + + General + Ogólne + + + + System + System + + + + Console Language + Język konsoli + + + + Emulator Language + Język emulatora + + + + Emulator + Emulator + + + + Enable Fullscreen + Włącz pełny ekran + + + + Show Splash + Pokaż ekran powitania + + + + Is PS4 Pro + Emulacja PS4 Pro + + + + Username + Nazwa użytkownika + + + + Logger + Dziennik zdarzeń + + + + Log Type + Typ dziennika + + + + Log Filter + Filtrowanie dziennika + + + + Graphics + Grafika + + + + Graphics Device + Karta graficzna + + + + Width + Szerokość + + + + Height + Wysokość + + + + Vblank Divider + Dzielnik przerwy pionowej (Vblank) + + + + Advanced + Zaawansowane + + + + Enable Shaders Dumping + Włącz zgrywanie cieni + + + + Enable NULL GPU + Wyłącz kartę graficzną + + + + Enable PM4 Dumping + Włącz zgrywanie PM4 + + + + Debug + Debugowanie + + + + Enable Debug Dumping + Włącz zgrywanie debugowania + + + + Enable Vulkan Validation Layers + Włącz warstwy walidacji Vulkan + + + + Enable Vulkan Synchronization Validation + Włącz walidację synchronizacji Vulkan + + + + Enable RenderDoc Debugging + Włącz debugowanie RenderDoc + + + + MainWindow + + + * Unsupported Vulkan Version + * Nieobsługiwana wersja Vulkan + + + + Download Cheats For All Installed Games + Pobierz kody do wszystkich zainstalowanych gier + + + + Download Patches For All Games + Pobierz poprawki do wszystkich gier + + + + Download Complete + Pobieranie zakończone + + + + You have downloaded cheats for all the games you have installed. + Pobrałeś kody do wszystkich zainstalowanych gier. + + + + Patches Downloaded Successfully! + Poprawki pobrane pomyślnie! + + + + All Patches available for all games have been downloaded. + Wszystkie poprawki dostępne dla wszystkich gier zostały pobrane. + + + + Games: + Gry: + + + + PKG File (*.PKG) + Plik PKG (*.PKG) + + + + ELF files (*.bin *.elf *.oelf) + Pliki ELF (*.bin *.elf *.oelf) + + + + Game Boot + Uruchomienie gry + + + + Only one file can be selected! + Można wybrać tylko jeden plik! + + + + PKG Extraction + Wypakowywanie PKG + + + + Patch detected! + Wykryto łatkę! + + + + PKG and Game versions match: + Wersje PKG i gry są zgodne: + + + + Would you like to overwrite? + Czy chcesz nadpisać? + + + + PKG Version %1 is older than installed version: + Wersja PKG %1 jest starsza niż zainstalowana wersja: + + + + Game is installed: + Gra jest zainstalowana: + + + + Would you like to install Patch: + Czy chcesz zainstalować łatkę: + + + + DLC Installation + Instalacja DLC + + + + Would you like to install DLC: %1? + Czy chcesz zainstalować DLC: %1? + + + + DLC already installed: + DLC już zainstalowane: + + + + Game already installed + Gra już zainstalowana + + + + PKG is a patch, please install the game first! + PKG jest poprawką, proszę najpierw zainstalować grę! + + + + PKG ERROR + BŁĄD PKG + + + + Extracting PKG %1/%2 + Wypakowywanie PKG %1/%2 + + + + Extraction Finished + Wypakowywanie zakończone + + + + Game successfully installed at %1 + Gra pomyślnie zainstalowana w %1 + + + + File doesn't appear to be a valid PKG file + Plik nie wydaje się być prawidłowym plikiem PKG + + + + CheatsPatches + + + Cheats / Patches + Kody / poprawki + + + + defaultTextEdit_MSG + Cheaty/Patche są eksperymentalne.\nUżywaj ich ostrożnie.\n\nPobierz cheaty pojedynczo, wybierając repozytorium i klikając przycisk pobierania.\nNa zakładce Patches możesz pobrać wszystkie patche jednocześnie, wybrać, które chcesz używać, i zapisać wybór.\n\nPonieważ nie rozwijamy Cheats/Patches,\nproszę zgłosić problemy do autora cheatu.\n\nStworzyłeś nowy cheat? Odwiedź:\nhttps://github.com/shadps4-emu/ps4_cheats + + + + No Image Available + Brak dostępnego obrazu + + + + Serial: + Numer seryjny: + + + + Version: + Wersja: + + + + Size: + Rozmiar: + + + + Select Cheat File: + Wybierz plik kodu: + + + + Repository: + Repozytorium: + + + + Download Cheats + Pobierz kody + + + + Remove Old Files + Usuń stare pliki + + + + Do you want to delete the files after downloading them? + Czy chcesz usunąć pliki po ich pobraniu? + + + + Do you want to delete the files after downloading them?\n%1 + Czy chcesz usunąć pliki po ich pobraniu?\n%1 + + + + Do you want to delete the selected file?\n%1 + Czy chcesz usunąć wybrany plik?\n%1 + + + + Select Patch File: + Wybierz plik poprawki: + + + + Download Patches + Pobierz poprawki + + + + Save + Zapisz + + + + Cheats + Kody + + + + Patches + Poprawki + + + + Error + Błąd + + + + No patch selected. + Nie wybrano poprawki. + + + + Unable to open files.json for reading. + Nie można otworzyć pliku files.json do odczytu. + + + + No patch file found for the current serial. + Nie znaleziono pliku poprawki dla bieżącego numeru seryjnego. + + + + Unable to open the file for reading. + Nie można otworzyć pliku do odczytu. + + + + Unable to open the file for writing. + Nie można otworzyć pliku do zapisu. + + + + Failed to parse XML: + Nie udało się przeanalizować XML: + + + + Success + Sukces + + + + Options saved successfully. + Opcje zostały pomyślnie zapisane. + + + + Invalid Source + Nieprawidłowe źródło + + + + The selected source is invalid. + Wybrane źródło jest nieprawidłowe. + + + + File Exists + Plik istnieje + + + + File already exists. Do you want to replace it? + Plik już istnieje. Czy chcesz go zastąpić? + + + + Failed to save file: + Nie udało się zapisać pliku: + + + + Failed to download file: + Nie udało się pobrać pliku: + + + + Cheats Not Found + Nie znaleziono kodów + + + + CheatsNotFound_MSG + Nie znaleziono kodów do tej gry w tej wersji wybranego repozytorium. Spróbuj innego repozytorium lub innej wersji gry. + + + + Cheats Downloaded Successfully + Kody pobrane pomyślnie + + + + CheatsDownloadedSuccessfully_MSG + Pomyślnie pobrano kody dla tej wersji gry z wybranego repozytorium. Możesz spróbować pobrać z innego repozytorium. Jeśli jest dostępne, możesz również użyć go, wybierając plik z listy. + + + + Failed to save: + Nie udało się zapisać: + + + + Failed to download: + Nie udało się pobrać: + + + + Download Complete + Pobieranie zakończone + + + + DownloadComplete_MSG + Poprawki zostały pomyślnie pobrane! Wszystkie dostępne poprawki dla wszystkich gier zostały pobrane. Nie ma potrzeby pobierania ich osobno dla każdej gry, która ma kody. + + + + Failed to parse JSON data from HTML. + Nie udało się przeanalizować danych JSON z HTML. + + + + Failed to retrieve HTML page. + Nie udało się pobrać strony HTML. + + + + Failed to open file: + Nie udało się otworzyć pliku: + + + + XML ERROR: + BŁĄD XML: + + + + Failed to open files.json for writing + Nie udało się otworzyć pliku files.json do zapisu + + + + Author: + Autor: + + + + Directory does not exist: + Katalog nie istnieje: + + + + Directory does not exist: %1 + Katalog nie istnieje: %1 + + + + Failed to parse JSON: + Nie udało się przeanlizować JSON: + + + diff --git a/src/qt_gui/translations/pt_BR.ts b/src/qt_gui/translations/pt_BR.ts new file mode 100644 index 000000000..8b4538b9d --- /dev/null +++ b/src/qt_gui/translations/pt_BR.ts @@ -0,0 +1,902 @@ + + + + AboutDialog + + + About shadPS4 + Sobre o shadPS4 + + + + shadPS4 + shadPS4 + + + + shadPS4 is an experimental open-source emulator for the PlayStation 4. + shadPS4 é um emulador experimental de código-fonte aberto para o PlayStation 4. + + + + This software should not be used to play games you have not legally obtained. + Este software não deve ser usado para jogar jogos piratas. + + + + ElfViewer + + + Open Folder + Abrir Pasta + + + + GameInfoClass + + + Loading game list, please wait :3 + Carregando a lista de jogos, por favor aguarde :3 + + + + Cancel + Cancelar + + + + Loading... + Carregando... + + + + GameInstallDialog + + + shadPS4 - Choose directory + shadPS4 - Escolha o diretório + + + + Directory to install games + Diretório para instalar jogos + + + + Browse + Procurar + + + + Error + Erro + + + + The value for location to install games is not valid. + O diretório da instalação dos jogos não é válido. + + + + GuiContextMenus + + + Create Shortcut + Criar Atalho + + + + Open Game Folder + Abrir Pasta do Jogo + + + + Cheats / Patches + Cheats / Patches + + + + SFO Viewer + Visualizador de SFO + + + + Trophy Viewer + Visualizador de Troféu + + + + Copy info + Copiar informação + + + + Copy Name + Copiar Nome + + + + Copy Serial + Copiar Serial + + + + Copy All + Copiar Tudo + + + + Shortcut creation + Criação de atalho + + + + Shortcut created successfully!\n %1 + Atalho criado com sucesso!\n %1 + + + + Error + Erro + + + + Error creating shortcut!\n %1 + Erro ao criar atalho!\n %1 + + + + Install PKG + Instalar PKG + + + + MainWindow + + + Open/Add Elf Folder + Abrir/Adicionar pasta Elf + + + + Install Packages (PKG) + Instalar Pacotes (PKG) + + + + Boot Game + Iniciar Jogo + + + + About shadPS4 + Sobre o shadPS4 + + + + Configure... + Configurar... + + + + Install application from a .pkg file + Instalar aplicação de um arquivo .pkg + + + + Recent Games + Jogos Recentes + + + + Exit + Sair + + + + Exit shadPS4 + Sair do shadPS4 + + + + Exit the application. + Sair da aplicação. + + + + Show Game List + Mostrar Lista de Jogos + + + + Game List Refresh + Atualizar Lista de Jogos + + + + Tiny + Muito pequeno + + + + Small + Pequeno + + + + Medium + Médio + + + + Large + Grande + + + + List View + Visualizar em Lista + + + + Grid View + Visualizar em Grade + + + + Elf Viewer + Visualizador de Elf + + + + Game Install Directory + Diretório de Instalação de Jogos + + + + Download Cheats/Patches + Baixar Cheats/Patches + + + + Dump Game List + Dumpar Lista de Jogos + + + + PKG Viewer + Visualizador de PKG + + + + Search... + Pesquisar... + + + + File + Arquivo + + + + View + Ver + + + + Game List Icons + Ícones da Lista de Jogos + + + + Game List Mode + Modo da Lista de Jogos + + + + Settings + Configurações + + + + Utils + Utilitários + + + + Themes + Temas + + + + About + Sobre + + + + Dark + Escuro + + + + Light + Claro + + + + Green + Verde + + + + Blue + Azul + + + + Violet + Violeta + + + + toolBar + Barra de Ferramentas + + + + PKGViewer + + + Open Folder + Abrir Pasta + + + + TrophyViewer + + + Trophy Viewer + Visualizador de Troféu + + + + SettingsDialog + + + Settings + Configurações + + + + General + Geral + + + + System + Sistema + + + + Console Language + Idioma do Console + + + + Emulator Language + Idioma do Emulador + + + + Emulator + Emulador + + + + Enable Fullscreen + Ativar Tela Cheia + + + + Show Splash + Mostrar Splash Inicial + + + + Is PS4 Pro + Modo PS4 Pro + + + + Username + Nome de usuário + + + + Logger + Registro + + + + Log Type + Tipo de Registro + + + + Log Filter + Filtro do Registro + + + + Graphics + Gráficos + + + + Graphics Device + Placa de Vídeo + + + + Width + Largura + + + + Height + Altura + + + + Vblank Divider + Divisor Vblank + + + + Advanced + Avançado + + + + Enable Shaders Dumping + Ativar Dumping de Shaders + + + + Enable NULL GPU + Ativar GPU NULA + + + + Enable PM4 Dumping + Ativar Dumping de PM4 + + + + Debug + Depuração + + + + Enable Debug Dumping + Ativar Depuração de Dumping + + + + Enable Vulkan Validation Layers + Ativar Camadas de Validação do Vulkan + + + + Enable Vulkan Synchronization Validation + Ativar Validação de Sincronização do Vulkan + + + + Enable RenderDoc Debugging + Ativar Depuração por RenderDoc + + + + MainWindow + + + * Unsupported Vulkan Version + * Versão Vulkan não suportada + + + + Download Cheats For All Installed Games + Baixar Cheats para Todos os Jogos Instalados + + + + Download Patches For All Games + Baixar Patches para Todos os Jogos + + + + Download Complete + Download Completo + + + + You have downloaded cheats for all the games you have installed. + Você baixou cheats para todos os jogos que instalou. + + + + Patches Downloaded Successfully! + Patches Baixados com Sucesso! + + + + All Patches available for all games have been downloaded. + Todos os patches disponíveis para todos os jogos foram baixados. + + + + Games: + Jogos: + + + + PKG File (*.PKG) + Arquivo PKG (*.PKG) + + + + ELF files (*.bin *.elf *.oelf) + Arquivos ELF (*.bin *.elf *.oelf) + + + + Game Boot + Inicialização do Jogo + + + + Only one file can be selected! + Apenas um arquivo pode ser selecionado! + + + + PKG Extraction + Extração de PKG + + + + Patch detected! + Atualização detectada! + + + + PKG and Game versions match: + As versões do PKG e do Jogo são igual: + + + + Would you like to overwrite? + Gostaria de substituir? + + + + PKG Version %1 is older than installed version: + Versão do PKG %1 é mais antiga do que a versão instalada: + + + + Game is installed: + Jogo instalado: + + + + Would you like to install Patch: + Você gostaria de instalar a atualização: + + + + DLC Installation + Instalação de DLC + + + + Would you like to install DLC: %1? + Você gostaria de instalar o DLC: %1? + + + + DLC already installed: + DLC já instalada: + + + + Game already installed + O jogo já está instalado: + + + + PKG is a patch, please install the game first! + O PKG é um patch, por favor, instale o jogo primeiro! + + + + PKG ERROR + ERRO de PKG + + + + Extracting PKG %1/%2 + Extraindo PKG %1/%2 + + + + Extraction Finished + Extração Concluída + + + + Game successfully installed at %1 + Jogo instalado com sucesso em %1 + + + + File doesn't appear to be a valid PKG file + O arquivo não parece ser um arquivo PKG válido + + + + CheatsPatches + + + Cheats / Patches + Cheats / Patches + + + + defaultTextEdit_MSG + Cheats/Patches são experimentais.\nUse com cautela.\n\nBaixe os cheats individualmente selecionando o repositório e clicando no botão de download.\nNa aba Patches, você pode baixar todos os Patches de uma vez, escolha qual deseja usar e salve a opção.\n\nComo não desenvolvemos os Cheats/Patches,\npor favor, reporte problemas relacionados ao autor do cheat.\n\nCriou um novo cheat? Visite:\nhttps://github.com/shadps4-emu/ps4_cheats + + + + No Image Available + Imagem Não Disponível + + + + Serial: + Serial: + + + + Version: + Versão: + + + + Size: + Tamanho: + + + + Select Cheat File: + Selecione o Arquivo de Cheat: + + + + Repository: + Repositório: + + + + Download Cheats + Baixar Cheats + + + + Delete File + Excluir Arquivo + + + + No files selected. + Nenhum arquivo selecionado. + + + + You can delete the cheats you don't want after downloading them. + Você pode excluir os cheats que não deseja após baixá-las. + + + + Do you want to delete the selected file?\n%1 + Deseja excluir o arquivo selecionado?\n%1 + + + + Select Patch File: + Selecione o Arquivo de Patch: + + + + Download Patches + Baixar Patches + + + + Save + Salvar + + + + Cheats + Cheats + + + + Patches + Patches + + + + Error + Erro + + + + No patch selected. + Nenhum patch selecionado. + + + + Unable to open files.json for reading. + Não foi possível abrir files.json para leitura. + + + + No patch file found for the current serial. + Nenhum arquivo de patch encontrado para o serial atual. + + + + Unable to open the file for reading. + Não foi possível abrir o arquivo para leitura. + + + + Unable to open the file for writing. + Não foi possível abrir o arquivo para gravação. + + + + Failed to parse XML: + Falha ao analisar XML: + + + + Success + Sucesso + + + + Options saved successfully. + Opções salvas com sucesso. + + + + Invalid Source + Fonte Inválida + + + + The selected source is invalid. + A fonte selecionada é inválida. + + + + File Exists + Arquivo Existe + + + + File already exists. Do you want to replace it? + O arquivo já existe. Deseja substituí-lo? + + + + Failed to save file: + Falha ao salvar o arquivo: + + + + Failed to download file: + Falha ao baixar o arquivo: + + + + Cheats Not Found + Cheats Não Encontrados + + + + CheatsNotFound_MSG + Nenhum cheat encontrado para este jogo nesta versão do repositório selecionado, tente outro repositório ou uma versão diferente do jogo. + + + + Cheats Downloaded Successfully + Cheats Baixados com Sucesso + + + + CheatsDownloadedSuccessfully_MSG + Você baixou os cheats com sucesso. Para esta versão do jogo a partir do repositório selecionado. Você pode tentar baixar de outro repositório, se estiver disponível, também será possível usá-lo selecionando o arquivo da lista. + + + + Failed to save: + Falha ao salvar: + + + + Failed to download: + Falha ao baixar: + + + + Download Complete + Download Completo + + + + DownloadComplete_MSG + Patches Baixados com Sucesso! Todos os patches disponíveis para todos os jogos foram baixados, não é necessário baixá-los individualmente para cada jogo como acontece com os Cheats. + + + + Failed to parse JSON data from HTML. + Falha ao analisar dados JSON do HTML. + + + + Failed to retrieve HTML page. + Falha ao recuperar a página HTML. + + + + Failed to open file: + Falha ao abrir o arquivo: + + + + XML ERROR: + ERRO de XML: + + + + Failed to open files.json for writing + Falha ao abrir files.json para gravação + + + + Author: + Autor: + + + + Directory does not exist: + O Diretório não existe: + + + + Failed to open files.json for reading. + Falha ao abrir files.json para leitura. + + + + Name: + Nome: + + + \ No newline at end of file diff --git a/src/qt_gui/translations/ro_RO.ts b/src/qt_gui/translations/ro_RO.ts new file mode 100644 index 000000000..8b2fda0c1 --- /dev/null +++ b/src/qt_gui/translations/ro_RO.ts @@ -0,0 +1,902 @@ + + + + AboutDialog + + + About shadPS4 + About shadPS4 + + + + shadPS4 + shadPS4 + + + + shadPS4 is an experimental open-source emulator for the PlayStation 4. + shadPS4 is an experimental open-source emulator for the PlayStation 4. + + + + This software should not be used to play games you have not legally obtained. + This software should not be used to play games you have not legally obtained. + + + + ElfViewer + + + Open Folder + Open Folder + + + + GameInfoClass + + + Loading game list, please wait :3 + Loading game list, please wait :3 + + + + Cancel + Cancel + + + + Loading... + Loading... + + + + GameInstallDialog + + + shadPS4 - Choose directory + shadPS4 - Choose directory + + + + Directory to install games + Directory to install games + + + + Browse + Browse + + + + Error + Error + + + + The value for location to install games is not valid. + The value for location to install games is not valid. + + + + GuiContextMenus + + + Create Shortcut + Create Shortcut + + + + Open Game Folder + Open Game Folder + + + + Trapaças / Patches + Coduri / Patch-uri + + + + SFO Viewer + SFO Viewer + + + + Trophy Viewer + Trophy Viewer + + + + Copy info + Copy info + + + + Copy Name + Copy Name + + + + Copy Serial + Copy Serial + + + + Copy All + Copy All + + + + Shortcut creation + Shortcut creation + + + + Shortcut created successfully!\n %1 + Shortcut created successfully!\n %1 + + + + Error + Error + + + + Error creating shortcut!\n %1 + Error creating shortcut!\n %1 + + + + Install PKG + Install PKG + + + + MainWindow + + + Open/Add Elf Folder + Open/Add Elf Folder + + + + Install Packages (PKG) + Install Packages (PKG) + + + + Boot Game + Boot Game + + + + About shadPS4 + About shadPS4 + + + + Configure... + Configure... + + + + Install application from a .pkg file + Install application from a .pkg file + + + + Recent Games + Recent Games + + + + Exit + Exit + + + + Exit shadPS4 + Exit shadPS4 + + + + Exit the application. + Exit the application. + + + + Show Game List + Show Game List + + + + Game List Refresh + Game List Refresh + + + + Tiny + Tiny + + + + Small + Small + + + + Medium + Medium + + + + Large + Large + + + + List View + List View + + + + Grid View + Grid View + + + + Elf Viewer + Elf Viewer + + + + Game Install Directory + Game Install Directory + + + + Download Cheats/Patches + Descarcă Coduri / Patch-uri + + + + Dump Game List + Dump Game List + + + + PKG Viewer + PKG Viewer + + + + Search... + Search... + + + + File + File + + + + View + View + + + + Game List Icons + Game List Icons + + + + Game List Mode + Game List Mode + + + + Settings + Settings + + + + Utils + Utils + + + + Themes + Themes + + + + About + About + + + + Dark + Dark + + + + Light + Light + + + + Green + Green + + + + Blue + Blue + + + + Violet + Violet + + + + toolBar + toolBar + + + + PKGViewer + + + Open Folder + Open Folder + + + + TrophyViewer + + + Trophy Viewer + Trophy Viewer + + + + SettingsDialog + + + Settings + Settings + + + + General + General + + + + System + System + + + + Console Language + Console Language + + + + Emulator Language + Emulator Language + + + + Emulator + Emulator + + + + Enable Fullscreen + Enable Fullscreen + + + + Show Splash + Show Splash + + + + Is PS4 Pro + Is PS4 Pro + + + + Username + Username + + + + Logger + Logger + + + + Log Type + Log Type + + + + Log Filter + Log Filter + + + + Graphics + Graphics + + + + Graphics Device + Graphics Device + + + + Width + Width + + + + Height + Height + + + + Vblank Divider + Vblank Divider + + + + Advanced + Advanced + + + + Enable Shaders Dumping + Enable Shaders Dumping + + + + Enable NULL GPU + Enable NULL GPU + + + + Enable PM4 Dumping + Enable PM4 Dumping + + + + Debug + Debug + + + + Enable Debug Dumping + Enable Debug Dumping + + + + Enable Vulkan Validation Layers + Enable Vulkan Validation Layers + + + + Enable Vulkan Synchronization Validation + Enable Vulkan Synchronization Validation + + + + Enable RenderDoc Debugging + Enable RenderDoc Debugging + + + + MainWindow + + + * Unsupported Vulkan Version + * Versiune Vulkan nesuportată + + + + Download Cheats For All Installed Games + Descarcă Cheats pentru toate jocurile instalate + + + + Download Patches For All Games + Descarcă Patches pentru toate jocurile + + + + Download Complete + Descărcare completă + + + + You have downloaded cheats for all the games you have installed. + Ai descărcat cheats pentru toate jocurile instalate. + + + + Patches Downloaded Successfully! + Patches descărcate cu succes! + + + + All Patches available for all games have been downloaded. + Toate Patches disponibile pentru toate jocurile au fost descărcate. + + + + Games: + Jocuri: + + + + PKG File (*.PKG) + Fișier PKG (*.PKG) + + + + ELF files (*.bin *.elf *.oelf) + Fișiere ELF (*.bin *.elf *.oelf) + + + + Game Boot + Boot Joc + + + + Only one file can be selected! + Numai un fișier poate fi selectat! + + + + PKG Extraction + Extracție PKG + + + + Patch detected! + Patch detectat! + + + + PKG and Game versions match: + Versiunile PKG și ale jocului sunt compatibile: + + + + Would you like to overwrite? + Doriți să suprascrieți? + + + + PKG Version %1 is older than installed version: + Versiunea PKG %1 este mai veche decât versiunea instalată: + + + + Game is installed: + Jocul este instalat: + + + + Would you like to install Patch: + Doriți să instalați patch-ul: + + + + DLC Installation + Instalare DLC + + + + Would you like to install DLC: %1? + Doriți să instalați DLC-ul: %1? + + + + DLC already installed: + DLC deja instalat: + + + + Game already installed + Jocul deja instalat + + + + PKG is a patch, please install the game first! + PKG este un patch, te rugăm să instalezi mai întâi jocul! + + + + PKG ERROR + EROARE PKG + + + + Extracting PKG %1/%2 + Extracție PKG %1/%2 + + + + Extraction Finished + Extracție terminată + + + + Game successfully installed at %1 + Jocul a fost instalat cu succes la %1 + + + + File doesn't appear to be a valid PKG file + Fișierul nu pare să fie un fișier PKG valid + + + + CheatsPatches + + + Cheats / Patches + Cheats / Patches + + + + defaultTextEdit_MSG + Cheats/Patches sunt experimentale.\nUtilizați cu prudență.\n\nDescărcați cheats individual prin selectarea depozitului și făcând clic pe butonul de descărcare.\nÎn fila Patches, puteți descărca toate patch-urile deodată, alege pe cele pe care doriți să le utilizați și salvați selecția.\n\nDeoarece nu dezvoltăm Cheats/Patches,\nte rugăm să raportezi problemele autorului cheat-ului.\n\nAi creat un nou cheat? Vizitează:\nhttps://github.com/shadps4-emu/ps4_cheats + + + + No Image Available + Nu este disponibilă imaginea + + + + Serial: + Serial: + + + + Version: + Versiune: + + + + Size: + Dimensiune: + + + + Select Cheat File: + Selectează fișierul Cheat: + + + + Repository: + Repository: + + + + Download Cheats + Descarcă Cheats + + + + Delete File + Șterge Fișierul + + + + No files selected. + Nu sunt fișiere selectate. + + + + You can delete the cheats you don't want after downloading them. + Poti șterge cheats-urile pe care nu le dorești după ce le-ai descărcat. + + + + Do you want to delete the selected file?\n%1 + Vrei să ștergi fișierul selectat?\n%1 + + + + Select Patch File: + Selectează fișierul Patch: + + + + Download Patches + Descarcă Patches + + + + Save + Salvează + + + + Cheats + Cheats + + + + Patches + Patches + + + + Error + Eroare + + + + No patch selected. + Nu este selectat niciun patch. + + + + Unable to open files.json for reading. + Imposibil de deschis files.json pentru citire. + + + + No patch file found for the current serial. + Nu s-a găsit niciun fișier patch pentru serialul curent. + + + + Unable to open the file for reading. + Imposibil de deschis fișierul pentru citire. + + + + Unable to open the file for writing. + Imposibil de deschis fișierul pentru scriere. + + + + Failed to parse XML: + Nu s-a reușit pararea XML: + + + + Success + Succes + + + + Options saved successfully. + Opțiunile au fost salvate cu succes. + + + + Invalid Source + Sursă invalidă + + + + The selected source is invalid. + Sursa selectată este invalidă. + + + + File Exists + Fișier existent + + + + File already exists. Do you want to replace it? + Fișierul există deja. Vrei să-l înlocuiești? + + + + Failed to save file: + Nu s-a reușit salvarea fișierului: + + + + Failed to download file: + Nu s-a reușit descărcarea fișierului: + + + + Cheats Not Found + Cheats Nu au fost găsite + + + + CheatsNotFound_MSG + Nu au fost găsite cheats pentru acest joc în această versiune a repository-ului selectat, încearcă un alt repository sau o versiune diferită a jocului. + + + + Cheats Downloaded Successfully + Cheats descărcate cu succes + + + + CheatsDownloadedSuccessfully_MSG + Ai descărcat cu succes cheats-urile pentru această versiune a jocului din repository-ul selectat. Poți încerca descărcarea din alt repository; dacă este disponibil, va fi posibil să-l folosești selectând fișierul din listă. + + + + Failed to save: + Nu s-a reușit salvarea: + + + + Failed to download: + Nu s-a reușit descărcarea: + + + + Download Complete + Descărcare completă + + + + DownloadComplete_MSG + Patches descărcate cu succes! Toate Patches disponibile pentru toate jocurile au fost descărcate; nu este nevoie să le descarci individual pentru fiecare joc, așa cum se întâmplă cu Cheats. + + + + Failed to parse JSON data from HTML. + Nu s-a reușit pararea datelor JSON din HTML. + + + + Failed to retrieve HTML page. + Nu s-a reușit obținerea paginii HTML. + + + + Failed to open file: + Nu s-a reușit deschiderea fișierului: + + + + XML ERROR: + EROARE XML: + + + + Failed to open files.json for writing + Nu s-a reușit deschiderea files.json pentru scriere + + + + Author: + Autor: + + + + Directory does not exist: + Directorul nu există: + + + + Failed to open files.json for reading. + Nu s-a reușit deschiderea files.json pentru citire. + + + + Name: + Nume: + + + \ No newline at end of file diff --git a/src/qt_gui/translations/ru_RU.ts b/src/qt_gui/translations/ru_RU.ts new file mode 100644 index 000000000..9e3446ad4 --- /dev/null +++ b/src/qt_gui/translations/ru_RU.ts @@ -0,0 +1,902 @@ + + + + AboutDialog + + + About shadPS4 + О shadPS4 + + + + shadPS4 + shadPS4 + + + + shadPS4 is an experimental open-source emulator for the PlayStation 4. + shadPS4 это экспериментальный эмулятор с открытым исходным кодом для PlayStation 4. + + + + This software should not be used to play games you have not legally obtained. + Это програмное обеспечение не должно использоваться для запуска игр, которые вы получили нелегально. + + + + ElfViewer + + + Open Folder + Открыть папку + + + + GameInfoClass + + + Loading game list, please wait :3 + Загрузка списка игр, пожалуйста подождите :3 + + + + Cancel + Отмена + + + + Loading... + Загрузка... + + + + GameInstallDialog + + + shadPS4 - Choose directory + shadPS4 - Выберите папку + + + + Directory to install games + Папка для установки игр + + + + Browse + Обзор + + + + Error + Ошибка + + + + The value for location to install games is not valid. + Недопустимое значение местоположения для установки игр. + + + + GuiContextMenus + + + Create Shortcut + Создать ярлык + + + + Open Game Folder + Открыть папку с игрой + + + + Cheats / Patches + Читы и патчи + + + + SFO Viewer + Просмотр SFO + + + + Trophy Viewer + Просмотр трофеев + + + + Copy info + Копировать информацию + + + + Copy Name + Копировать имя + + + + Copy Serial + Копировать серийный номер + + + + Copy All + Копировать все + + + + Shortcut creation + Создание ярлыка + + + + Shortcut created successfully!\n %1 + Ярлык создан успешно!\n %1 + + + + Error + Ошибка + + + + Error creating shortcut!\n %1 + Ошибка создания ярлыка!\n %1 + + + + Install PKG + Установить PKG + + + + MainWindow + + + Open/Add Elf Folder + Открыть/Добавить папку Elf + + + + Install Packages (PKG) + Установить пакеты (PKG) + + + + Boot Game + Запустить игру + + + + About shadPS4 + О shadPS4 + + + + Configure... + Настроить... + + + + Install application from a .pkg file + Установить приложение из файла .pkg + + + + Recent Games + Недавние игры + + + + Exit + Выход + + + + Exit shadPS4 + Выйти из shadPS4 + + + + Exit the application. + Выйти из приложения. + + + + Show Game List + Показать список игр + + + + Game List Refresh + Обновить список игр + + + + Tiny + Крошечный + + + + Small + Маленький + + + + Medium + Средний + + + + Large + Большой + + + + List View + Список + + + + Grid View + Сетка + + + + Elf Viewer + Elf + + + + Game Install Directory + Каталог установки игры + + + + Download Cheats/Patches + Скачать читы или патчи + + + + Dump Game List + Дамп списка игр + + + + PKG Viewer + Просмотр PKG + + + + Search... + Поиск... + + + + File + Файл + + + + View + Вид + + + + Game List Icons + Размер иконок списка игр + + + + Game List Mode + Вид списка игр + + + + Settings + Настройки + + + + Utils + Утилиты + + + + Themes + Темы + + + + About + Справка + + + + Dark + Темная + + + + Light + Светлая + + + + Green + Зеленая + + + + Blue + Синяя + + + + Violet + Фиолетовая + + + + toolBar + Панель инструментов + + + + PKGViewer + + + Open Folder + Открыть папку + + + + TrophyViewer + + + Trophy Viewer + Трофеи + + + + SettingsDialog + + + Settings + Настройки + + + + General + Общее + + + + System + Система + + + + Console Language + Язык консоли + + + + Emulator Language + Язык эмулятора + + + + Emulator + Эмулятор + + + + Enable Fullscreen + Включить полноэкранный режим + + + + Show Splash + Показать заставку + + + + Is PS4 Pro + Режим PS4 Pro + + + + Username + Имя пользователя + + + + Logger + Логирование + + + + Log Type + Тип логов + + + + Log Filter + Фильтр логов + + + + Graphics + Графика + + + + Graphics Device + Графическое устройство + + + + Width + Ширина + + + + Height + Высота + + + + Vblank Divider + Разделитель Vblank + + + + Advanced + Продвинутые + + + + Enable Shaders Dumping + Включить дамп шейдеров + + + + Enable NULL GPU + Включить NULL GPU + + + + Enable PM4 Dumping + Включить дамп PM4 + + + + Debug + Отладка + + + + Enable Debug Dumping + Включить отладочные дампы + + + + Enable Vulkan Validation Layers + Включить слои валидации Vulkan + + + + Enable Vulkan Synchronization Validation + Включить валидацию синхронизации Vulkan + + + + Enable RenderDoc Debugging + Включить отладку RenderDoc + + + + MainWindow + + + * Unsupported Vulkan Version + * Неподдерживаемая версия Vulkan + + + + Download Cheats For All Installed Games + Скачать читы для всех установленных игр + + + + Download Patches For All Games + Скачать патчи для всех игр + + + + Download Complete + Скачивание завершено + + + + You have downloaded cheats for all the games you have installed. + Вы скачали читы для всех установленных игр. + + + + Patches Downloaded Successfully! + Патчи успешно скачаны! + + + + All Patches available for all games have been downloaded. + Все доступные патчи для всех игр были скачаны. + + + + Games: + Игры: + + + + PKG File (*.PKG) + Файл PKG (*.PKG) + + + + ELF files (*.bin *.elf *.oelf) + Файл ELF (*.bin *.elf *.oelf) + + + + Game Boot + Запуск игры + + + + Only one file can be selected! + Можно выбрать только один файл! + + + + PKG Extraction + Извлечение PKG + + + + Patch detected! + Обнаружен патч! + + + + PKG and Game versions match: + Версии PKG и игры совпадают: + + + + Would you like to overwrite? + Хотите перезаписать? + + + + PKG Version %1 is older than installed version: + Версия PKG %1 старее установленной версии: + + + + Game is installed: + Игра установлена: + + + + Would you like to install Patch: + Хотите установить патч: + + + + DLC Installation + Установка DLC + + + + Would you like to install DLC: %1? + Вы хотите установить DLC: %1?? + + + + DLC already installed: + DLC уже установлен: + + + + Game already installed + Игра уже установлена + + + + PKG is a patch, please install the game first! + PKG - это патч, сначала установите игру! + + + + PKG ERROR + ОШИБКА PKG + + + + Extracting PKG %1/%2 + Извлечение PKG %1/%2 + + + + Extraction Finished + Извлечение завершено + + + + Game successfully installed at %1 + Игра успешно установлена в %1 + + + + File doesn't appear to be a valid PKG file + Файл не является допустимым файлом PKG + + + + CheatsPatches + + + Cheats / Patches + Читы и патчи + + + + defaultTextEdit_MSG + Читы и патчи экспериментальны.\nИспользуйте с осторожностью.\n\nСкачивайте читы, выбрав репозиторий и нажав на кнопку загрузки.\nВо вкладке "Патчи" вы можете скачать все патчи сразу, выбирать какие вы хотите использовать, и сохранять выбор.\n\nПоскольку мы не разрабатываем читы/патчи,\nпожалуйста сообщайте о проблемах автору чита/патча.\n\nСоздали новый чит? Посетите:\nhttps://github.com/shadps4-emu/ps4_cheats + + + + No Image Available + Изображение недоступно + + + + Serial: + Серийный номер: + + + + Version: + Версия: + + + + Size: + Размер: + + + + Select Cheat File: + Выберите файл чита: + + + + Repository: + Репозиторий: + + + + Download Cheats + Скачать читы + + + + Delete File + Удалить файл + + + + No files selected. + Файлы не выбраны. + + + + You can delete the cheats you don't want after downloading them. + Вы можете удалить ненужные читы после их скачивания. + + + + Do you want to delete the selected file?\n%1 + Вы хотите удалить выбранный файл?\n%1 + + + + Select Patch File: + Выберите файл патча: + + + + Download Patches + Скачать патчи + + + + Save + Сохранить + + + + Cheats + Читы + + + + Patches + Патчи + + + + Error + Ошибка + + + + No patch selected. + Патч не выбран. + + + + Unable to open files.json for reading. + Не удалось открыть файл files.json для чтения. + + + + No patch file found for the current serial. + Не найден файл патча для текущего серийного номера. + + + + Unable to open the file for reading. + Не удалось открыть файл для чтения. + + + + Unable to open the file for writing. + Не удалось открыть файл для записи. + + + + Failed to parse XML: + Не удалось разобрать XML: + + + + Success + Успех + + + + Options saved successfully. + Опции успешно сохранены. + + + + Invalid Source + Неверный источник + + + + The selected source is invalid. + Выбранный источник недействителен. + + + + File Exists + Файл существует + + + + File already exists. Do you want to replace it? + Файл уже существует. Хотите заменить его? + + + + Failed to save file: + Не удалось сохранить файл: + + + + Failed to download file: + Не удалось скачать файл: + + + + Cheats Not Found + Читы не найдены + + + + CheatsNotFound_MSG + Читы не найдены для этой игры в выбранном репозитории. Попробуйте другой репозиторий или другую версию игры. + + + + Cheats Downloaded Successfully + Читы успешно скачаны + + + + CheatsDownloadedSuccessfully_MSG + Вы успешно скачали читы для этой версии игры из выбранного репозитория. Вы можете попробовать скачать из другого репозитория. Если он доступен, его также можно будет использовать, выбрав файл из списка. + + + + Failed to save: + Не удалось сохранить: + + + + Failed to download: + Не удалось скачать: + + + + Download Complete + Скачивание завершено + + + + DownloadComplete_MSG + Патчи успешно скачаны! Все доступные патчи для всех игр были скачаны, нет необходимости скачивать их по отдельности для каждой игры, как это происходит с читами. + + + + Failed to parse JSON data from HTML. + Не удалось разобрать данные JSON из HTML. + + + + Failed to retrieve HTML page. + Не удалось получить HTML-страницу. + + + + Failed to open file: + Не удалось открыть файл: + + + + XML ERROR: + ОШИБКА XML: + + + + Failed to open files.json for writing + Не удалось открыть файл files.json для записи + + + + Author: + Автор: + + + + Directory does not exist: + Каталог не существует: + + + + Failed to open files.json for reading. + Не удалось открыть файл files.json для чтения. + + + + Name: + Имя: + + + \ No newline at end of file diff --git a/src/qt_gui/translations/tr_TR.ts b/src/qt_gui/translations/tr_TR.ts new file mode 100644 index 000000000..e11a2d960 --- /dev/null +++ b/src/qt_gui/translations/tr_TR.ts @@ -0,0 +1,973 @@ + + + + AboutDialog + + + About shadPS4 + shadPS4 Hakkında + + + + shadPS4 + shadPS4 + + + + shadPS4 is an experimental open-source emulator for the PlayStation 4. + shadPS4, PlayStation 4 için deneysel bir açık kaynak kodlu emülatördür. + + + + This software should not be used to play games you have not legally obtained. + Bu yazılım, yasal olarak edinmediğiniz oyunları oynamak için kullanılmamalıdır. + + + + ElfViewer + + + Open Folder + Klasörü Aç + + + + GameInfoClass + + + Loading game list, please wait :3 + Oyun listesi yükleniyor, lütfen bekleyin :3 + + + + Cancel + İptal + + + + Loading... + Yükleniyor... + + + + GameInstallDialog + + + shadPS4 - Choose directory + shadPS4 - Klasörü Seç + + + + Directory to install games + Oyunların yükleneceği klasör + + + + Browse + Gözat + + + + Error + Hata + + + + The value for location to install games is not valid. + Oyunların yükleneceği konum için girilen klasör geçerli değil. + + + + GuiContextMenus + + + Create Shortcut + Kısayol Oluştur + + + + Open Game Folder + Oyun Klasörünü Aç + + + + Cheats / Patches + Hileler / Yamanlar + + + + SFO Viewer + SFO Görüntüleyici + + + + Trophy Viewer + Kupa Görüntüleyici + + + + Copy info + Bilgiyi Kopyala + + + + Copy Name + Adı Kopyala + + + + Copy Serial + Seri Numarasını Kopyala + + + + Copy All + Tümünü Kopyala + + + + Shortcut creation + Kısayol oluşturma + + + + Shortcut created successfully!\n %1 + Kısayol başarıyla oluşturuldu!\n %1 + + + + Error + Hata + + + + Error creating shortcut!\n %1 + Kısayol oluşturulurken hata oluştu!\n %1 + + + + Install PKG + PKG Yükle + + + + MainWindow + + + Open/Add Elf Folder + Elf Klasörünü Aç/Ekle + + + + Install Packages (PKG) + Paketleri Kur (PKG) + + + + Boot Game + Oyunu Başlat + + + + About shadPS4 + shadPS4 Hakkında + + + + Configure... + Yapılandır... + + + + Install application from a .pkg file + .pkg dosyasından uygulama yükle + + + + Recent Games + Son Oyunlar + + + + Exit + Çıkış + + + + Exit shadPS4 + shadPS4'ten Çık + + + + Exit the application. + Uygulamadan çık. + + + + Show Game List + Oyun Listesini Göster + + + + Game List Refresh + Oyun Listesini Yenile + + + + Tiny + Küçük + + + + Small + Ufak + + + + Medium + Orta + + + + Large + Büyük + + + + List View + Liste Görünümü + + + + Grid View + Izgara Görünümü + + + + Elf Viewer + Elf Görüntüleyici + + + + Game Install Directory + Oyun Kurulum Klasörü + + + + Download Cheats/Patches + Hileler / Yamanlar İndir + + + + Dump Game List + Oyun Listesini Kaydet + + + + PKG Viewer + PKG Görüntüleyici + + + + Search... + Ara... + + + + File + Dosya + + + + View + Görünüm + + + + Game List Icons + Oyun Listesi Simgeleri + + + + Game List Mode + Oyun Listesi Modu + + + + Settings + Ayarlar + + + + Utils + Yardımcı Araçlar + + + + Themes + Temalar + + + + About + Hakkında + + + + Dark + Koyu + + + + Light + Açık + + + + Green + Yeşil + + + + Blue + Mavi + + + + Violet + Mor + + + + toolBar + Araç Çubuğu + + + + PKGViewer + + + Open Folder + Klasörü Aç + + + + TrophyViewer + + + Trophy Viewer + Kupa Görüntüleyici + + + + SettingsDialog + + + Settings + Ayarlar + + + + General + Genel + + + + System + Sistem + + + + Console Language + Konsol Dili + + + + Emulator Language + Emülatör Dili + + + + Emulator + Emülatör + + + + Enable Fullscreen + Tam Ekranı Etkinleştir + + + + Show Splash + Başlangıç Ekranını Göster + + + + Is PS4 Pro + PS4 Pro mu + + + + Username + Kullanıcı Adı + + + + Logger + Kayıt Tutucu + + + + Log Type + Kayıt Türü + + + + Log Filter + Kayıt Filtresi + + + + Graphics + Grafikler + + + + Graphics Device + Grafik Cihazı + + + + Width + Genişlik + + + + Height + Yükseklik + + + + Vblank Divider + Vblank Bölücü + + + + Advanced + Gelişmiş + + + + Enable Shaders Dumping + Shader Kaydını Etkinleştir + + + + Enable NULL GPU + NULL GPU'yu Etkinleştir + + + + Enable PM4 Dumping + PM4 Kaydını Etkinleştir + + + + Debug + Hata Ayıklama + + + + Enable Debug Dumping + Hata Ayıklama Dökümü Etkinleştir + + + + Enable Vulkan Validation Layers + Vulkan Doğrulama Katmanlarını Etkinleştir + + + + Enable Vulkan Synchronization Validation + Vulkan Senkronizasyon Doğrulamasını Etkinleştir + + + + Enable RenderDoc Debugging + RenderDoc Hata Ayıklamayı Etkinleştir + + + + MainWindow + + + * Unsupported Vulkan Version + * Desteklenmeyen Vulkan Sürümü + + + + Download Cheats For All Installed Games + Tüm Yüklenmiş Oyunlar İçin Hileleri İndir + + + + Download Patches For All Games + Tüm Oyunlar İçin Yamanları İndir + + + + Download Complete + İndirme Tamamlandı + + + + You have downloaded cheats for all the games you have installed. + Yüklediğiniz tüm oyunlar için hileleri indirdiniz. + + + + Patches Downloaded Successfully! + Yamalar Başarıyla İndirildi! + + + + All Patches available for all games have been downloaded. + Tüm oyunlar için mevcut tüm yamalar indirildi. + + + + Games: + Oyunlar: + + + + PKG File (*.PKG) + PKG Dosyası (*.PKG) + + + + ELF files (*.bin *.elf *.oelf) + ELF Dosyaları (*.bin *.elf *.oelf) + + + + Game Boot + Oyun Başlatma + + + + Only one file can be selected! + Sadece bir dosya seçilebilir! + + + + PKG Extraction + PKG Çıkartma + + + + Patch detected! + Yamanın tespit edildi! + + + + PKG and Game versions match: + PKG ve oyun sürümleri uyumlu: + + + + Would you like to overwrite? + Üzerine yazmak ister misiniz? + + + + PKG Version %1 is older than installed version: + PKG Sürümü %1, kurulu sürümden daha eski: + + + + Game is installed: + Oyun yüklendi: + + + + Would you like to install Patch: + Yamanın yüklenmesini ister misiniz: + + + + DLC Installation + DLC Yükleme + + + + Would you like to install DLC: %1? + DLC'yi yüklemek ister misiniz: %1? + + + + DLC already installed: + DLC zaten yüklü: + + + + Game already installed + Oyun zaten yüklü + + + + PKG is a patch, please install the game first! + PKG bir yama, lütfen önce oyunu yükleyin! + + + + PKG ERROR + PKG HATASI + + + + Extracting PKG %1/%2 + PKG Çıkarılıyor %1/%2 + + + + Extraction Finished + Çıkarma Tamamlandı + + + + Game successfully installed at %1 + Oyun başarıyla %1 konumuna yüklendi + + + + File doesn't appear to be a valid PKG file + Dosya geçerli bir PKG dosyası gibi görünmüyor + + + + CheatsPatches + + + Cheats / Patches + Hileler / Yamalar + + + + defaultTextEdit_MSG + Cheats/Patches deneysel niteliktedir.\nDikkatli kullanın.\n\nCheat'leri ayrı ayrı indirerek, depo seçerek ve indirme düğmesine tıklayarak indirin.\nPatches sekmesinde tüm patch'leri bir kerede indirebilir, hangi patch'leri kullanmak istediğinizi seçebilir ve seçiminizi kaydedebilirsiniz.\n\nCheats/Patches'i geliştirmediğimiz için,\nproblemleri cheat yazarına bildirin.\n\nYeni bir cheat mi oluşturduğunuz? Şu adresi ziyaret edin:\nhttps://github.com/shadps4-emu/ps4_cheats + + + + No Image Available + Görüntü Mevcut Değil + + + + Serial: + Seri Numarası: + + + + Version: + Sürüm: + + + + Size: + Boyut: + + + + Select Cheat File: + Hile Dosyasını Seçin: + + + + Repository: + Depo: + + + + Download Cheats + Hileleri İndir + + + + Confirm Delete + Silme Onayı + + + + Are you sure you want to delete the selected cheat?\n%1 + Seçilen hileyi silmek istediğinizden emin misiniz?\n%1 + + + + You can delete the cheats you don't want after downloading them. + İndirdikten sonra istemediğiniz hileleri silebilirsiniz. + + + + Do you want to delete the selected file?\n%1 + Seçilen dosyayı silmek istiyor musunuz?\n%1 + + + + Select Patch File: + Yama Dosyasını Seçin: + + + + Download Patches + Yamaları İndir + + + + Save + Kaydet + + + + Cheats + Hileler + + + + Patches + Yamalar + + + + Error + Hata + + + + No patch selected. + Hiç yama seçilmedi. + + + + Unable to open files.json for reading. + files.json dosyasını okumak için açılamadı. + + + + No patch file found for the current serial. + Mevcut seri numarası için hiç yama dosyası bulunamadı. + + + + Unable to open the file for reading. + Dosya okumak için açılamadı. + + + + Unable to open the file for writing. + Dosya yazmak için açılamadı. + + + + Failed to parse XML: + XML ayrıştırılamadı: + + + + Success + Başarı + + + + Options saved successfully. + Ayarlar başarıyla kaydedildi. + + + + Invalid Source + Geçersiz Kaynak + + + + The selected source is invalid. + Seçilen kaynak geçersiz. + + + + File Exists + Dosya Var + + + + File already exists. Do you want to replace it? + Dosya zaten var. Üzerine yazmak ister misiniz? + + + + Failed to save file: + Dosya kaydedilemedi: + + + + Failed to download file: + Dosya indirilemedi: + + + + Cheats Not Found + Hileler Bulunamadı + + + + CheatsNotFound_MSG + Bu oyun için seçilen depoda hile bulunamadı.Başka bir depo veya oyun sürümü deneyin. + + + + Cheats Downloaded Successfully + Hileler Başarıyla İndirildi + + + + CheatsDownloadedSuccessfully_MSG + Bu oyun sürümü için hileleri başarıyla indirdiniz. Başka bir depodan indirmeyi deneyebilirsiniz. Eğer mevcutsa, listeden dosyayı seçerek de kullanılabilir. + + + + Failed to save: + Kaydedilemedi: + + + + Failed to download: + İndirilemedi: + + + + Download Complete + İndirme Tamamlandı + + + + DownloadComplete_MSG + Yamalar başarıyla indirildi! Tüm oyunlar için mevcut tüm yamalar indirildi, her oyun için ayrı ayrı indirme yapmanız gerekmez, hilelerle olduğu gibi. + + + + Failed to parse JSON data from HTML. + HTML'den JSON verileri ayrıştırılamadı. + + + + Failed to retrieve HTML page. + HTML sayfası alınamadı. + + + + Failed to open file: + Dosya açılamadı: + + + + XML ERROR: + XML HATASI: + + + + Failed to open files.json for writing + files.json dosyası yazmak için açılamadı + + + + Author: + Yazar: + + + + Directory does not exist: + Klasör mevcut değil: + + + + Failed to open files.json for reading. + files.json dosyası okumak için açılamadı. + + + + Name: + İsim: + + + + Version: + Sürüm: + + + + Size: + Boyut: + + + + LangDialog + + + Language Settings + Dil Ayarları + + + + Select Language: + Dil Seçin: + + + + Restart Required + Yeniden Başlatma Gerekiyor + + + + Changes will take effect after restarting the application. + Değişiklikler uygulama yeniden başlatıldığında geçerli olacaktır. + + + + SettingsDialog + + + Settings + Ayarlar + + + + General + Genel + + + + Cheats + Hileler + + + + Update + Güncelleme + + + + Save + Kaydet + + + + Reset to Default + Varsayılana Sıfırla + + + + Apply Changes + Değişiklikleri Uygula + + + \ No newline at end of file diff --git a/src/qt_gui/translations/vi_VN.ts b/src/qt_gui/translations/vi_VN.ts new file mode 100644 index 000000000..aead45a63 --- /dev/null +++ b/src/qt_gui/translations/vi_VN.ts @@ -0,0 +1,902 @@ + + + + AboutDialog + + + About shadPS4 + About shadPS4 + + + + shadPS4 + shadPS4 + + + + shadPS4 is an experimental open-source emulator for the PlayStation 4. + shadPS4 is an experimental open-source emulator for the PlayStation 4. + + + + This software should not be used to play games you have not legally obtained. + This software should not be used to play games you have not legally obtained. + + + + ElfViewer + + + Open Folder + Open Folder + + + + GameInfoClass + + + Loading game list, please wait :3 + Loading game list, please wait :3 + + + + Cancel + Cancel + + + + Loading... + Loading... + + + + GameInstallDialog + + + shadPS4 - Choose directory + shadPS4 - Choose directory + + + + Directory to install games + Directory to install games + + + + Browse + Browse + + + + Error + Error + + + + The value for location to install games is not valid. + The value for location to install games is not valid. + + + + GuiContextMenus + + + Create Shortcut + Create Shortcut + + + + Open Game Folder + Open Game Folder + + + + Cheats / Patches + Mẹo / Bản vá + + + + SFO Viewer + SFO Viewer + + + + Trophy Viewer + Trophy Viewer + + + + Copy info + Copy info + + + + Copy Name + Copy Name + + + + Copy Serial + Copy Serial + + + + Copy All + Copy All + + + + Shortcut creation + Shortcut creation + + + + Shortcut created successfully!\n %1 + Shortcut created successfully!\n %1 + + + + Error + Error + + + + Error creating shortcut!\n %1 + Error creating shortcut!\n %1 + + + + Install PKG + Install PKG + + + + MainWindow + + + Open/Add Elf Folder + Open/Add Elf Folder + + + + Install Packages (PKG) + Install Packages (PKG) + + + + Boot Game + Boot Game + + + + About shadPS4 + About shadPS4 + + + + Configure... + Configure... + + + + Install application from a .pkg file + Install application from a .pkg file + + + + Recent Games + Recent Games + + + + Exit + Exit + + + + Exit shadPS4 + Exit shadPS4 + + + + Exit the application. + Exit the application. + + + + Show Game List + Show Game List + + + + Game List Refresh + Game List Refresh + + + + Tiny + Tiny + + + + Small + Small + + + + Medium + Medium + + + + Large + Large + + + + List View + List View + + + + Grid View + Grid View + + + + Elf Viewer + Elf Viewer + + + + Game Install Directory + Game Install Directory + + + + Download Cheats/Patches + Tải Mẹo / Bản vá + + + + Dump Game List + Dump Game List + + + + PKG Viewer + PKG Viewer + + + + Search... + Search... + + + + File + File + + + + View + View + + + + Game List Icons + Game List Icons + + + + Game List Mode + Game List Mode + + + + Settings + Settings + + + + Utils + Utils + + + + Themes + Themes + + + + About + About + + + + Dark + Dark + + + + Light + Light + + + + Green + Green + + + + Blue + Blue + + + + Violet + Violet + + + + toolBar + toolBar + + + + PKGViewer + + + Open Folder + Open Folder + + + + TrophyViewer + + + Trophy Viewer + Trophy Viewer + + + + SettingsDialog + + + Settings + Settings + + + + General + General + + + + System + System + + + + Console Language + Console Language + + + + Emulator Language + Emulator Language + + + + Emulator + Emulator + + + + Enable Fullscreen + Enable Fullscreen + + + + Show Splash + Show Splash + + + + Is PS4 Pro + Is PS4 Pro + + + + Username + Username + + + + Logger + Logger + + + + Log Type + Log Type + + + + Log Filter + Log Filter + + + + Graphics + Graphics + + + + Graphics Device + Graphics Device + + + + Width + Width + + + + Height + Height + + + + Vblank Divider + Vblank Divider + + + + Advanced + Advanced + + + + Enable Shaders Dumping + Enable Shaders Dumping + + + + Enable NULL GPU + Enable NULL GPU + + + + Enable PM4 Dumping + Enable PM4 Dumping + + + + Debug + Debug + + + + Enable Debug Dumping + Enable Debug Dumping + + + + Enable Vulkan Validation Layers + Enable Vulkan Validation Layers + + + + Enable Vulkan Synchronization Validation + Enable Vulkan Synchronization Validation + + + + Enable RenderDoc Debugging + Enable RenderDoc Debugging + + + + MainWindow + + + * Unsupported Vulkan Version + * Phiên bản Vulkan không được hỗ trợ + + + + Download Cheats For All Installed Games + Tải xuống cheat cho tất cả các trò chơi đã cài đặt + + + + Download Patches For All Games + Tải xuống bản vá cho tất cả các trò chơi + + + + Download Complete + Tải xuống hoàn tất + + + + You have downloaded cheats for all the games you have installed. + Bạn đã tải xuống cheat cho tất cả các trò chơi mà bạn đã cài đặt. + + + + Patches Downloaded Successfully! + Bản vá đã tải xuống thành công! + + + + All Patches available for all games have been downloaded. + Tất cả các bản vá có sẵn cho tất cả các trò chơi đã được tải xuống. + + + + Games: + Trò chơi: + + + + PKG File (*.PKG) + Tệp PKG (*.PKG) + + + + ELF files (*.bin *.elf *.oelf) + Tệp ELF (*.bin *.elf *.oelf) + + + + Game Boot + Khởi động trò chơi + + + + Only one file can be selected! + Chỉ có thể chọn một tệp duy nhất! + + + + PKG Extraction + Giải nén PKG + + + + Patch detected! + Đã phát hiện bản vá! + + + + PKG and Game versions match: + Các phiên bản PKG và trò chơi khớp nhau: + + + + Would you like to overwrite? + Bạn có muốn ghi đè không? + + + + PKG Version %1 is older than installed version: + Phiên bản PKG %1 cũ hơn phiên bản đã cài đặt: + + + + Game is installed: + Trò chơi đã được cài đặt: + + + + Would you like to install Patch: + Bạn có muốn cài đặt bản vá: + + + + DLC Installation + Cài đặt DLC + + + + Would you like to install DLC: %1? + Bạn có muốn cài đặt DLC: %1? + + + + DLC already installed: + DLC đã được cài đặt: + + + + Game already installed + Trò chơi đã được cài đặt + + + + PKG is a patch, please install the game first! + PKG là bản vá, vui lòng cài đặt trò chơi trước! + + + + PKG ERROR + LOI PKG + + + + Extracting PKG %1/%2 + Đang giải nén PKG %1/%2 + + + + Extraction Finished + Giải nén hoàn tất + + + + Game successfully installed at %1 + Trò chơi đã được cài đặt thành công tại %1 + + + + File doesn't appear to be a valid PKG file + Tệp không có vẻ là tệp PKG hợp lệ + + + + CheatsPatches + + + Cheats / Patches + Cheat / Bản vá + + + + defaultTextEdit_MSG + Cheats/Patches là các tính năng thử nghiệm.\nHãy sử dụng cẩn thận.\n\nTải xuống các cheat riêng lẻ bằng cách chọn kho lưu trữ và nhấp vào nút tải xuống.\nTại tab Patches, bạn có thể tải xuống tất cả các patch cùng một lúc, chọn cái nào bạn muốn sử dụng và lưu lựa chọn của mình.\n\nVì chúng tôi không phát triển Cheats/Patches,\nxin vui lòng báo cáo các vấn đề cho tác giả cheat.\n\nBạn đã tạo ra một cheat mới? Truy cập:\nhttps://github.com/shadps4-emu/ps4_cheats + + + + No Image Available + Không có hình ảnh + + + + Serial: + Số seri: + + + + Version: + Phiên bản: + + + + Size: + Kích thước: + + + + Select Cheat File: + Chọn tệp Cheat: + + + + Repository: + Kho lưu trữ: + + + + Download Cheats + Tải xuống Cheat + + + + Delete File + Xóa tệp + + + + No files selected. + Không có tệp nào được chọn. + + + + You can delete the cheats you don't want after downloading them. + Bạn có thể xóa các cheat không muốn sau khi tải xuống. + + + + Do you want to delete the selected file?\n%1 + Bạn có muốn xóa tệp đã chọn?\n%1 + + + + Select Patch File: + Chọn tệp Bản vá: + + + + Download Patches + Tải xuống Bản vá + + + + Save + Lưu + + + + Cheats + Cheat + + + + Patches + Bản vá + + + + Error + Lỗi + + + + No patch selected. + Không có bản vá nào được chọn. + + + + Unable to open files.json for reading. + Không thể mở files.json để đọc. + + + + No patch file found for the current serial. + Không tìm thấy tệp bản vá cho số seri hiện tại. + + + + Unable to open the file for reading. + Không thể mở tệp để đọc. + + + + Unable to open the file for writing. + Không thể mở tệp để ghi. + + + + Failed to parse XML: + Không thể phân tích XML: + + + + Success + Thành công + + + + Options saved successfully. + Các tùy chọn đã được lưu thành công. + + + + Invalid Source + Nguồn không hợp lệ + + + + The selected source is invalid. + Nguồn đã chọn không hợp lệ. + + + + File Exists + Tệp đã tồn tại + + + + File already exists. Do you want to replace it? + Tệp đã tồn tại. Bạn có muốn thay thế nó không? + + + + Failed to save file: + Không thể lưu tệp: + + + + Failed to download file: + Không thể tải xuống tệp: + + + + Cheats Not Found + Không tìm thấy Cheat + + + + CheatsNotFound_MSG + Không tìm thấy Cheat cho trò chơi này trong phiên bản kho lưu trữ đã chọn,hãy thử kho lưu trữ khác hoặc phiên bản khác của trò chơi. + + + + Cheats Downloaded Successfully + Cheat đã tải xuống thành công + + + + CheatsDownloadedSuccessfully_MSG + Bạn đã tải xuống các cheat thành công. Cho phiên bản trò chơi này từ kho lưu trữ đã chọn. Bạn có thể thử tải xuống từ kho lưu trữ khác, nếu có, bạn cũng có thể sử dụng bằng cách chọn tệp từ danh sách. + + + + Failed to save: + Không thể lưu: + + + + Failed to download: + Không thể tải xuống: + + + + Download Complete + Tải xuống hoàn tất + + + + DownloadComplete_MSG + Bản vá đã tải xuống thành công! Tất cả các bản vá có sẵn cho tất cả các trò chơi đã được tải xuống, không cần tải xuống riêng lẻ cho mỗi trò chơi như trong Cheat. + + + + Failed to parse JSON data from HTML. + Không thể phân tích dữ liệu JSON từ HTML. + + + + Failed to retrieve HTML page. + Không thể lấy trang HTML. + + + + Failed to open file: + Không thể mở tệp: + + + + XML ERROR: + LỖI XML: + + + + Failed to open files.json for writing + Không thể mở files.json để ghi + + + + Author: + Tác giả: + + + + Directory does not exist: + Thư mục không tồn tại: + + + + Failed to open files.json for reading. + Không thể mở files.json để đọc. + + + + Name: + Tên: + + + \ No newline at end of file diff --git a/src/qt_gui/translations/zh_CN.ts b/src/qt_gui/translations/zh_CN.ts new file mode 100644 index 000000000..7584fd5ea --- /dev/null +++ b/src/qt_gui/translations/zh_CN.ts @@ -0,0 +1,902 @@ + + + + AboutDialog + + + About shadPS4 + 关于 shadPS4 + + + + shadPS4 + shadPS4 + + + + shadPS4 is an experimental open-source emulator for the PlayStation 4. + shadPS4 是一款实验性质的开源 PlayStation 4模拟器软件。 + + + + This software should not be used to play games you have not legally obtained. + 本软件不得用于运行未经合法授权而获得的游戏。 + + + + ElfViewer + + + Open Folder + 打开文件夹 + + + + GameInfoClass + + + Loading game list, please wait :3 + 加载游戏列表中, 请稍等 :3 + + + + Cancel + 取消 + + + + Loading... + 加载中... + + + + GameInstallDialog + + + shadPS4 - Choose directory + shadPS4 - 选择文件目录 + + + + Directory to install games + 要安装游戏的目录 + + + + Browse + 浏览 + + + + Error + 错误 + + + + The value for location to install games is not valid. + 游戏安装位置无效。 + + + + GuiContextMenus + + + Create Shortcut + 创建快捷方式 + + + + Open Game Folder + 打开游戏文件夹 + + + + Cheats / Patches + 作弊码 / 补丁 + + + + SFO Viewer + SFO 查看器 + + + + Trophy Viewer + Trophy 查看器 + + + + Copy info + 复制信息 + + + + Copy Name + 复制名称 + + + + Copy Serial + 复制序列号 + + + + Copy All + 复制全部 + + + + Shortcut creation + 创建快捷方式 + + + + Shortcut created successfully!\n %1 + 创建快捷方式成功!\n %1 + + + + Error + 错误 + + + + Error creating shortcut!\n %1 + 创建快捷方式出错!\n %1 + + + + Install PKG + 安装 PKG + + + + MainWindow + + + Open/Add Elf Folder + 打开/添加Elf文件夹 + + + + Install Packages (PKG) + 安装 Packages (PKG) + + + + Boot Game + 启动游戏 + + + + About shadPS4 + 关于 shadPS4 + + + + Configure... + 设置... + + + + Install application from a .pkg file + 从 .pkg 文件安装应用程序 + + + + Recent Games + 最近启动的游戏 + + + + Exit + 退出 + + + + Exit shadPS4 + 退出 shadPS4 + + + + Exit the application. + 退出应用程序. + + + + Show Game List + 显示游戏列表 + + + + Game List Refresh + 刷新游戏列表 + + + + Tiny + 微小 + + + + Small + + + + + Medium + 中等 + + + + Large + 巨大 + + + + List View + 列表视图 + + + + Grid View + 表格视图 + + + + Elf Viewer + Elf 查看器 + + + + Game Install Directory + 游戏安装目录 + + + + Download Cheats/Patches + 下载作弊码/补丁 + + + + Dump Game List + 转储游戏列表 + + + + PKG Viewer + PKG 查看器 + + + + Search... + 搜索... + + + + File + 文件 + + + + View + 显示 + + + + Game List Icons + 游戏列表图标 + + + + Game List Mode + 游戏列表模式 + + + + Settings + 设置 + + + + Utils + 工具 + + + + Themes + 主题 + + + + About + 关于 + + + + Dark + Dark + + + + Light + Light + + + + Green + Green + + + + Blue + Blue + + + + Violet + Violet + + + + toolBar + 工具栏 + + + + PKGViewer + + + Open Folder + 打开文件夹 + + + + TrophyViewer + + + Trophy Viewer + Trophy 查看器 + + + + SettingsDialog + + + Settings + 设置 + + + + General + 通用 + + + + System + 系统 + + + + Console Language + 主机语言 + + + + Emulator Language + 模拟器语言 + + + + Emulator + 模拟器 + + + + Enable Fullscreen + 启用全屏 + + + + Show Splash + 显示Splash + + + + Is PS4 Pro + 是否是 PS4 Pro + + + + Username + 用户名 + + + + Logger + 日志 + + + + Log Type + 日志类型 + + + + Log Filter + 日志过滤 + + + + Graphics + 图像 + + + + Graphics Device + 图像设备 + + + + Width + 宽带 + + + + Height + 高度 + + + + Vblank Divider + Vblank Divider + + + + Advanced + 高级 + + + + Enable Shaders Dumping + 启用着色器转储 + + + + Enable NULL GPU + 启用 NULL GPU + + + + Enable PM4 Dumping + 启用 PM4 转储 + + + + Debug + 调试 + + + + Enable Debug Dumping + 启用调试转储 + + + + Enable Vulkan Validation Layers + 启用 Vulkan 验证层 + + + + Enable Vulkan Synchronization Validation + 启用 Vulkan 同步验证 + + + + Enable RenderDoc Debugging + 启用 RenderDoc 调试 + + + + MainWindow + + + * Unsupported Vulkan Version + * 不支持的 Vulkan 版本 + + + + Download Cheats For All Installed Games + 下载所有已安装游戏的作弊码 + + + + Download Patches For All Games + 下载所有游戏的补丁 + + + + Download Complete + 下载完成 + + + + You have downloaded cheats for all the games you have installed. + 您已下载了所有已安装游戏的作弊码。 + + + + Patches Downloaded Successfully! + 补丁下载成功! + + + + All Patches available for all games have been downloaded. + 所有游戏的所有补丁都已下载。 + + + + Games: + 游戏: + + + + PKG File (*.PKG) + PKG 文件 (*.PKG) + + + + ELF files (*.bin *.elf *.oelf) + ELF 文件 (*.bin *.elf *.oelf) + + + + Game Boot + 游戏启动 + + + + Only one file can be selected! + 只能选择一个文件! + + + + PKG Extraction + PKG 解压 + + + + Patch detected! + 检测到补丁! + + + + PKG and Game versions match: + PKG 和游戏版本匹配: + + + + Would you like to overwrite? + 您想要覆盖吗? + + + + PKG Version %1 is older than installed version: + PKG 版本 %1 比已安装版本更旧: + + + + Game is installed: + 游戏已安装: + + + + Would you like to install Patch: + 您想安装补丁吗: + + + + DLC Installation + DLC 安装 + + + + Would you like to install DLC: %1? + 您想安装 DLC: %1 吗? + + + + DLC already installed: + DLC 已经安装: + + + + Game already installed + 游戏已经安装 + + + + PKG is a patch, please install the game first! + PKG 是一个补丁,请先安装游戏! + + + + PKG ERROR + PKG 错误 + + + + Extracting PKG %1/%2 + 正在解压 PKG %1/%2 + + + + Extraction Finished + 解压完成 + + + + Game successfully installed at %1 + 游戏成功安装在 %1 + + + + File doesn't appear to be a valid PKG file + 文件似乎不是有效的 PKG 文件 + + + + CheatsPatches + + + Cheats / Patches + 作弊码 / 补丁 + + + + defaultTextEdit_MSG + 作弊/补丁是实验性的。\n请小心使用。\n\n通过选择存储库并点击下载按钮,单独下载作弊程序。\n在“补丁”选项卡中,您可以一次性下载所有补丁,选择要使用的补丁并保存选择。\n\n由于我们不开发作弊程序/补丁,\n请将问题报告给作弊程序的作者。\n\n创建了新的作弊程序?访问:\nhttps://github.com/shadps4-emu/ps4_cheats + + + + No Image Available + 没有可用的图像 + + + + Serial: + 序列号: + + + + Version: + 版本: + + + + Size: + 大小: + + + + Select Cheat File: + 选择作弊码文件: + + + + Repository: + 存储库: + + + + Download Cheats + 下载作弊码 + + + + Delete File + 删除文件 + + + + No files selected. + 没有选择文件。 + + + + You can delete the cheats you don't want after downloading them. + 您可以在下载后删除不想要的作弊码。 + + + + Do you want to delete the selected file?\n%1 + 您要删除选中的文件吗?\n%1 + + + + Select Patch File: + 选择补丁文件: + + + + Download Patches + 下载补丁 + + + + Save + 保存 + + + + Cheats + 作弊码 + + + + Patches + 补丁 + + + + Error + 错误 + + + + No patch selected. + 没有选择补丁。 + + + + Unable to open files.json for reading. + 无法打开 files.json 进行读取。 + + + + No patch file found for the current serial. + 未找到当前序列号的补丁文件。 + + + + Unable to open the file for reading. + 无法打开文件进行读取。 + + + + Unable to open the file for writing. + 无法打开文件进行写入。 + + + + Failed to parse XML: + 解析 XML 失败: + + + + Success + 成功 + + + + Options saved successfully. + 选项已成功保存。 + + + + Invalid Source + 无效的来源 + + + + The selected source is invalid. + 选择的来源无效。 + + + + File Exists + 文件已存在 + + + + File already exists. Do you want to replace it? + 文件已存在。您要替换它吗? + + + + Failed to save file: + 保存文件失败: + + + + Failed to download file: + 下载文件失败: + + + + Cheats Not Found + 未找到作弊码 + + + + CheatsNotFound_MSG + 在所选存储库的版本中找不到该游戏的作弊码,请尝试其他存储库或游戏版本。 + + + + Cheats Downloaded Successfully + 作弊码下载成功 + + + + CheatsDownloadedSuccessfully_MSG + 您已成功下载了该游戏版本的作弊码 从所选存储库中。如果有,您还可以尝试从其他存储库下载,或通过从列表中选择文件来使用它们。 + + + + Failed to save: + 保存失败: + + + + Failed to download: + 下载失败: + + + + Download Complete + 下载完成 + + + + DownloadComplete_MSG + 补丁下载成功!所有可用的补丁已下载完成,无需像作弊码那样单独下载每个游戏的补丁。 + + + + Failed to parse JSON data from HTML. + 无法解析 HTML 中的 JSON 数据。 + + + + Failed to retrieve HTML page. + 无法获取 HTML 页面。 + + + + Failed to open file: + 无法打开文件: + + + + XML ERROR: + XML 错误: + + + + Failed to open files.json for writing + 无法打开 files.json 进行写入 + + + + Author: + 作者: + + + + Directory does not exist: + 目录不存在: + + + + Failed to open files.json for reading. + 无法打开 files.json 进行读取。 + + + + Name: + 名称: + + + \ No newline at end of file diff --git a/src/qt_gui/translations/zh_TW.ts b/src/qt_gui/translations/zh_TW.ts new file mode 100644 index 000000000..3836ed18a --- /dev/null +++ b/src/qt_gui/translations/zh_TW.ts @@ -0,0 +1,902 @@ + + + + AboutDialog + + + About shadPS4 + About shadPS4 + + + + shadPS4 + shadPS4 + + + + shadPS4 is an experimental open-source emulator for the PlayStation 4. + shadPS4 is an experimental open-source emulator for the PlayStation 4. + + + + This software should not be used to play games you have not legally obtained. + This software should not be used to play games you have not legally obtained. + + + + ElfViewer + + + Open Folder + Open Folder + + + + GameInfoClass + + + Loading game list, please wait :3 + Loading game list, please wait :3 + + + + Cancel + Cancel + + + + Loading... + Loading... + + + + GameInstallDialog + + + shadPS4 - Choose directory + shadPS4 - Choose directory + + + + Directory to install games + Directory to install games + + + + Browse + Browse + + + + Error + Error + + + + The value for location to install games is not valid. + The value for location to install games is not valid. + + + + GuiContextMenus + + + Create Shortcut + Create Shortcut + + + + Open Game Folder + Open Game Folder + + + + Cheats / Patches + Zuòbì / Xiūbǔ chéngshì + + + + SFO Viewer + SFO Viewer + + + + Trophy Viewer + Trophy Viewer + + + + Copy info + Copy info + + + + Copy Name + Copy Name + + + + Copy Serial + Copy Serial + + + + Copy All + Copy All + + + + Shortcut creation + Shortcut creation + + + + Shortcut created successfully!\n %1 + Shortcut created successfully!\n %1 + + + + Error + Error + + + + Error creating shortcut!\n %1 + Error creating shortcut!\n %1 + + + + Install PKG + Install PKG + + + + MainWindow + + + Open/Add Elf Folder + Open/Add Elf Folder + + + + Install Packages (PKG) + Install Packages (PKG) + + + + Boot Game + Boot Game + + + + About shadPS4 + About shadPS4 + + + + Configure... + Configure... + + + + Install application from a .pkg file + Install application from a .pkg file + + + + Recent Games + Recent Games + + + + Exit + Exit + + + + Exit shadPS4 + Exit shadPS4 + + + + Exit the application. + Exit the application. + + + + Show Game List + Show Game List + + + + Game List Refresh + Game List Refresh + + + + Tiny + Tiny + + + + Small + Small + + + + Medium + Medium + + + + Large + Large + + + + List View + List View + + + + Grid View + Grid View + + + + Elf Viewer + Elf Viewer + + + + Game Install Directory + Game Install Directory + + + + Download Cheats/Patches + Xiàzài Zuòbì / Xiūbǔ chéngshì + + + + Dump Game List + Dump Game List + + + + PKG Viewer + PKG Viewer + + + + Search... + Search... + + + + File + File + + + + View + View + + + + Game List Icons + Game List Icons + + + + Game List Mode + Game List Mode + + + + Settings + Settings + + + + Utils + Utils + + + + Themes + Themes + + + + About + About + + + + Dark + Dark + + + + Light + Light + + + + Green + Green + + + + Blue + Blue + + + + Violet + Violet + + + + toolBar + toolBar + + + + PKGViewer + + + Open Folder + Open Folder + + + + TrophyViewer + + + Trophy Viewer + Trophy Viewer + + + + SettingsDialog + + + Settings + Settings + + + + General + General + + + + System + System + + + + Console Language + Console Language + + + + Emulator Language + Emulator Language + + + + Emulator + Emulator + + + + Enable Fullscreen + Enable Fullscreen + + + + Show Splash + Show Splash + + + + Is PS4 Pro + Is PS4 Pro + + + + Username + Username + + + + Logger + Logger + + + + Log Type + Log Type + + + + Log Filter + Log Filter + + + + Graphics + Graphics + + + + Graphics Device + Graphics Device + + + + Width + Width + + + + Height + Height + + + + Vblank Divider + Vblank Divider + + + + Advanced + Advanced + + + + Enable Shaders Dumping + Enable Shaders Dumping + + + + Enable NULL GPU + Enable NULL GPU + + + + Enable PM4 Dumping + Enable PM4 Dumping + + + + Debug + Debug + + + + Enable Debug Dumping + Enable Debug Dumping + + + + Enable Vulkan Validation Layers + Enable Vulkan Validation Layers + + + + Enable Vulkan Synchronization Validation + Enable Vulkan Synchronization Validation + + + + Enable RenderDoc Debugging + Enable RenderDoc Debugging + + + + MainWindow + + + * Unsupported Vulkan Version + * 不支援的 Vulkan 版本 + + + + Download Cheats For All Installed Games + 下載所有已安裝遊戲的作弊碼 + + + + Download Patches For All Games + 下載所有遊戲的修補檔 + + + + Download Complete + 下載完成 + + + + You have downloaded cheats for all the games you have installed. + 您已經下載了所有已安裝遊戲的作弊碼。 + + + + Patches Downloaded Successfully! + 修補檔下載成功! + + + + All Patches available for all games have been downloaded. + 所有遊戲的修補檔已經下載完成。 + + + + Games: + 遊戲: + + + + PKG File (*.PKG) + PKG 檔案 (*.PKG) + + + + ELF files (*.bin *.elf *.oelf) + ELF 檔案 (*.bin *.elf *.oelf) + + + + Game Boot + 遊戲啟動 + + + + Only one file can be selected! + 只能選擇一個檔案! + + + + PKG Extraction + PKG 解壓縮 + + + + Patch detected! + 檢測到補丁! + + + + PKG and Game versions match: + PKG 和遊戲版本匹配: + + + + Would you like to overwrite? + 您想要覆蓋嗎? + + + + PKG Version %1 is older than installed version: + PKG 版本 %1 比已安裝版本更舊: + + + + Game is installed: + 遊戲已安裝: + + + + Would you like to install Patch: + 您想要安裝補丁嗎: + + + + DLC Installation + DLC 安裝 + + + + Would you like to install DLC: %1? + 您想要安裝 DLC: %1 嗎? + + + + DLC already installed: + DLC 已經安裝: + + + + Game already installed + 遊戲已經安裝 + + + + PKG is a patch, please install the game first! + PKG 是修補檔,請先安裝遊戲! + + + + PKG ERROR + PKG 錯誤 + + + + Extracting PKG %1/%2 + 正在解壓縮 PKG %1/%2 + + + + Extraction Finished + 解壓縮完成 + + + + Game successfully installed at %1 + 遊戲成功安裝於 %1 + + + + File doesn't appear to be a valid PKG file + 檔案似乎不是有效的 PKG 檔案 + + + + CheatsPatches + + + Cheats / Patches + 作弊碼 / 修補檔 + + + + defaultTextEdit_MSG + 作弊/補丁為實驗性功能。\n請小心使用。\n\n透過選擇儲存庫並點擊下載按鈕來單獨下載作弊程式。\n在“補丁”標籤頁中,您可以一次下載所有補丁,選擇要使用的補丁並保存您的選擇。\n\n由於我們不開發作弊/補丁,\n請將問題報告給作弊程式的作者。\n\n創建了新的作弊程式?請訪問:\nhttps://github.com/shadps4-emu/ps4_cheats + + + + No Image Available + 沒有可用的圖片 + + + + Serial: + 序號: + + + + Version: + 版本: + + + + Size: + 大小: + + + + Select Cheat File: + 選擇作弊檔案: + + + + Repository: + 儲存庫: + + + + Download Cheats + 下載作弊碼 + + + + Delete File + 刪除檔案 + + + + No files selected. + 沒有選擇檔案。 + + + + You can delete the cheats you don't want after downloading them. + 您可以在下載後刪除不需要的作弊碼。 + + + + Do you want to delete the selected file?\n%1 + 您是否要刪除選定的檔案?\n%1 + + + + Select Patch File: + 選擇修補檔案: + + + + Download Patches + 下載修補檔 + + + + Save + 儲存 + + + + Cheats + 作弊碼 + + + + Patches + 修補檔 + + + + Error + 錯誤 + + + + No patch selected. + 未選擇修補檔。 + + + + Unable to open files.json for reading. + 無法打開 files.json 進行讀取。 + + + + No patch file found for the current serial. + 找不到當前序號的修補檔。 + + + + Unable to open the file for reading. + 無法打開檔案進行讀取。 + + + + Unable to open the file for writing. + 無法打開檔案進行寫入。 + + + + Failed to parse XML: + 解析 XML 失敗: + + + + Success + 成功 + + + + Options saved successfully. + 選項已成功儲存。 + + + + Invalid Source + 無效的來源 + + + + The selected source is invalid. + 選擇的來源無效。 + + + + File Exists + 檔案已存在 + + + + File already exists. Do you want to replace it? + 檔案已存在。您是否希望替換它? + + + + Failed to save file: + 無法儲存檔案: + + + + Failed to download file: + 無法下載檔案: + + + + Cheats Not Found + 未找到作弊碼 + + + + CheatsNotFound_MSG + 在此版本的儲存庫中未找到該遊戲的作弊碼,請嘗試另一個儲存庫或不同版本的遊戲。 + + + + Cheats Downloaded Successfully + 作弊碼下載成功 + + + + CheatsDownloadedSuccessfully_MSG + 您已成功下載該遊戲版本的作弊碼 從選定的儲存庫中。 您可以嘗試從其他儲存庫下載,如果可用,您也可以選擇從列表中選擇檔案來使用它。 + + + + Failed to save: + 儲存失敗: + + + + Failed to download: + 下載失敗: + + + + Download Complete + 下載完成 + + + + DownloadComplete_MSG + 修補檔下載成功!所有遊戲的修補檔已下載完成,無需像作弊碼那樣為每個遊戲單獨下載。 + + + + Failed to parse JSON data from HTML. + 無法從 HTML 解析 JSON 數據。 + + + + Failed to retrieve HTML page. + 無法檢索 HTML 頁面。 + + + + Failed to open file: + 無法打開檔案: + + + + XML ERROR: + XML 錯誤: + + + + Failed to open files.json for writing + 無法打開 files.json 進行寫入 + + + + Author: + 作者: + + + + Directory does not exist: + 目錄不存在: + + + + Failed to open files.json for reading. + 無法打開 files.json 進行讀取。 + + + + Name: + 名稱: + + + \ No newline at end of file diff --git a/src/qt_gui/trophy_viewer.cpp b/src/qt_gui/trophy_viewer.cpp index bb17dfc3a..57dce6b4e 100644 --- a/src/qt_gui/trophy_viewer.cpp +++ b/src/qt_gui/trophy_viewer.cpp @@ -5,7 +5,7 @@ #include "trophy_viewer.h" TrophyViewer::TrophyViewer(QString trophyPath, QString gameTrpPath) : QMainWindow() { - this->setWindowTitle("Trophy Viewer"); + this->setWindowTitle(tr("Trophy Viewer")); this->setAttribute(Qt::WA_DeleteOnClose); tabWidget = new QTabWidget(this); gameTrpPath_ = gameTrpPath; diff --git a/src/qt_gui/trophy_viewer.h b/src/qt_gui/trophy_viewer.h index 2b794593f..81b9b1adc 100644 --- a/src/qt_gui/trophy_viewer.h +++ b/src/qt_gui/trophy_viewer.h @@ -10,11 +10,9 @@ #include #include #include -#include #include #include #include -#include #include #include "common/types.h" diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index 9fd596699..b83afd299 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -98,6 +98,11 @@ void WindowSDL::waitEvent() { case SDL_EVENT_GAMEPAD_BUTTON_DOWN: case SDL_EVENT_GAMEPAD_BUTTON_UP: case SDL_EVENT_GAMEPAD_AXIS_MOTION: + case SDL_EVENT_GAMEPAD_ADDED: + case SDL_EVENT_GAMEPAD_REMOVED: + case SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN: + case SDL_EVENT_GAMEPAD_TOUCHPAD_UP: + case SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION: onGamepadEvent(&event); break; case SDL_EVENT_QUIT: @@ -273,6 +278,15 @@ void WindowSDL::onKeyPress(const SDL_Event* event) { case SDLK_SPACE: button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_TOUCH_PAD; break; + case SDLK_F11: + if (event->type == SDL_EVENT_KEY_DOWN) { + { + SDL_WindowFlags flag = SDL_GetWindowFlags(window); + bool is_fullscreen = flag & SDL_WINDOW_FULLSCREEN; + SDL_SetWindowFullscreen(window, !is_fullscreen); + } + } + break; default: break; } @@ -290,6 +304,17 @@ void WindowSDL::onGamepadEvent(const SDL_Event* event) { u32 button = 0; Input::Axis axis = Input::Axis::AxisMax; switch (event->type) { + case SDL_EVENT_GAMEPAD_ADDED: + case SDL_EVENT_GAMEPAD_REMOVED: + controller->TryOpenSDLController(); + break; + case SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN: + case SDL_EVENT_GAMEPAD_TOUCHPAD_UP: + case SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION: + controller->SetTouchpadState(event->gtouchpad.finger, + event->type != SDL_EVENT_GAMEPAD_TOUCHPAD_UP, + event->gtouchpad.x, event->gtouchpad.y); + break; case SDL_EVENT_GAMEPAD_BUTTON_DOWN: case SDL_EVENT_GAMEPAD_BUTTON_UP: button = sdlGamepadToOrbisButton(event->gbutton.button); @@ -306,7 +331,13 @@ void WindowSDL::onGamepadEvent(const SDL_Event* event) { : event->gaxis.axis == SDL_GAMEPAD_AXIS_RIGHT_TRIGGER ? Input::Axis::TriggerRight : Input::Axis::AxisMax; if (axis != Input::Axis::AxisMax) { - controller->Axis(0, axis, Input::GetAxis(-0x8000, 0x8000, event->gaxis.value)); + if (event->gaxis.axis == SDL_GAMEPAD_AXIS_LEFT_TRIGGER || + event->gaxis.axis == SDL_GAMEPAD_AXIS_RIGHT_TRIGGER) { + controller->Axis(0, axis, Input::GetAxis(0, 0x8000, event->gaxis.value)); + + } else { + controller->Axis(0, axis, Input::GetAxis(-0x8000, 0x8000, event->gaxis.value)); + } } break; } diff --git a/src/sdl_window.h b/src/sdl_window.h index cf6c37116..11ee92896 100644 --- a/src/sdl_window.h +++ b/src/sdl_window.h @@ -25,9 +25,6 @@ enum class WindowSystemType : u8 { }; struct WindowSystemInfo { - // Window system type. Determines which GL context or Vulkan WSI is used. - WindowSystemType type = WindowSystemType::Headless; - // Connection to a display server. This is used on X11 and Wayland platforms. void* display_connection = nullptr; @@ -38,6 +35,9 @@ struct WindowSystemInfo { // Scale of the render surface. For hidpi systems, this will be >1. float render_surface_scale = 1.0f; + + // Window system type. Determines which GL context or Vulkan WSI is used. + WindowSystemType type = WindowSystemType::Headless; }; class WindowSDL { diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp index c70427635..98eac0819 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp @@ -99,7 +99,7 @@ Id TypeId(const EmitContext& ctx, IR::Type type) { } } -void Traverse(EmitContext& ctx, IR::Program& program) { +void Traverse(EmitContext& ctx, const IR::Program& program) { IR::Block* current_block{}; for (const IR::AbstractSyntaxNode& node : program.syntax_list) { switch (node.type) { @@ -162,7 +162,7 @@ void Traverse(EmitContext& ctx, IR::Program& program) { } } -Id DefineMain(EmitContext& ctx, IR::Program& program) { +Id DefineMain(EmitContext& ctx, const IR::Program& program) { const Id void_function{ctx.TypeFunction(ctx.void_id)}; const Id main{ctx.OpFunction(ctx.void_id, spv::FunctionControlMask::MaskNone, void_function)}; for (IR::Block* const block : program.blocks) { @@ -179,13 +179,34 @@ void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) { spv::ExecutionModel execution_model{}; ctx.AddCapability(spv::Capability::Image1D); ctx.AddCapability(spv::Capability::Sampled1D); + ctx.AddCapability(spv::Capability::ImageQuery); if (info.uses_fp16) { ctx.AddCapability(spv::Capability::Float16); ctx.AddCapability(spv::Capability::Int16); } ctx.AddCapability(spv::Capability::Int64); - if (info.has_storage_images) { + if (info.has_storage_images || info.has_image_buffers) { ctx.AddCapability(spv::Capability::StorageImageExtendedFormats); + ctx.AddCapability(spv::Capability::StorageImageReadWithoutFormat); + ctx.AddCapability(spv::Capability::StorageImageWriteWithoutFormat); + } + if (info.has_texel_buffers) { + ctx.AddCapability(spv::Capability::SampledBuffer); + } + if (info.has_image_buffers) { + ctx.AddCapability(spv::Capability::ImageBuffer); + } + if (info.has_image_gather) { + ctx.AddCapability(spv::Capability::ImageGatherExtended); + } + if (info.has_image_query) { + ctx.AddCapability(spv::Capability::ImageQuery); + } + if (info.uses_lane_id) { + ctx.AddCapability(spv::Capability::GroupNonUniform); + } + if (info.uses_group_quad) { + ctx.AddCapability(spv::Capability::GroupNonUniformQuad); } switch (program.info.stage) { case Stage::Compute: { @@ -205,19 +226,9 @@ void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) { } else { ctx.AddExecutionMode(main, spv::ExecutionMode::OriginUpperLeft); } - ctx.AddCapability(spv::Capability::GroupNonUniform); - if (info.uses_group_quad) { - ctx.AddCapability(spv::Capability::GroupNonUniformQuad); - } if (info.has_discard) { ctx.AddCapability(spv::Capability::DemoteToHelperInvocationEXT); } - if (info.has_image_gather) { - ctx.AddCapability(spv::Capability::ImageGatherExtended); - } - if (info.has_image_query) { - ctx.AddCapability(spv::Capability::ImageQuery); - } if (info.stores.Get(IR::Attribute::Depth)) { ctx.AddExecutionMode(main, spv::ExecutionMode::DepthReplacing); } @@ -228,7 +239,7 @@ void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) { ctx.AddEntryPoint(execution_model, main, "main", interfaces); } -void PatchPhiNodes(IR::Program& program, EmitContext& ctx) { +void PatchPhiNodes(const IR::Program& program, EmitContext& ctx) { auto inst{program.blocks.front()->begin()}; size_t block_index{0}; ctx.PatchDeferredPhi([&](size_t phi_arg) { @@ -247,8 +258,8 @@ void PatchPhiNodes(IR::Program& program, EmitContext& ctx) { } } // Anonymous namespace -std::vector EmitSPIRV(const Profile& profile, IR::Program& program, u32& binding) { - EmitContext ctx{profile, program, binding}; +std::vector EmitSPIRV(const Profile& profile, const IR::Program& program, u32& binding) { + EmitContext ctx{profile, program.info, binding}; const Id main{DefineMain(ctx, program)}; DefineEntryPoint(program, ctx, main); if (program.info.stage == Stage::Vertex) { diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.h b/src/shader_recompiler/backend/spirv/emit_spirv.h index e513975b3..4c862185f 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv.h @@ -9,7 +9,7 @@ namespace Shader::Backend::SPIRV { -[[nodiscard]] std::vector EmitSPIRV(const Profile& profile, IR::Program& program, +[[nodiscard]] std::vector EmitSPIRV(const Profile& profile, const IR::Program& program, u32& binding); } // namespace Shader::Backend::SPIRV diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp index 37e91d3b1..1d553dc56 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp @@ -102,7 +102,7 @@ Id EmitBufferAtomicXor32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id addres return BufferAtomicU32(ctx, inst, handle, address, value, &Sirit::Module::OpAtomicXor); } -Id EmitBufferAtomicExchange32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) { +Id EmitBufferAtomicSwap32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) { return BufferAtomicU32(ctx, inst, handle, address, value, &Sirit::Module::OpAtomicExchange); } diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp index 0b02f3a37..7bdc98de9 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp @@ -262,171 +262,16 @@ Id EmitLoadBufferF32x4(EmitContext& ctx, IR::Inst*, u32 handle, Id address) { return EmitLoadBufferF32xN<4>(ctx, handle, address); } -static bool IsSignedInteger(AmdGpu::NumberFormat format) { - switch (format) { - case AmdGpu::NumberFormat::Unorm: - case AmdGpu::NumberFormat::Uscaled: - case AmdGpu::NumberFormat::Uint: - return false; - case AmdGpu::NumberFormat::Snorm: - case AmdGpu::NumberFormat::Sscaled: - case AmdGpu::NumberFormat::Sint: - case AmdGpu::NumberFormat::SnormNz: - return true; - case AmdGpu::NumberFormat::Float: - default: - UNREACHABLE(); - } -} - -static u32 UXBitsMax(u32 bit_width) { - return (1u << bit_width) - 1u; -} - -static u32 SXBitsMax(u32 bit_width) { - return (1u << (bit_width - 1u)) - 1u; -} - -static Id ConvertValue(EmitContext& ctx, Id value, AmdGpu::NumberFormat format, u32 bit_width) { - switch (format) { - case AmdGpu::NumberFormat::Unorm: - return ctx.OpFDiv(ctx.F32[1], value, ctx.ConstF32(float(UXBitsMax(bit_width)))); - case AmdGpu::NumberFormat::Snorm: - return ctx.OpFDiv(ctx.F32[1], value, ctx.ConstF32(float(SXBitsMax(bit_width)))); - case AmdGpu::NumberFormat::SnormNz: - // (x * 2 + 1) / (Format::SMAX * 2) - value = ctx.OpFMul(ctx.F32[1], value, ctx.ConstF32(2.f)); - value = ctx.OpFAdd(ctx.F32[1], value, ctx.ConstF32(1.f)); - return ctx.OpFDiv(ctx.F32[1], value, ctx.ConstF32(float(SXBitsMax(bit_width) * 2))); - case AmdGpu::NumberFormat::Uscaled: - case AmdGpu::NumberFormat::Sscaled: - case AmdGpu::NumberFormat::Uint: - case AmdGpu::NumberFormat::Sint: - case AmdGpu::NumberFormat::Float: - return value; - default: - UNREACHABLE_MSG("Unsupported number fromat for conversion: {}", - magic_enum::enum_name(format)); - } -} - -static Id ComponentOffset(EmitContext& ctx, Id address, u32 stride, u32 bit_offset) { - Id comp_offset = ctx.ConstU32(bit_offset); - if (stride < 4) { - // comp_offset += (address % 4) * 8; - const Id byte_offset = ctx.OpUMod(ctx.U32[1], address, ctx.ConstU32(4u)); - const Id bit_offset = ctx.OpShiftLeftLogical(ctx.U32[1], byte_offset, ctx.ConstU32(3u)); - comp_offset = ctx.OpIAdd(ctx.U32[1], comp_offset, bit_offset); - } - return comp_offset; -} - -static Id GetBufferFormatValue(EmitContext& ctx, u32 handle, Id address, u32 comp) { - auto& buffer = ctx.buffers[handle]; - const auto format = buffer.dfmt; - switch (format) { - case AmdGpu::DataFormat::FormatInvalid: - return ctx.f32_zero_value; - case AmdGpu::DataFormat::Format8: - case AmdGpu::DataFormat::Format16: - case AmdGpu::DataFormat::Format32: - case AmdGpu::DataFormat::Format8_8: - case AmdGpu::DataFormat::Format16_16: - case AmdGpu::DataFormat::Format10_11_11: - case AmdGpu::DataFormat::Format11_11_10: - case AmdGpu::DataFormat::Format10_10_10_2: - case AmdGpu::DataFormat::Format2_10_10_10: - case AmdGpu::DataFormat::Format8_8_8_8: - case AmdGpu::DataFormat::Format32_32: - case AmdGpu::DataFormat::Format16_16_16_16: - case AmdGpu::DataFormat::Format32_32_32: - case AmdGpu::DataFormat::Format32_32_32_32: { - const u32 num_components = AmdGpu::NumComponents(format); - if (comp >= num_components) { - return ctx.f32_zero_value; - } - - // uint index = address / 4; - Id index = ctx.OpShiftRightLogical(ctx.U32[1], address, ctx.ConstU32(2u)); - const u32 stride = buffer.stride; - if (stride > 4) { - const u32 index_offset = u32(AmdGpu::ComponentOffset(format, comp) / 32); - if (index_offset > 0) { - // index += index_offset; - index = ctx.OpIAdd(ctx.U32[1], index, ctx.ConstU32(index_offset)); - } - } - const Id ptr = ctx.OpAccessChain(buffer.pointer_type, buffer.id, ctx.u32_zero_value, index); - - const u32 bit_offset = AmdGpu::ComponentOffset(format, comp) % 32; - const u32 bit_width = AmdGpu::ComponentBits(format, comp); - const auto num_format = buffer.nfmt; - if (num_format == AmdGpu::NumberFormat::Float) { - if (bit_width == 32) { - return ctx.OpLoad(ctx.F32[1], ptr); - } else if (bit_width == 16) { - const Id comp_offset = ComponentOffset(ctx, address, stride, bit_offset); - Id value = ctx.OpLoad(ctx.U32[1], ptr); - value = - ctx.OpBitFieldSExtract(ctx.S32[1], value, comp_offset, ctx.ConstU32(bit_width)); - value = ctx.OpSConvert(ctx.U16, value); - value = ctx.OpBitcast(ctx.F16[1], value); - return ctx.OpFConvert(ctx.F32[1], value); - } else { - UNREACHABLE_MSG("Invalid float bit width {}", bit_width); - } - } else { - Id value = ctx.OpLoad(ctx.U32[1], ptr); - const bool is_signed = IsSignedInteger(num_format); - if (bit_width < 32) { - const Id comp_offset = ComponentOffset(ctx, address, stride, bit_offset); - if (is_signed) { - value = ctx.OpBitFieldSExtract(ctx.S32[1], value, comp_offset, - ctx.ConstU32(bit_width)); - } else { - value = ctx.OpBitFieldUExtract(ctx.U32[1], value, comp_offset, - ctx.ConstU32(bit_width)); - } - } - value = ctx.OpBitcast(ctx.F32[1], value); - return ConvertValue(ctx, value, num_format, bit_width); - } - break; - } - default: - UNREACHABLE_MSG("Invalid format for conversion: {}", magic_enum::enum_name(format)); - } -} - -template -static Id EmitLoadBufferFormatF32xN(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) { - auto& buffer = ctx.buffers[handle]; - address = ctx.OpIAdd(ctx.U32[1], address, buffer.offset); - if constexpr (N == 1) { - return GetBufferFormatValue(ctx, handle, address, 0); - } else { - boost::container::static_vector ids; - for (u32 i = 0; i < N; i++) { - ids.push_back(GetBufferFormatValue(ctx, handle, address, i)); - } - return ctx.OpCompositeConstruct(ctx.F32[N], ids); - } -} - Id EmitLoadBufferFormatF32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) { - return EmitLoadBufferFormatF32xN<1>(ctx, inst, handle, address); -} - -Id EmitLoadBufferFormatF32x2(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) { - return EmitLoadBufferFormatF32xN<2>(ctx, inst, handle, address); -} - -Id EmitLoadBufferFormatF32x3(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) { - return EmitLoadBufferFormatF32xN<3>(ctx, inst, handle, address); -} - -Id EmitLoadBufferFormatF32x4(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) { - return EmitLoadBufferFormatF32xN<4>(ctx, inst, handle, address); + const auto& buffer = ctx.texture_buffers[handle]; + const Id tex_buffer = ctx.OpLoad(buffer.image_type, buffer.id); + const Id coord = ctx.OpIAdd(ctx.U32[1], address, buffer.coord_offset); + Id texel = buffer.is_storage ? ctx.OpImageRead(buffer.result_type, tex_buffer, coord) + : ctx.OpImageFetch(buffer.result_type, tex_buffer, coord); + if (buffer.is_integer) { + texel = ctx.OpBitcast(ctx.F32[4], texel); + } + return texel; } template @@ -467,97 +312,14 @@ void EmitStoreBufferU32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address EmitStoreBufferF32xN<1>(ctx, handle, address, value); } -static Id ConvertF32ToFormat(EmitContext& ctx, Id value, AmdGpu::NumberFormat format, - u32 bit_width) { - switch (format) { - case AmdGpu::NumberFormat::Unorm: - return ctx.OpConvertFToU( - ctx.U32[1], ctx.OpFMul(ctx.F32[1], value, ctx.ConstF32(float(UXBitsMax(bit_width))))); - case AmdGpu::NumberFormat::Uint: - return ctx.OpBitcast(ctx.U32[1], value); - case AmdGpu::NumberFormat::Float: - return value; - default: - UNREACHABLE_MSG("Unsupported number fromat for conversion: {}", - magic_enum::enum_name(format)); - } -} - -template -static void EmitStoreBufferFormatF32xN(EmitContext& ctx, u32 handle, Id address, Id value) { - auto& buffer = ctx.buffers[handle]; - const auto format = buffer.dfmt; - const auto num_format = buffer.nfmt; - - switch (format) { - case AmdGpu::DataFormat::FormatInvalid: - return; - case AmdGpu::DataFormat::Format8_8_8_8: - case AmdGpu::DataFormat::Format16: - case AmdGpu::DataFormat::Format32: - case AmdGpu::DataFormat::Format32_32: - case AmdGpu::DataFormat::Format32_32_32_32: { - ASSERT(N == AmdGpu::NumComponents(format)); - - address = ctx.OpIAdd(ctx.U32[1], address, buffer.offset); - const Id index = ctx.OpShiftRightLogical(ctx.U32[1], address, ctx.ConstU32(2u)); - const Id ptr = ctx.OpAccessChain(buffer.pointer_type, buffer.id, ctx.u32_zero_value, index); - - Id packed_value{}; - for (u32 i = 0; i < N; i++) { - const u32 bit_width = AmdGpu::ComponentBits(format, i); - const u32 bit_offset = AmdGpu::ComponentOffset(format, i) % 32; - - const Id comp{ConvertF32ToFormat( - ctx, N == 1 ? value : ctx.OpCompositeExtract(ctx.F32[1], value, i), num_format, - bit_width)}; - - if (bit_width == 32) { - if constexpr (N == 1) { - ctx.OpStore(ptr, comp); - } else { - const Id index_i = ctx.OpIAdd(ctx.U32[1], index, ctx.ConstU32(i)); - const Id ptr = ctx.OpAccessChain(buffer.pointer_type, buffer.id, - ctx.u32_zero_value, index_i); - ctx.OpStore(ptr, comp); - } - } else { - if (i == 0) { - packed_value = comp; - } else { - packed_value = - ctx.OpBitFieldInsert(ctx.U32[1], packed_value, comp, - ctx.ConstU32(bit_offset), ctx.ConstU32(bit_width)); - } - - if (i == N - 1) { - ctx.OpStore(ptr, packed_value); - } - } - } - } break; - default: - UNREACHABLE_MSG("Invalid format for conversion: {}", magic_enum::enum_name(format)); - } -} - void EmitStoreBufferFormatF32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) { - EmitStoreBufferFormatF32xN<1>(ctx, handle, address, value); -} - -void EmitStoreBufferFormatF32x2(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, - Id value) { - EmitStoreBufferFormatF32xN<2>(ctx, handle, address, value); -} - -void EmitStoreBufferFormatF32x3(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, - Id value) { - EmitStoreBufferFormatF32xN<3>(ctx, handle, address, value); -} - -void EmitStoreBufferFormatF32x4(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, - Id value) { - EmitStoreBufferFormatF32xN<4>(ctx, handle, address, value); + const auto& buffer = ctx.texture_buffers[handle]; + const Id tex_buffer = ctx.OpLoad(buffer.image_type, buffer.id); + const Id coord = ctx.OpIAdd(ctx.U32[1], address, buffer.coord_offset); + if (buffer.is_integer) { + value = ctx.OpBitcast(ctx.U32[4], value); + } + ctx.OpImageWrite(tex_buffer, coord, value); } } // namespace Shader::Backend::SPIRV diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp index 5526e5411..530f381d7 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp @@ -16,6 +16,12 @@ struct ImageOperands { static_cast(new_mask)); operands.push_back(value); } + void Add(spv::ImageOperandsMask new_mask, Id value1, Id value2) { + mask = static_cast(static_cast(mask) | + static_cast(new_mask)); + operands.push_back(value1); + operands.push_back(value2); + } void AddOffset(EmitContext& ctx, const IR::Value& offset, bool can_use_runtime_offsets = false) { @@ -53,6 +59,15 @@ struct ImageOperands { } } + void AddDerivatives(EmitContext& ctx, Id derivatives) { + if (!Sirit::ValidId(derivatives)) { + return; + } + const Id dx{ctx.OpVectorShuffle(ctx.F32[2], derivatives, derivatives, 0, 1)}; + const Id dy{ctx.OpVectorShuffle(ctx.F32[2], derivatives, derivatives, 2, 3)}; + Add(spv::ImageOperandsMask::Grad, dx, dy); + } + spv::ImageOperandsMask mask{}; boost::container::static_vector operands; }; @@ -117,7 +132,7 @@ Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, const Id sampled_image = ctx.OpSampledImage(texture.sampled_type, image, sampler); const u32 comp = inst->Flags().gather_comp.Value(); ImageOperands operands; - operands.AddOffset(ctx, offset); + operands.AddOffset(ctx, offset, true); return ctx.OpImageGather(ctx.F32[4], sampled_image, coords, ctx.ConstU32(comp), operands.mask, operands.operands); } @@ -129,7 +144,7 @@ Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, const Id sampler = ctx.OpLoad(ctx.sampler_type, ctx.samplers[handle >> 16]); const Id sampled_image = ctx.OpSampledImage(texture.sampled_type, image, sampler); ImageOperands operands; - operands.AddOffset(ctx, offset); + operands.AddOffset(ctx, offset, true); return ctx.OpImageDrefGather(ctx.F32[4], sampled_image, coords, dref, operands.mask, operands.operands); } @@ -181,9 +196,17 @@ Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords) { return ctx.OpImageQueryLod(ctx.F32[2], sampled_image, coords); } -Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, - Id derivatives, const IR::Value& offset, Id lod_clamp) { - UNREACHABLE_MSG("SPIR-V Instruction"); +Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id derivatives, + const IR::Value& offset, Id lod_clamp) { + const auto& texture = ctx.images[handle & 0xFFFF]; + const Id image = ctx.OpLoad(texture.image_type, texture.id); + const Id sampler = ctx.OpLoad(ctx.sampler_type, ctx.samplers[handle >> 16]); + const Id sampled_image = ctx.OpSampledImage(texture.sampled_type, image, sampler); + ImageOperands operands; + operands.AddDerivatives(ctx, derivatives); + operands.AddOffset(ctx, offset); + return ctx.OpImageSampleExplicitLod(ctx.F32[4], sampled_image, coords, operands.mask, + operands.operands); } Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords) { diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h index bc39bc0f3..ce4d3f137 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h @@ -91,7 +91,7 @@ Id EmitBufferAtomicDec32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id addres Id EmitBufferAtomicAnd32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value); Id EmitBufferAtomicOr32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value); Id EmitBufferAtomicXor32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value); -Id EmitBufferAtomicExchange32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value); +Id EmitBufferAtomicSwap32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value); Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, u32 comp); Id EmitGetAttributeU32(EmitContext& ctx, IR::Attribute attr, u32 comp); void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value, u32 comp); @@ -286,6 +286,7 @@ Id EmitShiftRightLogical64(EmitContext& ctx, Id base, Id shift); Id EmitShiftRightArithmetic32(EmitContext& ctx, Id base, Id shift); Id EmitShiftRightArithmetic64(EmitContext& ctx, Id base, Id shift); Id EmitBitwiseAnd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b); +Id EmitBitwiseAnd64(EmitContext& ctx, IR::Inst* inst, Id a, Id b); Id EmitBitwiseOr32(EmitContext& ctx, IR::Inst* inst, Id a, Id b); Id EmitBitwiseOr64(EmitContext& ctx, IR::Inst* inst, Id a, Id b); Id EmitBitwiseXor32(EmitContext& ctx, IR::Inst* inst, Id a, Id b); @@ -387,8 +388,8 @@ Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, const Id lod, Id ms); Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, u32 handle, Id lod, bool skip_mips); Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords); -Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, - Id derivatives, const IR::Value& offset, Id lod_clamp); +Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id derivatives, + const IR::Value& offset, Id lod_clamp); Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords); void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id color); @@ -407,5 +408,8 @@ Id EmitImageAtomicExchange32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id co Id EmitLaneId(EmitContext& ctx); Id EmitWarpId(EmitContext& ctx); Id EmitQuadShuffle(EmitContext& ctx, Id value, Id index); +Id EmitReadFirstLane(EmitContext& ctx, Id value); +Id EmitReadLane(EmitContext& ctx, Id value, u32 lane); +Id EmitWriteLane(EmitContext& ctx, Id value, Id write_value, u32 lane); } // namespace Shader::Backend::SPIRV diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp index f20c4fac2..a9becb1eb 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp @@ -139,6 +139,13 @@ Id EmitBitwiseAnd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) { return result; } +Id EmitBitwiseAnd64(EmitContext& ctx, IR::Inst* inst, Id a, Id b) { + const Id result{ctx.OpBitwiseAnd(ctx.U64, a, b)}; + SetZeroFlag(ctx, inst, result); + SetSignFlag(ctx, inst, result); + return result; +} + Id EmitBitwiseOr32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) { const Id result{ctx.OpBitwiseOr(ctx.U32[1], a, b)}; SetZeroFlag(ctx, inst, result); diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp index 38afd90f1..898de8b57 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp @@ -22,4 +22,16 @@ Id EmitQuadShuffle(EmitContext& ctx, Id value, Id index) { return ctx.OpGroupNonUniformQuadBroadcast(ctx.U32[1], SubgroupScope(ctx), value, index); } +Id EmitReadFirstLane(EmitContext& ctx, Id value) { + return ctx.OpGroupNonUniformBroadcastFirst(ctx.U32[1], SubgroupScope(ctx), value); +} + +Id EmitReadLane(EmitContext& ctx, Id value, u32 lane) { + UNREACHABLE(); +} + +Id EmitWriteLane(EmitContext& ctx, Id value, Id write_value, u32 lane) { + return ctx.u32_zero_value; +} + } // namespace Shader::Backend::SPIRV diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index d61e108f6..51315139f 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -41,13 +41,14 @@ void Name(EmitContext& ctx, Id object, std::string_view format_str, Args&&... ar } // Anonymous namespace -EmitContext::EmitContext(const Profile& profile_, IR::Program& program, u32& binding_) - : Sirit::Module(profile_.supported_spirv), info{program.info}, profile{profile_}, - stage{program.info.stage}, binding{binding_} { +EmitContext::EmitContext(const Profile& profile_, const Shader::Info& info_, u32& binding_) + : Sirit::Module(profile_.supported_spirv), info{info_}, profile{profile_}, stage{info.stage}, + binding{binding_} { AddCapability(spv::Capability::Shader); DefineArithmeticTypes(); DefineInterfaces(); DefineBuffers(); + DefineTextureBuffers(); DefineImagesAndSamplers(); DefineSharedMemory(); } @@ -123,25 +124,24 @@ void EmitContext::DefineInterfaces() { DefineOutputs(); } -Id GetAttributeType(EmitContext& ctx, AmdGpu::NumberFormat fmt) { +const VectorIds& GetAttributeType(EmitContext& ctx, AmdGpu::NumberFormat fmt) { switch (fmt) { case AmdGpu::NumberFormat::Float: case AmdGpu::NumberFormat::Unorm: case AmdGpu::NumberFormat::Snorm: case AmdGpu::NumberFormat::SnormNz: - return ctx.F32[4]; - case AmdGpu::NumberFormat::Sint: - return ctx.S32[4]; - case AmdGpu::NumberFormat::Uint: - return ctx.U32[4]; case AmdGpu::NumberFormat::Sscaled: - return ctx.F32[4]; case AmdGpu::NumberFormat::Uscaled: - return ctx.F32[4]; + case AmdGpu::NumberFormat::Srgb: + return ctx.F32; + case AmdGpu::NumberFormat::Sint: + return ctx.S32; + case AmdGpu::NumberFormat::Uint: + return ctx.U32; default: break; } - throw InvalidArgument("Invalid attribute type {}", fmt); + UNREACHABLE_MSG("Invalid attribute type {}", fmt); } EmitContext::SpirvAttribute EmitContext::GetAttributeInfo(AmdGpu::NumberFormat fmt, Id id) { @@ -162,7 +162,7 @@ EmitContext::SpirvAttribute EmitContext::GetAttributeInfo(AmdGpu::NumberFormat f default: break; } - throw InvalidArgument("Invalid attribute type {}", fmt); + UNREACHABLE_MSG("Invalid attribute type {}", fmt); } void EmitContext::DefineBufferOffsets() { @@ -177,6 +177,16 @@ void EmitContext::DefineBufferOffsets() { buffer.offset = OpBitFieldUExtract(U32[1], value, ConstU32(offset), ConstU32(8U)); buffer.offset_dwords = OpShiftRightLogical(U32[1], buffer.offset, ConstU32(2U)); } + for (auto& tex_buffer : texture_buffers) { + const u32 binding = tex_buffer.binding; + const u32 half = Shader::PushData::BufOffsetIndex + (binding >> 4); + const u32 comp = (binding & 0xf) >> 2; + const u32 offset = (binding & 0x3) << 3; + const Id ptr{OpAccessChain(TypePointer(spv::StorageClass::PushConstant, U32[1]), + push_data_block, ConstU32(half), ConstU32(comp))}; + const Id value{OpLoad(U32[1], ptr)}; + tex_buffer.coord_offset = OpBitFieldUExtract(U32[1], value, ConstU32(offset), ConstU32(8U)); + } } Id MakeDefaultValue(EmitContext& ctx, u32 default_value) { @@ -195,6 +205,11 @@ Id MakeDefaultValue(EmitContext& ctx, u32 default_value) { } void EmitContext::DefineInputs() { + if (info.uses_lane_id) { + subgroup_local_invocation_id = DefineVariable( + U32[1], spv::BuiltIn::SubgroupLocalInvocationId, spv::StorageClass::Input); + Decorate(subgroup_local_invocation_id, spv::Decoration::Flat); + } switch (stage) { case Stage::Vertex: { vertex_index = DefineVariable(U32[1], spv::BuiltIn::VertexIndex, spv::StorageClass::Input); @@ -202,7 +217,7 @@ void EmitContext::DefineInputs() { instance_id = DefineVariable(U32[1], spv::BuiltIn::InstanceIndex, spv::StorageClass::Input); for (const auto& input : info.vs_inputs) { - const Id type{GetAttributeType(*this, input.fmt)}; + const Id type{GetAttributeType(*this, input.fmt)[4]}; if (input.instance_step_rate == Info::VsInput::InstanceIdType::OverStepRate0 || input.instance_step_rate == Info::VsInput::InstanceIdType::OverStepRate1) { @@ -229,15 +244,12 @@ void EmitContext::DefineInputs() { break; } case Stage::Fragment: - subgroup_local_invocation_id = DefineVariable( - U32[1], spv::BuiltIn::SubgroupLocalInvocationId, spv::StorageClass::Input); - Decorate(subgroup_local_invocation_id, spv::Decoration::Flat); frag_coord = DefineVariable(F32[4], spv::BuiltIn::FragCoord, spv::StorageClass::Input); frag_depth = DefineVariable(F32[1], spv::BuiltIn::FragDepth, spv::StorageClass::Output); front_facing = DefineVariable(U1[1], spv::BuiltIn::FrontFacing, spv::StorageClass::Input); for (const auto& input : info.ps_inputs) { const u32 semantic = input.param_index; - if (input.is_default) { + if (input.is_default && !input.is_flat) { input_params[semantic] = {MakeDefaultValue(*this, input.default_value), F32[1], F32[1], 4, true}; continue; @@ -328,47 +340,75 @@ void EmitContext::DefinePushDataBlock() { void EmitContext::DefineBuffers() { boost::container::small_vector type_ids; - for (u32 i = 0; const auto& buffer : info.buffers) { - const auto* data_types = True(buffer.used_types & IR::Type::F32) ? &F32 : &U32; - const Id data_type = (*data_types)[1]; - const Id record_array_type{buffer.is_storage - ? TypeRuntimeArray(data_type) - : TypeArray(data_type, ConstU32(buffer.length))}; + const auto define_struct = [&](Id record_array_type, bool is_instance_data) { const Id struct_type{TypeStruct(record_array_type)}; - if (std::ranges::find(type_ids, record_array_type.value, &Id::value) == type_ids.end()) { - Decorate(record_array_type, spv::Decoration::ArrayStride, 4); - const auto name = - buffer.is_instance_data - ? fmt::format("{}_instance_data{}_{}{}", stage, i, 'f', - sizeof(float) * CHAR_BIT) - : fmt::format("{}_cbuf_block_{}{}", stage, 'f', sizeof(float) * CHAR_BIT); - Name(struct_type, name); - Decorate(struct_type, spv::Decoration::Block); - MemberName(struct_type, 0, "data"); - MemberDecorate(struct_type, 0, spv::Decoration::Offset, 0U); - type_ids.push_back(record_array_type); + if (std::ranges::find(type_ids, record_array_type.value, &Id::value) != type_ids.end()) { + return struct_type; } + Decorate(record_array_type, spv::Decoration::ArrayStride, 4); + const auto name = is_instance_data ? fmt::format("{}_instance_data_f32", stage) + : fmt::format("{}_cbuf_block_f32", stage); + Name(struct_type, name); + Decorate(struct_type, spv::Decoration::Block); + MemberName(struct_type, 0, "data"); + MemberDecorate(struct_type, 0, spv::Decoration::Offset, 0U); + type_ids.push_back(record_array_type); + return struct_type; + }; + + for (const auto& desc : info.buffers) { + const auto sharp = desc.GetSharp(info); + const bool is_storage = desc.IsStorage(sharp); + const auto* data_types = True(desc.used_types & IR::Type::F32) ? &F32 : &U32; + const Id data_type = (*data_types)[1]; + const Id record_array_type{is_storage ? TypeRuntimeArray(data_type) + : TypeArray(data_type, ConstU32(sharp.NumDwords()))}; + const Id struct_type{define_struct(record_array_type, desc.is_instance_data)}; const auto storage_class = - buffer.is_storage ? spv::StorageClass::StorageBuffer : spv::StorageClass::Uniform; + is_storage ? spv::StorageClass::StorageBuffer : spv::StorageClass::Uniform; const Id struct_pointer_type{TypePointer(storage_class, struct_type)}; const Id pointer_type = TypePointer(storage_class, data_type); const Id id{AddGlobalVariable(struct_pointer_type, storage_class)}; Decorate(id, spv::Decoration::Binding, binding); Decorate(id, spv::Decoration::DescriptorSet, 0U); - Name(id, fmt::format("{}_{}", buffer.is_storage ? "ssbo" : "cbuf", buffer.sgpr_base)); + if (is_storage && !desc.is_written) { + Decorate(id, spv::Decoration::NonWritable); + } + Name(id, fmt::format("{}_{}", is_storage ? "ssbo" : "cbuf", desc.sgpr_base)); buffers.push_back({ .id = id, .binding = binding++, .data_types = data_types, .pointer_type = pointer_type, - .dfmt = buffer.dfmt, - .nfmt = buffer.nfmt, - .stride = buffer.GetVsharp(info).GetStride(), }); interfaces.push_back(id); - i++; + } +} + +void EmitContext::DefineTextureBuffers() { + for (const auto& desc : info.texture_buffers) { + const bool is_integer = + desc.nfmt == AmdGpu::NumberFormat::Uint || desc.nfmt == AmdGpu::NumberFormat::Sint; + const VectorIds& sampled_type{GetAttributeType(*this, desc.nfmt)}; + const u32 sampled = desc.is_written ? 2 : 1; + const Id image_type{TypeImage(sampled_type[1], spv::Dim::Buffer, false, false, false, + sampled, spv::ImageFormat::Unknown)}; + const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, image_type)}; + const Id id{AddGlobalVariable(pointer_type, spv::StorageClass::UniformConstant)}; + Decorate(id, spv::Decoration::Binding, binding); + Decorate(id, spv::Decoration::DescriptorSet, 0U); + Name(id, fmt::format("{}_{}", desc.is_written ? "imgbuf" : "texbuf", desc.sgpr_base)); + texture_buffers.push_back({ + .id = id, + .binding = binding++, + .image_type = image_type, + .result_type = sampled_type[4], + .is_integer = is_integer, + .is_storage = desc.is_written, + }); + interfaces.push_back(id); } } @@ -405,6 +445,10 @@ spv::ImageFormat GetFormat(const AmdGpu::Image& image) { image.GetNumberFmt() == AmdGpu::NumberFormat::Float) { return spv::ImageFormat::Rg16f; } + if (image.GetDataFmt() == AmdGpu::DataFormat::Format16_16 && + image.GetNumberFmt() == AmdGpu::NumberFormat::Snorm) { + return spv::ImageFormat::Rg16Snorm; + } if (image.GetDataFmt() == AmdGpu::DataFormat::Format8_8 && image.GetNumberFmt() == AmdGpu::NumberFormat::Unorm) { return spv::ImageFormat::Rg8; @@ -443,7 +487,7 @@ spv::ImageFormat GetFormat(const AmdGpu::Image& image) { Id ImageType(EmitContext& ctx, const ImageResource& desc, Id sampled_type) { const auto image = ctx.info.ReadUd(desc.sgpr_base, desc.dword_offset); - const auto format = desc.is_storage ? GetFormat(image) : spv::ImageFormat::Unknown; + const auto format = desc.is_atomic ? GetFormat(image) : spv::ImageFormat::Unknown; const u32 sampled = desc.is_storage ? 2 : 1; switch (desc.type) { case AmdGpu::ImageType::Color1D: @@ -466,17 +510,8 @@ Id ImageType(EmitContext& ctx, const ImageResource& desc, Id sampled_type) { void EmitContext::DefineImagesAndSamplers() { for (const auto& image_desc : info.images) { - const VectorIds* data_types = [&] { - switch (image_desc.nfmt) { - case AmdGpu::NumberFormat::Uint: - return &U32; - case AmdGpu::NumberFormat::Sint: - return &S32; - default: - return &F32; - } - }(); - const Id sampled_type = data_types->Get(1); + const VectorIds& data_types = GetAttributeType(*this, image_desc.nfmt); + const Id sampled_type = data_types[1]; const Id image_type{ImageType(*this, image_desc, sampled_type)}; const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, image_type)}; const Id id{AddGlobalVariable(pointer_type, spv::StorageClass::UniformConstant)}; @@ -485,8 +520,8 @@ void EmitContext::DefineImagesAndSamplers() { Name(id, fmt::format("{}_{}{}_{:02x}", stage, "img", image_desc.sgpr_base, image_desc.dword_offset)); images.push_back({ + .data_types = &data_types, .id = id, - .data_types = data_types, .sampled_type = image_desc.is_storage ? sampled_type : TypeSampledImage(image_type), .pointer_type = pointer_type, .image_type = image_type, @@ -494,13 +529,12 @@ void EmitContext::DefineImagesAndSamplers() { interfaces.push_back(id); ++binding; } - - image_u32 = TypePointer(spv::StorageClass::Image, U32[1]); - + if (std::ranges::any_of(info.images, &ImageResource::is_atomic)) { + image_u32 = TypePointer(spv::StorageClass::Image, U32[1]); + } if (info.samplers.empty()) { return; } - sampler_type = TypeSampler(); sampler_pointer_type = TypePointer(spv::StorageClass::UniformConstant, sampler_type); for (const auto& samp_desc : info.samplers) { @@ -516,14 +550,15 @@ void EmitContext::DefineImagesAndSamplers() { } void EmitContext::DefineSharedMemory() { - static constexpr size_t DefaultSharedMemSize = 16_KB; + static constexpr size_t DefaultSharedMemSize = 2_KB; if (!info.uses_shared) { return; } - if (info.shared_memory_size == 0) { - info.shared_memory_size = DefaultSharedMemSize; + u32 shared_memory_size = info.shared_memory_size; + if (shared_memory_size == 0) { + shared_memory_size = DefaultSharedMemSize; } - const u32 num_elements{Common::DivCeil(info.shared_memory_size, 4U)}; + const u32 num_elements{Common::DivCeil(shared_memory_size, 4U)}; const Id type{TypeArray(U32[1], ConstU32(num_elements))}; shared_memory_u32_type = TypePointer(spv::StorageClass::Workgroup, type); shared_u32 = TypePointer(spv::StorageClass::Workgroup, U32[1]); diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h index 768b591f5..d3646382f 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h @@ -36,7 +36,7 @@ struct VectorIds { class EmitContext final : public Sirit::Module { public: - explicit EmitContext(const Profile& profile, IR::Program& program, u32& binding); + explicit EmitContext(const Profile& profile, const Shader::Info& info, u32& binding); ~EmitContext(); Id Def(const IR::Value& value); @@ -124,7 +124,7 @@ public: return ConstantComposite(type, constituents); } - Info& info; + const Info& info; const Profile& profile; Stage stage{}; @@ -193,8 +193,8 @@ public: Id shared_memory_u32_type{}; struct TextureDefinition { - Id id; const VectorIds* data_types; + Id id; Id sampled_type; Id pointer_type; Id image_type; @@ -207,13 +207,20 @@ public: u32 binding; const VectorIds* data_types; Id pointer_type; - AmdGpu::DataFormat dfmt; - AmdGpu::NumberFormat nfmt; - u32 stride; + }; + struct TextureBufferDefinition { + Id id; + Id coord_offset; + u32 binding; + Id image_type; + Id result_type; + bool is_integer; + bool is_storage; }; u32& binding; boost::container::small_vector buffers; + boost::container::small_vector texture_buffers; boost::container::small_vector images; boost::container::small_vector samplers; @@ -238,6 +245,7 @@ private: void DefineOutputs(); void DefinePushDataBlock(); void DefineBuffers(); + void DefineTextureBuffers(); void DefineImagesAndSamplers(); void DefineSharedMemory(); diff --git a/src/shader_recompiler/frontend/control_flow_graph.cpp b/src/shader_recompiler/frontend/control_flow_graph.cpp index 4f3ab86e4..276bd9db0 100644 --- a/src/shader_recompiler/frontend/control_flow_graph.cpp +++ b/src/shader_recompiler/frontend/control_flow_graph.cpp @@ -21,8 +21,13 @@ struct Compare { } }; -static IR::Condition MakeCondition(Opcode opcode) { - switch (opcode) { +static IR::Condition MakeCondition(const GcnInst& inst) { + if (inst.IsCmpx()) { + ASSERT(inst.opcode == Opcode::V_CMPX_NE_U32); + return IR::Condition::Execnz; + } + + switch (inst.opcode) { case Opcode::S_CBRANCH_SCC0: return IR::Condition::Scc0; case Opcode::S_CBRANCH_SCC1: @@ -93,7 +98,8 @@ void CFG::EmitDivergenceLabels() { // While this instruction does not save EXEC it is often used paired // with SAVEEXEC to mask the threads that didn't pass the condition // of initial branch. - inst.opcode == Opcode::S_ANDN2_B64; + (inst.opcode == Opcode::S_ANDN2_B64 && inst.dst[0].field == OperandField::ExecLo) || + inst.opcode == Opcode::V_CMPX_NE_U32; }; const auto is_close_scope = [](const GcnInst& inst) { // Closing an EXEC scope can be either a branch instruction @@ -103,7 +109,8 @@ void CFG::EmitDivergenceLabels() { // Sometimes compiler might insert instructions between the SAVEEXEC and the branch. // Those instructions need to be wrapped in the condition as well so allow branch // as end scope instruction. - inst.opcode == Opcode::S_CBRANCH_EXECZ || inst.opcode == Opcode::S_ANDN2_B64; + inst.opcode == Opcode::S_CBRANCH_EXECZ || + (inst.opcode == Opcode::S_ANDN2_B64 && inst.dst[0].field == OperandField::ExecLo); }; // Since we will be adding new labels, avoid iterating those as well. @@ -170,7 +177,7 @@ void CFG::EmitBlocks() { block->begin_index = GetIndex(start); block->end_index = end_index; block->end_inst = end_inst; - block->cond = MakeCondition(end_inst.opcode); + block->cond = MakeCondition(end_inst); blocks.insert(*block); } } @@ -187,7 +194,7 @@ void CFG::LinkBlocks() { const auto end_inst{block.end_inst}; // Handle divergence block inserted here. if (end_inst.opcode == Opcode::S_AND_SAVEEXEC_B64 || - end_inst.opcode == Opcode::S_ANDN2_B64) { + end_inst.opcode == Opcode::S_ANDN2_B64 || end_inst.opcode == Opcode::V_CMPX_NE_U32) { // Blocks are stored ordered by address in the set auto next_it = std::next(it); auto* target_block = &(*next_it); diff --git a/src/shader_recompiler/frontend/fetch_shader.cpp b/src/shader_recompiler/frontend/fetch_shader.cpp index 81e4093f2..16938410c 100644 --- a/src/shader_recompiler/frontend/fetch_shader.cpp +++ b/src/shader_recompiler/frontend/fetch_shader.cpp @@ -3,6 +3,7 @@ #include #include +#include "common/assert.h" #include "shader_recompiler/frontend/decode.h" #include "shader_recompiler/frontend/fetch_shader.h" @@ -33,8 +34,8 @@ namespace Shader::Gcn { * We take the reverse way, extract the original input semantics from these instructions. **/ -std::vector ParseFetchShader(const u32* code, u32* out_size) { - std::vector attributes; +FetchShaderData ParseFetchShader(const u32* code, u32* out_size) { + FetchShaderData data{}; GcnCodeSlice code_slice(code, code + std::numeric_limits::max()); GcnDecodeContext decoder; @@ -59,6 +60,21 @@ std::vector ParseFetchShader(const u32* code, u32* out_size) { continue; } + if (inst.opcode == Opcode::V_ADD_I32) { + const auto vgpr = inst.dst[0].code; + const auto sgpr = s8(inst.src[0].code); + switch (vgpr) { + case 0: // V0 is always the vertex offset + data.vertex_offset_sgpr = sgpr; + break; + case 3: // V3 is always the instance offset + data.instance_offset_sgpr = sgpr; + break; + default: + UNREACHABLE(); + } + } + if (inst.inst_class == InstClass::VectorMemBufFmt) { // SRSRC is in units of 4 SPGRs while SBASE is in pairs of SGPRs const u32 base_sgpr = inst.src[2].code * 4; @@ -68,7 +84,7 @@ std::vector ParseFetchShader(const u32* code, u32* out_size) { const auto it = std::ranges::find_if( loads, [&](VsharpLoad& load) { return load.dst_reg == base_sgpr; }); - auto& attrib = attributes.emplace_back(); + auto& attrib = data.attributes.emplace_back(); attrib.semantic = semantic_index++; attrib.dest_vgpr = inst.src[1].code; attrib.num_elements = inst.control.mubuf.count; @@ -83,7 +99,7 @@ std::vector ParseFetchShader(const u32* code, u32* out_size) { } } - return attributes; + return data; } } // namespace Shader::Gcn diff --git a/src/shader_recompiler/frontend/fetch_shader.h b/src/shader_recompiler/frontend/fetch_shader.h index 0858061a1..0e5d15419 100644 --- a/src/shader_recompiler/frontend/fetch_shader.h +++ b/src/shader_recompiler/frontend/fetch_shader.h @@ -17,6 +17,12 @@ struct VertexAttribute { u8 instance_data; ///< Indicates that the buffer will be accessed in instance rate }; -std::vector ParseFetchShader(const u32* code, u32* out_size); +struct FetchShaderData { + std::vector attributes; + s8 vertex_offset_sgpr = -1; ///< SGPR of vertex offset from VADDR + s8 instance_offset_sgpr = -1; ///< SGPR of instance offset from VADDR +}; + +FetchShaderData ParseFetchShader(const u32* code, u32* out_size); } // namespace Shader::Gcn diff --git a/src/shader_recompiler/frontend/instruction.cpp b/src/shader_recompiler/frontend/instruction.cpp index d4847708b..a0c132053 100644 --- a/src/shader_recompiler/frontend/instruction.cpp +++ b/src/shader_recompiler/frontend/instruction.cpp @@ -7,7 +7,7 @@ namespace Shader::Gcn { u32 GcnInst::BranchTarget(u32 pc) const { - const s16 simm = static_cast(control.sopp.simm * 4); + const s32 simm = static_cast(control.sopp.simm) * 4; const u32 target = pc + simm + 4; return target; } @@ -47,4 +47,18 @@ bool GcnInst::IsConditionalBranch() const { return false; } +bool GcnInst::IsCmpx() const { + if ((opcode >= Opcode::V_CMPX_F_F32 && opcode <= Opcode::V_CMPX_T_F32) || + (opcode >= Opcode::V_CMPX_F_F64 && opcode <= Opcode::V_CMPX_T_F64) || + (opcode >= Opcode::V_CMPSX_F_F32 && opcode <= Opcode::V_CMPSX_T_F32) || + (opcode >= Opcode::V_CMPSX_F_F64 && opcode <= Opcode::V_CMPSX_T_F64) || + (opcode >= Opcode::V_CMPX_F_I32 && opcode <= Opcode::V_CMPX_CLASS_F32) || + (opcode >= Opcode::V_CMPX_F_I64 && opcode <= Opcode::V_CMPX_CLASS_F64) || + (opcode >= Opcode::V_CMPX_F_U32 && opcode <= Opcode::V_CMPX_T_U32) || + (opcode >= Opcode::V_CMPX_F_U64 && opcode <= Opcode::V_CMPX_T_U64)) { + return true; + } + return false; +} + } // namespace Shader::Gcn diff --git a/src/shader_recompiler/frontend/instruction.h b/src/shader_recompiler/frontend/instruction.h index f83f43db5..7c2e0bd1e 100644 --- a/src/shader_recompiler/frontend/instruction.h +++ b/src/shader_recompiler/frontend/instruction.h @@ -203,6 +203,7 @@ struct GcnInst { bool IsUnconditionalBranch() const; bool IsConditionalBranch() const; bool IsFork() const; + bool IsCmpx() const; }; } // namespace Shader::Gcn diff --git a/src/shader_recompiler/frontend/structured_control_flow.cpp b/src/shader_recompiler/frontend/structured_control_flow.cpp index b50205d46..fefc623fc 100644 --- a/src/shader_recompiler/frontend/structured_control_flow.cpp +++ b/src/shader_recompiler/frontend/structured_control_flow.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include diff --git a/src/shader_recompiler/frontend/translate/data_share.cpp b/src/shader_recompiler/frontend/translate/data_share.cpp index b7b5aa138..7c23c7284 100644 --- a/src/shader_recompiler/frontend/translate/data_share.cpp +++ b/src/shader_recompiler/frontend/translate/data_share.cpp @@ -18,25 +18,31 @@ void Translator::EmitDataShare(const GcnInst& inst) { case Opcode::DS_READ2_B64: return DS_READ(64, false, true, inst); case Opcode::DS_WRITE_B32: - return DS_WRITE(32, false, false, inst); + return DS_WRITE(32, false, false, false, inst); + case Opcode::DS_WRITE2ST64_B32: + return DS_WRITE(32, false, true, true, inst); case Opcode::DS_WRITE_B64: - return DS_WRITE(64, false, false, inst); + return DS_WRITE(64, false, false, false, inst); case Opcode::DS_WRITE2_B32: - return DS_WRITE(32, false, true, inst); + return DS_WRITE(32, false, true, false, inst); case Opcode::DS_WRITE2_B64: - return DS_WRITE(64, false, true, inst); + return DS_WRITE(64, false, true, false, inst); case Opcode::DS_ADD_U32: return DS_ADD_U32(inst, false); case Opcode::DS_MIN_U32: - return DS_MIN_U32(inst, false); + return DS_MIN_U32(inst, false, false); + case Opcode::DS_MIN_I32: + return DS_MIN_U32(inst, true, false); case Opcode::DS_MAX_U32: - return DS_MAX_U32(inst, false); + return DS_MAX_U32(inst, false, false); + case Opcode::DS_MAX_I32: + return DS_MAX_U32(inst, true, false); case Opcode::DS_ADD_RTN_U32: return DS_ADD_U32(inst, true); case Opcode::DS_MIN_RTN_U32: - return DS_MIN_U32(inst, true); + return DS_MIN_U32(inst, false, true); case Opcode::DS_MAX_RTN_U32: - return DS_MAX_U32(inst, true); + return DS_MAX_U32(inst, false, true); default: LogMissingOpcode(inst); } @@ -89,12 +95,13 @@ void Translator::DS_READ(int bit_size, bool is_signed, bool is_pair, const GcnIn } } -void Translator::DS_WRITE(int bit_size, bool is_signed, bool is_pair, const GcnInst& inst) { +void Translator::DS_WRITE(int bit_size, bool is_signed, bool is_pair, bool stride64, + const GcnInst& inst) { const IR::U32 addr{ir.GetVectorReg(IR::VectorReg(inst.src[0].code))}; const IR::VectorReg data0{inst.src[1].code}; const IR::VectorReg data1{inst.src[2].code}; if (is_pair) { - const u32 adj = bit_size == 32 ? 4 : 8; + const u32 adj = (bit_size == 32 ? 4 : 8) * (stride64 ? 64 : 1); const IR::U32 addr0 = ir.IAdd(addr, ir.Imm32(u32(inst.control.ds.offset0 * adj))); if (bit_size == 32) { ir.WriteShared(32, ir.GetVectorReg(data0), addr0); @@ -127,32 +134,29 @@ void Translator::DS_ADD_U32(const GcnInst& inst, bool rtn) { const IR::U32 data{GetSrc(inst.src[1])}; const IR::U32 offset = ir.Imm32(u32(inst.control.ds.offset0)); const IR::U32 addr_offset = ir.IAdd(addr, offset); - IR::VectorReg dst_reg{inst.dst[0].code}; const IR::Value original_val = ir.SharedAtomicIAdd(addr_offset, data); if (rtn) { SetDst(inst.dst[0], IR::U32{original_val}); } } -void Translator::DS_MIN_U32(const GcnInst& inst, bool rtn) { +void Translator::DS_MIN_U32(const GcnInst& inst, bool is_signed, bool rtn) { const IR::U32 addr{GetSrc(inst.src[0])}; const IR::U32 data{GetSrc(inst.src[1])}; const IR::U32 offset = ir.Imm32(u32(inst.control.ds.offset0)); const IR::U32 addr_offset = ir.IAdd(addr, offset); - IR::VectorReg dst_reg{inst.dst[0].code}; - const IR::Value original_val = ir.SharedAtomicIMin(addr_offset, data, false); + const IR::Value original_val = ir.SharedAtomicIMin(addr_offset, data, is_signed); if (rtn) { SetDst(inst.dst[0], IR::U32{original_val}); } } -void Translator::DS_MAX_U32(const GcnInst& inst, bool rtn) { +void Translator::DS_MAX_U32(const GcnInst& inst, bool is_signed, bool rtn) { const IR::U32 addr{GetSrc(inst.src[0])}; const IR::U32 data{GetSrc(inst.src[1])}; const IR::U32 offset = ir.Imm32(u32(inst.control.ds.offset0)); const IR::U32 addr_offset = ir.IAdd(addr, offset); - IR::VectorReg dst_reg{inst.dst[0].code}; - const IR::Value original_val = ir.SharedAtomicIMax(addr_offset, data, false); + const IR::Value original_val = ir.SharedAtomicIMax(addr_offset, data, is_signed); if (rtn) { SetDst(inst.dst[0], IR::U32{original_val}); } @@ -163,18 +167,29 @@ void Translator::S_BARRIER() { } void Translator::V_READFIRSTLANE_B32(const GcnInst& inst) { - ASSERT(info.stage != Stage::Compute); - SetDst(inst.dst[0], GetSrc(inst.src[0])); + const IR::ScalarReg dst{inst.dst[0].code}; + const IR::U32 value{GetSrc(inst.src[0])}; + + if (info.stage != Stage::Compute) { + ir.SetScalarReg(dst, value); + } else { + ir.SetScalarReg(dst, ir.ReadFirstLane(value)); + } } void Translator::V_READLANE_B32(const GcnInst& inst) { - ASSERT(info.stage != Stage::Compute); - SetDst(inst.dst[0], GetSrc(inst.src[0])); + const IR::ScalarReg dst{inst.dst[0].code}; + const IR::U32 value{GetSrc(inst.src[0])}; + const IR::U32 lane{GetSrc(inst.src[1])}; + ir.SetScalarReg(dst, ir.ReadLane(value, lane)); } void Translator::V_WRITELANE_B32(const GcnInst& inst) { - ASSERT(info.stage != Stage::Compute); - SetDst(inst.dst[0], GetSrc(inst.src[0])); + const IR::VectorReg dst{inst.dst[0].code}; + const IR::U32 value{GetSrc(inst.src[0])}; + const IR::U32 lane{GetSrc(inst.src[1])}; + const IR::U32 old_value{GetSrc(inst.dst[0])}; + ir.SetVectorReg(dst, ir.WriteLane(old_value, value, lane)); } } // namespace Shader::Gcn diff --git a/src/shader_recompiler/frontend/translate/export.cpp b/src/shader_recompiler/frontend/translate/export.cpp index 889de21b7..d80de002c 100644 --- a/src/shader_recompiler/frontend/translate/export.cpp +++ b/src/shader_recompiler/frontend/translate/export.cpp @@ -1,14 +1,12 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "common/logging/log.h" #include "shader_recompiler/frontend/translate/translate.h" namespace Shader::Gcn { void Translator::EmitExport(const GcnInst& inst) { if (ir.block->has_multiple_predecessors && info.stage == Stage::Fragment) { - LOG_WARNING(Render_Recompiler, "An ambiguous export appeared in translation"); ir.Discard(ir.LogicalNot(ir.GetExec())); } diff --git a/src/shader_recompiler/frontend/translate/flat_memory.cpp b/src/shader_recompiler/frontend/translate/flat_memory.cpp deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/shader_recompiler/frontend/translate/scalar_alu.cpp b/src/shader_recompiler/frontend/translate/scalar_alu.cpp index 812d93bae..af258cd19 100644 --- a/src/shader_recompiler/frontend/translate/scalar_alu.cpp +++ b/src/shader_recompiler/frontend/translate/scalar_alu.cpp @@ -6,96 +6,152 @@ namespace Shader::Gcn { void Translator::EmitScalarAlu(const GcnInst& inst) { + switch (inst.encoding) { + case InstEncoding::SOPC: { + EmitSOPC(inst); + break; + } + case InstEncoding::SOPK: { + EmitSOPK(inst); + break; + } + default: + switch (inst.opcode) { + case Opcode::S_MOV_B32: + return S_MOV(inst); + case Opcode::S_MUL_I32: + return S_MUL_I32(inst); + case Opcode::S_AND_SAVEEXEC_B64: + return S_AND_SAVEEXEC_B64(inst); + case Opcode::S_MOV_B64: + return S_MOV_B64(inst); + case Opcode::S_OR_B64: + return S_OR_B64(NegateMode::None, false, inst); + case Opcode::S_NOR_B64: + return S_OR_B64(NegateMode::Result, false, inst); + case Opcode::S_XOR_B64: + return S_OR_B64(NegateMode::None, true, inst); + case Opcode::S_XNOR_B64: + return S_OR_B64(NegateMode::Result, true, inst); + case Opcode::S_ORN2_B64: + return S_OR_B64(NegateMode::Src1, false, inst); + case Opcode::S_AND_B64: + return S_AND_B64(NegateMode::None, inst); + case Opcode::S_NAND_B64: + return S_AND_B64(NegateMode::Result, inst); + case Opcode::S_ANDN2_B64: + return S_AND_B64(NegateMode::Src1, inst); + case Opcode::S_NOT_B64: + return S_NOT_B64(inst); + case Opcode::S_ADD_I32: + return S_ADD_I32(inst); + case Opcode::S_AND_B32: + return S_AND_B32(inst); + case Opcode::S_ASHR_I32: + return S_ASHR_I32(inst); + case Opcode::S_OR_B32: + return S_OR_B32(inst); + case Opcode::S_LSHL_B32: + return S_LSHL_B32(inst); + case Opcode::S_LSHR_B32: + return S_LSHR_B32(inst); + case Opcode::S_CSELECT_B32: + return S_CSELECT_B32(inst); + case Opcode::S_CSELECT_B64: + return S_CSELECT_B64(inst); + case Opcode::S_BFE_U32: + return S_BFE_U32(inst); + case Opcode::S_BFM_B32: + return S_BFM_B32(inst); + case Opcode::S_BREV_B32: + return S_BREV_B32(inst); + case Opcode::S_ADD_U32: + return S_ADD_U32(inst); + case Opcode::S_ADDC_U32: + return S_ADDC_U32(inst); + case Opcode::S_SUB_U32: + case Opcode::S_SUB_I32: + return S_SUB_U32(inst); + case Opcode::S_MIN_U32: + return S_MIN_U32(inst); + case Opcode::S_MAX_U32: + return S_MAX_U32(inst); + case Opcode::S_WQM_B64: + break; + default: + LogMissingOpcode(inst); + } + break; + } +} + +void Translator::EmitSOPC(const GcnInst& inst) { switch (inst.opcode) { - case Opcode::S_MOVK_I32: - return S_MOVK(inst); - case Opcode::S_MOV_B32: - return S_MOV(inst); - case Opcode::S_MUL_I32: - return S_MUL_I32(inst); - case Opcode::S_AND_SAVEEXEC_B64: - return S_AND_SAVEEXEC_B64(inst); - case Opcode::S_MOV_B64: - return S_MOV_B64(inst); - case Opcode::S_CMP_LT_U32: - return S_CMP(ConditionOp::LT, false, inst); - case Opcode::S_CMP_LE_U32: - return S_CMP(ConditionOp::LE, false, inst); - case Opcode::S_CMP_LG_U32: - return S_CMP(ConditionOp::LG, false, inst); - case Opcode::S_CMP_LT_I32: - return S_CMP(ConditionOp::LT, true, inst); + case Opcode::S_CMP_EQ_I32: + return S_CMP(ConditionOp::EQ, true, inst); case Opcode::S_CMP_LG_I32: return S_CMP(ConditionOp::LG, true, inst); case Opcode::S_CMP_GT_I32: return S_CMP(ConditionOp::GT, true, inst); - case Opcode::S_CMP_LE_I32: - return S_CMP(ConditionOp::LE, true, inst); case Opcode::S_CMP_GE_I32: return S_CMP(ConditionOp::GE, true, inst); - case Opcode::S_CMP_EQ_I32: - return S_CMP(ConditionOp::EQ, true, inst); + case Opcode::S_CMP_LT_I32: + return S_CMP(ConditionOp::LT, true, inst); + case Opcode::S_CMP_LE_I32: + return S_CMP(ConditionOp::LE, true, inst); + case Opcode::S_CMP_EQ_U32: return S_CMP(ConditionOp::EQ, false, inst); - case Opcode::S_CMP_GE_U32: - return S_CMP(ConditionOp::GE, false, inst); + case Opcode::S_CMP_LG_U32: + return S_CMP(ConditionOp::LG, false, inst); case Opcode::S_CMP_GT_U32: return S_CMP(ConditionOp::GT, false, inst); - case Opcode::S_OR_B64: - return S_OR_B64(NegateMode::None, false, inst); - case Opcode::S_NOR_B64: - return S_OR_B64(NegateMode::Result, false, inst); - case Opcode::S_XOR_B64: - return S_OR_B64(NegateMode::None, true, inst); - case Opcode::S_ORN2_B64: - return S_OR_B64(NegateMode::Src1, false, inst); - case Opcode::S_AND_B64: - return S_AND_B64(NegateMode::None, inst); - case Opcode::S_NAND_B64: - return S_AND_B64(NegateMode::Result, inst); - case Opcode::S_ANDN2_B64: - return S_AND_B64(NegateMode::Src1, inst); - case Opcode::S_NOT_B64: - return S_NOT_B64(inst); - case Opcode::S_ADD_I32: - return S_ADD_I32(inst); - case Opcode::S_AND_B32: - return S_AND_B32(inst); - case Opcode::S_ASHR_I32: - return S_ASHR_I32(inst); - case Opcode::S_OR_B32: - return S_OR_B32(inst); - case Opcode::S_LSHL_B32: - return S_LSHL_B32(inst); - case Opcode::S_LSHR_B32: - return S_LSHR_B32(inst); - case Opcode::S_CSELECT_B32: - return S_CSELECT_B32(inst); - case Opcode::S_CSELECT_B64: - return S_CSELECT_B64(inst); - case Opcode::S_BFE_U32: - return S_BFE_U32(inst); - case Opcode::S_BFM_B32: - return S_BFM_B32(inst); - case Opcode::S_BREV_B32: - return S_BREV_B32(inst); - case Opcode::S_ADD_U32: - return S_ADD_U32(inst); - case Opcode::S_ADDC_U32: - return S_ADDC_U32(inst); + case Opcode::S_CMP_GE_U32: + return S_CMP(ConditionOp::GE, false, inst); + case Opcode::S_CMP_LT_U32: + return S_CMP(ConditionOp::LT, false, inst); + case Opcode::S_CMP_LE_U32: + return S_CMP(ConditionOp::LE, false, inst); + default: + LogMissingOpcode(inst); + } +} + +void Translator::EmitSOPK(const GcnInst& inst) { + switch (inst.opcode) { + case Opcode::S_MOVK_I32: + return S_MOVK(inst); + + case Opcode::S_CMPK_EQ_I32: + return S_CMPK(ConditionOp::EQ, true, inst); + case Opcode::S_CMPK_LG_I32: + return S_CMPK(ConditionOp::LG, true, inst); + case Opcode::S_CMPK_GT_I32: + return S_CMPK(ConditionOp::GT, true, inst); + case Opcode::S_CMPK_GE_I32: + return S_CMPK(ConditionOp::GE, true, inst); + case Opcode::S_CMPK_LT_I32: + return S_CMPK(ConditionOp::LT, true, inst); + case Opcode::S_CMPK_LE_I32: + return S_CMPK(ConditionOp::LE, true, inst); + + case Opcode::S_CMPK_EQ_U32: + return S_CMPK(ConditionOp::EQ, false, inst); + case Opcode::S_CMPK_LG_U32: + return S_CMPK(ConditionOp::LG, false, inst); + case Opcode::S_CMPK_GT_U32: + return S_CMPK(ConditionOp::GT, false, inst); + case Opcode::S_CMPK_GE_U32: + return S_CMPK(ConditionOp::GE, false, inst); + case Opcode::S_CMPK_LT_U32: + return S_CMPK(ConditionOp::LT, false, inst); + case Opcode::S_CMPK_LE_U32: + return S_CMPK(ConditionOp::LE, false, inst); + case Opcode::S_ADDK_I32: return S_ADDK_I32(inst); case Opcode::S_MULK_I32: return S_MULK_I32(inst); - case Opcode::S_SUB_U32: - case Opcode::S_SUB_I32: - return S_SUB_U32(inst); - case Opcode::S_MIN_U32: - return S_MIN_U32(inst); - case Opcode::S_MAX_U32: - return S_MAX_U32(inst); - case Opcode::S_WQM_B64: - break; default: LogMissingOpcode(inst); } @@ -152,6 +208,31 @@ void Translator::S_CMP(ConditionOp cond, bool is_signed, const GcnInst& inst) { ir.SetScc(result); } +void Translator::S_CMPK(ConditionOp cond, bool is_signed, const GcnInst& inst) { + const s32 simm16 = inst.control.sopk.simm; + const IR::U32 lhs = GetSrc(inst.dst[0]); + const IR::U32 rhs = ir.Imm32(simm16); + const IR::U1 result = [&] { + switch (cond) { + case ConditionOp::EQ: + return ir.IEqual(lhs, rhs); + case ConditionOp::LG: + return ir.INotEqual(lhs, rhs); + case ConditionOp::GT: + return ir.IGreaterThan(lhs, rhs, is_signed); + case ConditionOp::GE: + return ir.IGreaterThanEqual(lhs, rhs, is_signed); + case ConditionOp::LT: + return ir.ILessThan(lhs, rhs, is_signed); + case ConditionOp::LE: + return ir.ILessThanEqual(lhs, rhs, is_signed); + default: + UNREACHABLE(); + } + }(); + ir.SetScc(result); +} + void Translator::S_AND_SAVEEXEC_B64(const GcnInst& inst) { // This instruction normally operates on 64-bit data (EXEC, VCC, SGPRs) // However here we flatten it to 1-bit EXEC and 1-bit VCC. For the destination @@ -440,13 +521,16 @@ void Translator::S_SUB_U32(const GcnInst& inst) { void Translator::S_GETPC_B64(u32 pc, const GcnInst& inst) { // This only really exists to let resource tracking pass know // there is an inline cbuf. - SetDst(inst.dst[0], ir.Imm32(pc)); + const IR::ScalarReg dst{inst.dst[0].code}; + ir.SetScalarReg(dst, ir.Imm32(pc)); + ir.SetScalarReg(dst + 1, ir.Imm32(0)); } void Translator::S_ADDC_U32(const GcnInst& inst) { const IR::U32 src0{GetSrc(inst.src[0])}; const IR::U32 src1{GetSrc(inst.src[1])}; - SetDst(inst.dst[0], ir.IAdd(ir.IAdd(src0, src1), ir.GetSccLo())); + const IR::U32 carry{ir.Select(ir.GetScc(), ir.Imm32(1U), ir.Imm32(0U))}; + SetDst(inst.dst[0], ir.IAdd(ir.IAdd(src0, src1), carry)); } void Translator::S_MAX_U32(const GcnInst& inst) { diff --git a/src/shader_recompiler/frontend/translate/translate.cpp b/src/shader_recompiler/frontend/translate/translate.cpp index 4070560ae..eb86310b8 100644 --- a/src/shader_recompiler/frontend/translate/translate.cpp +++ b/src/shader_recompiler/frontend/translate/translate.cpp @@ -346,7 +346,7 @@ void Translator::EmitFetch(const GcnInst& inst) { // Parse the assembly to generate a list of attributes. u32 fetch_size{}; - const auto attribs = ParseFetchShader(code, &fetch_size); + const auto fetch_data = ParseFetchShader(code, &fetch_size); if (Config::dumpShaders()) { using namespace Common::FS; @@ -354,12 +354,15 @@ void Translator::EmitFetch(const GcnInst& inst) { if (!std::filesystem::exists(dump_dir)) { std::filesystem::create_directories(dump_dir); } - const auto filename = fmt::format("vs_fetch_{:#018x}.bin", info.pgm_hash); + const auto filename = fmt::format("vs_{:#018x}_fetch.bin", info.pgm_hash); const auto file = IOFile{dump_dir / filename, FileAccessMode::Write}; file.WriteRaw(code, fetch_size); } - for (const auto& attrib : attribs) { + info.vertex_offset_sgpr = fetch_data.vertex_offset_sgpr; + info.instance_offset_sgpr = fetch_data.instance_offset_sgpr; + + for (const auto& attrib : fetch_data.attributes) { const IR::Attribute attr{IR::Attribute::Param0 + attrib.semantic}; IR::VectorReg dst_reg{attrib.dest_vgpr}; @@ -396,9 +399,7 @@ void Translator::EmitFetch(const GcnInst& inst) { info.buffers.push_back({ .sgpr_base = attrib.sgpr_base, .dword_offset = attrib.dword_offset, - .length = buffer.num_records, .used_types = IR::Type::F32, - .is_storage = true, // we may not fit into UBO with large meshes .is_instance_data = true, }); instance_buf_handle = s32(info.buffers.size() - 1); @@ -435,6 +436,7 @@ void Translator::EmitFlowControl(u32 pc, const GcnInst& inst) { case Opcode::S_CBRANCH_SCC1: case Opcode::S_CBRANCH_VCCNZ: case Opcode::S_CBRANCH_VCCZ: + case Opcode::S_CBRANCH_EXECNZ: case Opcode::S_BRANCH: return; default: diff --git a/src/shader_recompiler/frontend/translate/translate.h b/src/shader_recompiler/frontend/translate/translate.h index 009acabdf..f1619e810 100644 --- a/src/shader_recompiler/frontend/translate/translate.h +++ b/src/shader_recompiler/frontend/translate/translate.h @@ -69,6 +69,10 @@ public: void EmitScalarAlu(const GcnInst& inst); void EmitVectorAlu(const GcnInst& inst); + // Instruction encodings + void EmitSOPC(const GcnInst& inst); + void EmitSOPK(const GcnInst& inst); + // Scalar ALU void S_MOVK(const GcnInst& inst); void S_MOV(const GcnInst& inst); @@ -98,6 +102,7 @@ public: void S_ADDK_I32(const GcnInst& inst); void S_MAX_U32(const GcnInst& inst); void S_MIN_U32(const GcnInst& inst); + void S_CMPK(ConditionOp cond, bool is_signed, const GcnInst& inst); // Scalar Memory void S_LOAD_DWORD(int num_dwords, const GcnInst& inst); @@ -116,6 +121,7 @@ public: void V_AND_B32(const GcnInst& inst); void V_LSHLREV_B32(const GcnInst& inst); void V_LSHL_B32(const GcnInst& inst); + void V_LSHL_B64(const GcnInst& inst); void V_ADD_I32(const GcnInst& inst); void V_ADDC_U32(const GcnInst& inst); void V_CVT_F32_I32(const GcnInst& inst); @@ -183,10 +189,14 @@ public: void V_CMP_CLASS_F32(const GcnInst& inst); void V_FFBL_B32(const GcnInst& inst); void V_MBCNT_U32_B32(bool is_low, const GcnInst& inst); + void V_BFM_B32(const GcnInst& inst); + void V_FFBH_U32(const GcnInst& inst); // Vector Memory - void BUFFER_LOAD_FORMAT(u32 num_dwords, bool is_typed, bool is_format, const GcnInst& inst); - void BUFFER_STORE_FORMAT(u32 num_dwords, bool is_typed, bool is_format, const GcnInst& inst); + void BUFFER_LOAD(u32 num_dwords, bool is_typed, const GcnInst& inst); + void BUFFER_LOAD_FORMAT(u32 num_dwords, const GcnInst& inst); + void BUFFER_STORE(u32 num_dwords, bool is_typed, const GcnInst& inst); + void BUFFER_STORE_FORMAT(u32 num_dwords, const GcnInst& inst); void BUFFER_ATOMIC(AtomicOp op, const GcnInst& inst); // Vector interpolation @@ -196,10 +206,10 @@ public: // Data share void DS_SWIZZLE_B32(const GcnInst& inst); void DS_READ(int bit_size, bool is_signed, bool is_pair, const GcnInst& inst); - void DS_WRITE(int bit_size, bool is_signed, bool is_pair, const GcnInst& inst); + void DS_WRITE(int bit_size, bool is_signed, bool is_pair, bool stride64, const GcnInst& inst); void DS_ADD_U32(const GcnInst& inst, bool rtn); - void DS_MIN_U32(const GcnInst& inst, bool rtn); - void DS_MAX_U32(const GcnInst& inst, bool rtn); + void DS_MIN_U32(const GcnInst& inst, bool is_signed, bool rtn); + void DS_MAX_U32(const GcnInst& inst, bool is_signed, bool rtn); void V_READFIRSTLANE_B32(const GcnInst& inst); void V_READLANE_B32(const GcnInst& inst); void V_WRITELANE_B32(const GcnInst& inst); diff --git a/src/shader_recompiler/frontend/translate/vector_alu.cpp b/src/shader_recompiler/frontend/translate/vector_alu.cpp index 1bbc3c162..7fef91377 100644 --- a/src/shader_recompiler/frontend/translate/vector_alu.cpp +++ b/src/shader_recompiler/frontend/translate/vector_alu.cpp @@ -11,6 +11,8 @@ void Translator::EmitVectorAlu(const GcnInst& inst) { return V_LSHLREV_B32(inst); case Opcode::V_LSHL_B32: return V_LSHL_B32(inst); + case Opcode::V_LSHL_B64: + return V_LSHL_B64(inst); case Opcode::V_BFREV_B32: return V_BFREV_B32(inst); case Opcode::V_BFE_U32: @@ -280,6 +282,8 @@ void Translator::EmitVectorAlu(const GcnInst& inst) { return V_CMP_U32(ConditionOp::GT, true, false, inst); case Opcode::V_CMP_LT_I32: return V_CMP_U32(ConditionOp::LT, true, false, inst); + case Opcode::V_CMPX_GT_I32: + return V_CMP_U32(ConditionOp::GT, true, true, inst); case Opcode::V_CMPX_LT_I32: return V_CMP_U32(ConditionOp::LT, true, true, inst); case Opcode::V_CMPX_F_U32: @@ -305,6 +309,13 @@ void Translator::EmitVectorAlu(const GcnInst& inst) { return V_MBCNT_U32_B32(true, inst); case Opcode::V_MBCNT_HI_U32_B32: return V_MBCNT_U32_B32(false, inst); + case Opcode::V_NOP: + return; + + case Opcode::V_BFM_B32: + return V_BFM_B32(inst); + case Opcode::V_FFBH_U32: + return V_FFBH_U32(inst); default: LogMissingOpcode(inst); } @@ -386,6 +397,16 @@ void Translator::V_LSHL_B32(const GcnInst& inst) { SetDst(inst.dst[0], ir.ShiftLeftLogical(src0, ir.BitwiseAnd(src1, ir.Imm32(0x1F)))); } +void Translator::V_LSHL_B64(const GcnInst& inst) { + const IR::U64 src0{GetSrc64(inst.src[0])}; + const IR::U64 src1{GetSrc64(inst.src[1])}; + const IR::VectorReg dst_reg{inst.dst[0].code}; + ASSERT_MSG(src0.IsImmediate() && src0.U64() == 0 && src1.IsImmediate() && src1.U64() == 0, + "V_LSHL_B64 with non-zero src0 or src1 is not supported"); + ir.SetVectorReg(dst_reg, ir.Imm32(0)); + ir.SetVectorReg(dst_reg + 1, ir.Imm32(0)); +} + void Translator::V_ADD_I32(const GcnInst& inst) { const IR::U32 src0{GetSrc(inst.src[0])}; const IR::U32 src1{ir.GetVectorReg(IR::VectorReg(inst.src[1].code))}; @@ -399,14 +420,20 @@ void Translator::V_ADDC_U32(const GcnInst& inst) { const auto src0 = GetSrc(inst.src[0]); const auto src1 = GetSrc(inst.src[1]); - IR::U32 scarry; + IR::U1 carry; if (inst.src_count == 3) { // VOP3 - IR::U1 thread_bit{ir.GetThreadBitScalarReg(IR::ScalarReg(inst.src[2].code))}; - scarry = IR::U32{ir.Select(thread_bit, ir.Imm32(1), ir.Imm32(0))}; + if (inst.src[2].field == OperandField::VccLo) { + carry = ir.GetVcc(); + } else if (inst.src[2].field == OperandField::ScalarGPR) { + carry = ir.GetThreadBitScalarReg(IR::ScalarReg(inst.src[2].code)); + } else { + UNREACHABLE(); + } } else { // VOP2 - scarry = ir.GetVccLo(); + carry = ir.GetVcc(); } + const IR::U32 scarry = IR::U32{ir.Select(carry, ir.Imm32(1), ir.Imm32(0))}; const IR::U32 result = ir.IAdd(ir.IAdd(src0, src1), scarry); const IR::VectorReg dst_reg{inst.dst[0].code}; @@ -942,4 +969,24 @@ void Translator::V_MBCNT_U32_B32(bool is_low, const GcnInst& inst) { SetDst(inst.dst[0], ir.LaneId()); } +void Translator::V_BFM_B32(const GcnInst& inst) { + // bitmask width + const IR::U32 src0{ir.BitFieldExtract(GetSrc(inst.src[0]), ir.Imm32(0), ir.Imm32(4))}; + // bitmask offset + const IR::U32 src1{ir.BitFieldExtract(GetSrc(inst.src[1]), ir.Imm32(0), ir.Imm32(4))}; + const IR::U32 ones = ir.ISub(ir.ShiftLeftLogical(ir.Imm32(1), src0), ir.Imm32(1)); + SetDst(inst.dst[0], ir.ShiftLeftLogical(ones, src1)); +} + +void Translator::V_FFBH_U32(const GcnInst& inst) { + const IR::U32 src0{GetSrc(inst.src[0])}; + // Gcn wants the MSB position counting from the left, but SPIR-V counts from the rightmost (LSB) + // position + const IR::U32 msb_pos = ir.FindUMsb(src0); + const IR::U32 pos_from_left = ir.ISub(ir.Imm32(31), msb_pos); + // Select 0xFFFFFFFF if src0 was 0 + const IR::U1 cond = ir.INotEqual(src0, ir.Imm32(0)); + SetDst(inst.dst[0], IR::U32{ir.Select(cond, pos_from_left, ir.Imm32(~0U))}); +} + } // namespace Shader::Gcn diff --git a/src/shader_recompiler/frontend/translate/vector_memory.cpp b/src/shader_recompiler/frontend/translate/vector_memory.cpp index 08674fa2d..73530dade 100644 --- a/src/shader_recompiler/frontend/translate/vector_memory.cpp +++ b/src/shader_recompiler/frontend/translate/vector_memory.cpp @@ -17,6 +17,7 @@ void Translator::EmitVectorMemory(const GcnInst& inst) { case Opcode::IMAGE_SAMPLE_C_O: case Opcode::IMAGE_SAMPLE_B: case Opcode::IMAGE_SAMPLE_C_LZ_O: + case Opcode::IMAGE_SAMPLE_D: return IMAGE_SAMPLE(inst); case Opcode::IMAGE_GATHER4_C: case Opcode::IMAGE_GATHER4_LZ: @@ -55,59 +56,63 @@ void Translator::EmitVectorMemory(const GcnInst& inst) { // Buffer load operations case Opcode::TBUFFER_LOAD_FORMAT_X: - return BUFFER_LOAD_FORMAT(1, true, true, inst); + return BUFFER_LOAD(1, true, inst); case Opcode::TBUFFER_LOAD_FORMAT_XY: - return BUFFER_LOAD_FORMAT(2, true, true, inst); + return BUFFER_LOAD(2, true, inst); case Opcode::TBUFFER_LOAD_FORMAT_XYZ: - return BUFFER_LOAD_FORMAT(3, true, true, inst); + return BUFFER_LOAD(3, true, inst); case Opcode::TBUFFER_LOAD_FORMAT_XYZW: - return BUFFER_LOAD_FORMAT(4, true, true, inst); + return BUFFER_LOAD(4, true, inst); case Opcode::BUFFER_LOAD_FORMAT_X: - return BUFFER_LOAD_FORMAT(1, false, true, inst); + return BUFFER_LOAD_FORMAT(1, inst); case Opcode::BUFFER_LOAD_FORMAT_XY: - return BUFFER_LOAD_FORMAT(2, false, true, inst); + return BUFFER_LOAD_FORMAT(2, inst); case Opcode::BUFFER_LOAD_FORMAT_XYZ: - return BUFFER_LOAD_FORMAT(3, false, true, inst); + return BUFFER_LOAD_FORMAT(3, inst); case Opcode::BUFFER_LOAD_FORMAT_XYZW: - return BUFFER_LOAD_FORMAT(4, false, true, inst); + return BUFFER_LOAD_FORMAT(4, inst); case Opcode::BUFFER_LOAD_DWORD: - return BUFFER_LOAD_FORMAT(1, false, false, inst); + return BUFFER_LOAD(1, false, inst); case Opcode::BUFFER_LOAD_DWORDX2: - return BUFFER_LOAD_FORMAT(2, false, false, inst); + return BUFFER_LOAD(2, false, inst); case Opcode::BUFFER_LOAD_DWORDX3: - return BUFFER_LOAD_FORMAT(3, false, false, inst); + return BUFFER_LOAD(3, false, inst); case Opcode::BUFFER_LOAD_DWORDX4: - return BUFFER_LOAD_FORMAT(4, false, false, inst); + return BUFFER_LOAD(4, false, inst); // Buffer store operations case Opcode::BUFFER_STORE_FORMAT_X: - return BUFFER_STORE_FORMAT(1, false, true, inst); + return BUFFER_STORE_FORMAT(1, inst); case Opcode::BUFFER_STORE_FORMAT_XY: - return BUFFER_STORE_FORMAT(2, false, true, inst); + return BUFFER_STORE_FORMAT(2, inst); case Opcode::BUFFER_STORE_FORMAT_XYZ: - return BUFFER_STORE_FORMAT(3, false, true, inst); + return BUFFER_STORE_FORMAT(3, inst); case Opcode::BUFFER_STORE_FORMAT_XYZW: - return BUFFER_STORE_FORMAT(4, false, true, inst); + return BUFFER_STORE_FORMAT(4, inst); case Opcode::TBUFFER_STORE_FORMAT_X: - return BUFFER_STORE_FORMAT(1, true, true, inst); + return BUFFER_STORE(1, true, inst); + case Opcode::TBUFFER_STORE_FORMAT_XY: + return BUFFER_STORE(2, true, inst); case Opcode::TBUFFER_STORE_FORMAT_XYZ: - return BUFFER_STORE_FORMAT(3, true, true, inst); + return BUFFER_STORE(3, true, inst); case Opcode::BUFFER_STORE_DWORD: - return BUFFER_STORE_FORMAT(1, false, false, inst); + return BUFFER_STORE(1, false, inst); case Opcode::BUFFER_STORE_DWORDX2: - return BUFFER_STORE_FORMAT(2, false, false, inst); + return BUFFER_STORE(2, false, inst); case Opcode::BUFFER_STORE_DWORDX3: - return BUFFER_STORE_FORMAT(3, false, false, inst); + return BUFFER_STORE(3, false, inst); case Opcode::BUFFER_STORE_DWORDX4: - return BUFFER_STORE_FORMAT(4, false, false, inst); + return BUFFER_STORE(4, false, inst); // Buffer atomic operations case Opcode::BUFFER_ATOMIC_ADD: return BUFFER_ATOMIC(AtomicOp::Add, inst); + case Opcode::BUFFER_ATOMIC_SWAP: + return BUFFER_ATOMIC(AtomicOp::Swap, inst); default: LogMissingOpcode(inst); } @@ -162,12 +167,15 @@ void Translator::IMAGE_SAMPLE(const GcnInst& inst) { flags.test(MimgModifier::LodBias) ? ir.GetVectorReg(addr_reg++) : IR::F32{}; const IR::F32 dref = flags.test(MimgModifier::Pcf) ? ir.GetVectorReg(addr_reg++) : IR::F32{}; - - // Derivatives are tricky because their number depends on the texture type which is located in - // T#. We don't have access to T# though until resource tracking pass. For now assume no - // derivatives are present, otherwise we don't know where coordinates are placed in the address - // stream. - ASSERT_MSG(!flags.test(MimgModifier::Derivative), "Derivative image instruction"); + const IR::Value derivatives = [&] -> IR::Value { + if (!flags.test(MimgModifier::Derivative)) { + return {}; + } + addr_reg = addr_reg + 4; + return ir.CompositeConstruct( + ir.GetVectorReg(addr_reg - 4), ir.GetVectorReg(addr_reg - 3), + ir.GetVectorReg(addr_reg - 2), ir.GetVectorReg(addr_reg - 1)); + }(); // Now we can load body components as noted in Table 8.9 Image Opcodes with Sampler // Since these are at most 4 dwords, we load them into a single uvec4 and place them @@ -177,6 +185,10 @@ void Translator::IMAGE_SAMPLE(const GcnInst& inst) { ir.GetVectorReg(addr_reg), ir.GetVectorReg(addr_reg + 1), ir.GetVectorReg(addr_reg + 2), ir.GetVectorReg(addr_reg + 3)); + // Derivatives are tricky because their number depends on the texture type which is located in + // T#. We don't have access to T# though until resource tracking pass. For now assume if + // derivatives are present, that a 2D image is bound. + const bool has_derivatives = flags.test(MimgModifier::Derivative); const bool explicit_lod = flags.any(MimgModifier::Level0, MimgModifier::Lod); IR::TextureInstInfo info{}; @@ -186,9 +198,13 @@ void Translator::IMAGE_SAMPLE(const GcnInst& inst) { info.force_level0.Assign(flags.test(MimgModifier::Level0)); info.has_offset.Assign(flags.test(MimgModifier::Offset)); info.explicit_lod.Assign(explicit_lod); + info.has_derivatives.Assign(has_derivatives); // Issue IR instruction, leaving unknown fields blank to patch later. const IR::Value texel = [&]() -> IR::Value { + if (has_derivatives) { + return ir.ImageGradient(handle, body, derivatives, offset, {}, info); + } if (!flags.test(MimgModifier::Pcf)) { if (explicit_lod) { return ir.ImageSampleExplicitLod(handle, body, offset, info); @@ -333,8 +349,7 @@ void Translator::IMAGE_STORE(const GcnInst& inst) { ir.ImageWrite(handle, body, value, {}); } -void Translator::BUFFER_LOAD_FORMAT(u32 num_dwords, bool is_typed, bool is_format, - const GcnInst& inst) { +void Translator::BUFFER_LOAD(u32 num_dwords, bool is_typed, const GcnInst& inst) { const auto& mtbuf = inst.control.mtbuf; const IR::VectorReg vaddr{inst.src[0].code}; const IR::ScalarReg sharp{inst.src[2].code * 4}; @@ -354,22 +369,19 @@ void Translator::BUFFER_LOAD_FORMAT(u32 num_dwords, bool is_typed, bool is_forma info.index_enable.Assign(mtbuf.idxen); info.offset_enable.Assign(mtbuf.offen); info.inst_offset.Assign(mtbuf.offset); - info.is_typed.Assign(is_typed); if (is_typed) { - info.dmft.Assign(static_cast(mtbuf.dfmt)); - info.nfmt.Assign(static_cast(mtbuf.nfmt)); - ASSERT(info.nfmt == AmdGpu::NumberFormat::Float && - (info.dmft == AmdGpu::DataFormat::Format32_32_32_32 || - info.dmft == AmdGpu::DataFormat::Format32_32_32 || - info.dmft == AmdGpu::DataFormat::Format32_32 || - info.dmft == AmdGpu::DataFormat::Format32)); + const auto dmft = static_cast(mtbuf.dfmt); + const auto nfmt = static_cast(mtbuf.nfmt); + ASSERT(nfmt == AmdGpu::NumberFormat::Float && + (dmft == AmdGpu::DataFormat::Format32_32_32_32 || + dmft == AmdGpu::DataFormat::Format32_32_32 || + dmft == AmdGpu::DataFormat::Format32_32 || dmft == AmdGpu::DataFormat::Format32)); } const IR::Value handle = ir.CompositeConstruct(ir.GetScalarReg(sharp), ir.GetScalarReg(sharp + 1), ir.GetScalarReg(sharp + 2), ir.GetScalarReg(sharp + 3)); - const IR::Value value = is_format ? ir.LoadBufferFormat(num_dwords, handle, address, info) - : ir.LoadBuffer(num_dwords, handle, address, info); + const IR::Value value = ir.LoadBuffer(num_dwords, handle, address, info); const IR::VectorReg dst_reg{inst.src[1].code}; if (num_dwords == 1) { ir.SetVectorReg(dst_reg, IR::F32{value}); @@ -380,8 +392,34 @@ void Translator::BUFFER_LOAD_FORMAT(u32 num_dwords, bool is_typed, bool is_forma } } -void Translator::BUFFER_STORE_FORMAT(u32 num_dwords, bool is_typed, bool is_format, - const GcnInst& inst) { +void Translator::BUFFER_LOAD_FORMAT(u32 num_dwords, const GcnInst& inst) { + const auto& mubuf = inst.control.mubuf; + const IR::VectorReg vaddr{inst.src[0].code}; + const IR::ScalarReg sharp{inst.src[2].code * 4}; + ASSERT_MSG(!mubuf.offen && mubuf.offset == 0, "Offsets for image buffers are not supported"); + const IR::Value address = [&] -> IR::Value { + if (mubuf.idxen) { + return ir.GetVectorReg(vaddr); + } + return {}; + }(); + const IR::Value soffset{GetSrc(inst.src[3])}; + ASSERT_MSG(soffset.IsImmediate() && soffset.U32() == 0, "Non immediate offset not supported"); + + IR::BufferInstInfo info{}; + info.index_enable.Assign(mubuf.idxen); + + const IR::Value handle = + ir.CompositeConstruct(ir.GetScalarReg(sharp), ir.GetScalarReg(sharp + 1), + ir.GetScalarReg(sharp + 2), ir.GetScalarReg(sharp + 3)); + const IR::Value value = ir.LoadBufferFormat(handle, address, info); + const IR::VectorReg dst_reg{inst.src[1].code}; + for (u32 i = 0; i < num_dwords; i++) { + ir.SetVectorReg(dst_reg + i, IR::F32{ir.CompositeExtract(value, i)}); + } +} + +void Translator::BUFFER_STORE(u32 num_dwords, bool is_typed, const GcnInst& inst) { const auto& mtbuf = inst.control.mtbuf; const IR::VectorReg vaddr{inst.src[0].code}; const IR::ScalarReg sharp{inst.src[2].code * 4}; @@ -401,45 +439,76 @@ void Translator::BUFFER_STORE_FORMAT(u32 num_dwords, bool is_typed, bool is_form info.index_enable.Assign(mtbuf.idxen); info.offset_enable.Assign(mtbuf.offen); info.inst_offset.Assign(mtbuf.offset); - info.is_typed.Assign(is_typed); if (is_typed) { - info.dmft.Assign(static_cast(mtbuf.dfmt)); - info.nfmt.Assign(static_cast(mtbuf.nfmt)); + const auto dmft = static_cast(mtbuf.dfmt); + const auto nfmt = static_cast(mtbuf.nfmt); + ASSERT(nfmt == AmdGpu::NumberFormat::Float && + (dmft == AmdGpu::DataFormat::Format32_32_32_32 || + dmft == AmdGpu::DataFormat::Format32_32_32 || + dmft == AmdGpu::DataFormat::Format32_32 || dmft == AmdGpu::DataFormat::Format32)); } IR::Value value{}; const IR::VectorReg src_reg{inst.src[1].code}; switch (num_dwords) { case 1: - value = ir.GetVectorReg(src_reg); + value = ir.GetVectorReg(src_reg); break; case 2: - value = ir.CompositeConstruct(ir.GetVectorReg(src_reg), - ir.GetVectorReg(src_reg + 1)); + value = ir.CompositeConstruct(ir.GetVectorReg(src_reg), + ir.GetVectorReg(src_reg + 1)); break; case 3: - value = ir.CompositeConstruct(ir.GetVectorReg(src_reg), - ir.GetVectorReg(src_reg + 1), - ir.GetVectorReg(src_reg + 2)); + value = ir.CompositeConstruct(ir.GetVectorReg(src_reg), + ir.GetVectorReg(src_reg + 1), + ir.GetVectorReg(src_reg + 2)); break; case 4: - value = ir.CompositeConstruct(ir.GetVectorReg(src_reg), - ir.GetVectorReg(src_reg + 1), - ir.GetVectorReg(src_reg + 2), - ir.GetVectorReg(src_reg + 3)); + value = ir.CompositeConstruct( + ir.GetVectorReg(src_reg), ir.GetVectorReg(src_reg + 1), + ir.GetVectorReg(src_reg + 2), ir.GetVectorReg(src_reg + 3)); break; } const IR::Value handle = ir.CompositeConstruct(ir.GetScalarReg(sharp), ir.GetScalarReg(sharp + 1), ir.GetScalarReg(sharp + 2), ir.GetScalarReg(sharp + 3)); - if (is_format) { - ir.StoreBufferFormat(num_dwords, handle, address, value, info); - } else { - ir.StoreBuffer(num_dwords, handle, address, value, info); - } + ir.StoreBuffer(num_dwords, handle, address, value, info); +} + +void Translator::BUFFER_STORE_FORMAT(u32 num_dwords, const GcnInst& inst) { + const auto& mubuf = inst.control.mubuf; + const IR::VectorReg vaddr{inst.src[0].code}; + const IR::ScalarReg sharp{inst.src[2].code * 4}; + ASSERT_MSG(!mubuf.offen && mubuf.offset == 0, "Offsets for image buffers are not supported"); + const IR::Value address = [&] -> IR::Value { + if (mubuf.idxen) { + return ir.GetVectorReg(vaddr); + } + return {}; + }(); + const IR::Value soffset{GetSrc(inst.src[3])}; + ASSERT_MSG(soffset.IsImmediate() && soffset.U32() == 0, "Non immediate offset not supported"); + + IR::BufferInstInfo info{}; + info.index_enable.Assign(mubuf.idxen); + + const IR::VectorReg src_reg{inst.src[1].code}; + + std::array comps{}; + for (u32 i = 0; i < num_dwords; i++) { + comps[i] = ir.GetVectorReg(src_reg + i); + } + for (u32 i = num_dwords; i < 4; i++) { + comps[i] = ir.Imm32(0.f); + } + + const IR::Value value = ir.CompositeConstruct(comps[0], comps[1], comps[2], comps[3]); + const IR::Value handle = + ir.CompositeConstruct(ir.GetScalarReg(sharp), ir.GetScalarReg(sharp + 1), + ir.GetScalarReg(sharp + 2), ir.GetScalarReg(sharp + 3)); + ir.StoreBufferFormat(handle, address, value, info); } -// TODO: U64 void Translator::BUFFER_ATOMIC(AtomicOp op, const GcnInst& inst) { const auto& mubuf = inst.control.mubuf; const IR::VectorReg vaddr{inst.src[0].code}; @@ -462,7 +531,7 @@ void Translator::BUFFER_ATOMIC(AtomicOp op, const GcnInst& inst) { const IR::Value original_val = [&] { switch (op) { case AtomicOp::Swap: - return ir.BufferAtomicExchange(handle, address, vdata_val, info); + return ir.BufferAtomicSwap(handle, address, vdata_val, info); case AtomicOp::Add: return ir.BufferAtomicIAdd(handle, address, vdata_val, info); case AtomicOp::Smin: diff --git a/src/shader_recompiler/ir/ir_emitter.cpp b/src/shader_recompiler/ir/ir_emitter.cpp index 3ae068072..473ae4f66 100644 --- a/src/shader_recompiler/ir/ir_emitter.cpp +++ b/src/shader_recompiler/ir/ir_emitter.cpp @@ -209,10 +209,6 @@ U1 IREmitter::GetVcc() { return Inst(Opcode::GetVcc); } -U32 IREmitter::GetSccLo() { - return Inst(Opcode::GetSccLo); -} - U32 IREmitter::GetVccLo() { return Inst(Opcode::GetVccLo); } @@ -329,20 +325,8 @@ Value IREmitter::LoadBuffer(int num_dwords, const Value& handle, const Value& ad } } -Value IREmitter::LoadBufferFormat(int num_dwords, const Value& handle, const Value& address, - BufferInstInfo info) { - switch (num_dwords) { - case 1: - return Inst(Opcode::LoadBufferFormatF32, Flags{info}, handle, address); - case 2: - return Inst(Opcode::LoadBufferFormatF32x2, Flags{info}, handle, address); - case 3: - return Inst(Opcode::LoadBufferFormatF32x3, Flags{info}, handle, address); - case 4: - return Inst(Opcode::LoadBufferFormatF32x4, Flags{info}, handle, address); - default: - UNREACHABLE_MSG("Invalid number of dwords {}", num_dwords); - } +Value IREmitter::LoadBufferFormat(const Value& handle, const Value& address, BufferInstInfo info) { + return Inst(Opcode::LoadBufferFormatF32, Flags{info}, handle, address); } void IREmitter::StoreBuffer(int num_dwords, const Value& handle, const Value& address, @@ -408,29 +392,14 @@ Value IREmitter::BufferAtomicXor(const Value& handle, const Value& address, cons return Inst(Opcode::BufferAtomicXor32, Flags{info}, handle, address, value); } -Value IREmitter::BufferAtomicExchange(const Value& handle, const Value& address, const Value& value, - BufferInstInfo info) { - return Inst(Opcode::BufferAtomicExchange32, Flags{info}, handle, address, value); +Value IREmitter::BufferAtomicSwap(const Value& handle, const Value& address, const Value& value, + BufferInstInfo info) { + return Inst(Opcode::BufferAtomicSwap32, Flags{info}, handle, address, value); } -void IREmitter::StoreBufferFormat(int num_dwords, const Value& handle, const Value& address, - const Value& data, BufferInstInfo info) { - switch (num_dwords) { - case 1: - Inst(Opcode::StoreBufferFormatF32, Flags{info}, handle, address, data); - break; - case 2: - Inst(Opcode::StoreBufferFormatF32x2, Flags{info}, handle, address, data); - break; - case 3: - Inst(Opcode::StoreBufferFormatF32x3, Flags{info}, handle, address, data); - break; - case 4: - Inst(Opcode::StoreBufferFormatF32x4, Flags{info}, handle, address, data); - break; - default: - UNREACHABLE_MSG("Invalid number of dwords {}", num_dwords); - } +void IREmitter::StoreBufferFormat(const Value& handle, const Value& address, const Value& data, + BufferInstInfo info) { + Inst(Opcode::StoreBufferFormatF32, Flags{info}, handle, address, data); } U32 IREmitter::LaneId() { @@ -445,6 +414,18 @@ U32 IREmitter::QuadShuffle(const U32& value, const U32& index) { return Inst(Opcode::QuadShuffle, value, index); } +U32 IREmitter::ReadFirstLane(const U32& value) { + return Inst(Opcode::ReadFirstLane, value); +} + +U32 IREmitter::ReadLane(const U32& value, const U32& lane) { + return Inst(Opcode::ReadLane, value, lane); +} + +U32 IREmitter::WriteLane(const U32& value, const U32& write_value, const U32& lane) { + return Inst(Opcode::WriteLane, value, write_value, lane); +} + F32F64 IREmitter::FPAdd(const F32F64& a, const F32F64& b) { if (a.Type() != b.Type()) { UNREACHABLE_MSG("Mismatching types {} and {}", a.Type(), b.Type()); @@ -1107,8 +1088,18 @@ U32U64 IREmitter::ShiftRightArithmetic(const U32U64& base, const U32& shift) { } } -U32 IREmitter::BitwiseAnd(const U32& a, const U32& b) { - return Inst(Opcode::BitwiseAnd32, a, b); +U32U64 IREmitter::BitwiseAnd(const U32U64& a, const U32U64& b) { + if (a.Type() != b.Type()) { + UNREACHABLE_MSG("Mismatching types {} and {}", a.Type(), b.Type()); + } + switch (a.Type()) { + case Type::U32: + return Inst(Opcode::BitwiseAnd32, a, b); + case Type::U64: + return Inst(Opcode::BitwiseAnd64, a, b); + default: + ThrowInvalidType(a.Type()); + } } U32U64 IREmitter::BitwiseOr(const U32U64& a, const U32U64& b) { diff --git a/src/shader_recompiler/ir/ir_emitter.h b/src/shader_recompiler/ir/ir_emitter.h index be7f25153..de8fe450d 100644 --- a/src/shader_recompiler/ir/ir_emitter.h +++ b/src/shader_recompiler/ir/ir_emitter.h @@ -65,7 +65,6 @@ public: [[nodiscard]] U1 GetScc(); [[nodiscard]] U1 GetExec(); [[nodiscard]] U1 GetVcc(); - [[nodiscard]] U32 GetSccLo(); [[nodiscard]] U32 GetVccLo(); [[nodiscard]] U32 GetVccHi(); void SetScc(const U1& value); @@ -93,12 +92,12 @@ public: [[nodiscard]] Value LoadBuffer(int num_dwords, const Value& handle, const Value& address, BufferInstInfo info); - [[nodiscard]] Value LoadBufferFormat(int num_dwords, const Value& handle, const Value& address, + [[nodiscard]] Value LoadBufferFormat(const Value& handle, const Value& address, BufferInstInfo info); void StoreBuffer(int num_dwords, const Value& handle, const Value& address, const Value& data, BufferInstInfo info); - void StoreBufferFormat(int num_dwords, const Value& handle, const Value& address, - const Value& data, BufferInstInfo info); + void StoreBufferFormat(const Value& handle, const Value& address, const Value& data, + BufferInstInfo info); [[nodiscard]] Value BufferAtomicIAdd(const Value& handle, const Value& address, const Value& value, BufferInstInfo info); @@ -116,12 +115,15 @@ public: const Value& value, BufferInstInfo info); [[nodiscard]] Value BufferAtomicXor(const Value& handle, const Value& address, const Value& value, BufferInstInfo info); - [[nodiscard]] Value BufferAtomicExchange(const Value& handle, const Value& address, - const Value& value, BufferInstInfo info); + [[nodiscard]] Value BufferAtomicSwap(const Value& handle, const Value& address, + const Value& value, BufferInstInfo info); [[nodiscard]] U32 LaneId(); [[nodiscard]] U32 WarpId(); [[nodiscard]] U32 QuadShuffle(const U32& value, const U32& index); + [[nodiscard]] U32 ReadFirstLane(const U32& value); + [[nodiscard]] U32 ReadLane(const U32& value, const U32& lane); + [[nodiscard]] U32 WriteLane(const U32& value, const U32& write_value, const U32& lane); [[nodiscard]] Value CompositeConstruct(const Value& e1, const Value& e2); [[nodiscard]] Value CompositeConstruct(const Value& e1, const Value& e2, const Value& e3); @@ -193,7 +195,7 @@ public: [[nodiscard]] U32U64 ShiftLeftLogical(const U32U64& base, const U32& shift); [[nodiscard]] U32U64 ShiftRightLogical(const U32U64& base, const U32& shift); [[nodiscard]] U32U64 ShiftRightArithmetic(const U32U64& base, const U32& shift); - [[nodiscard]] U32 BitwiseAnd(const U32& a, const U32& b); + [[nodiscard]] U32U64 BitwiseAnd(const U32U64& a, const U32U64& b); [[nodiscard]] U32U64 BitwiseOr(const U32U64& a, const U32U64& b); [[nodiscard]] U32 BitwiseXor(const U32& a, const U32& b); [[nodiscard]] U32 BitFieldInsert(const U32& base, const U32& insert, const U32& offset, diff --git a/src/shader_recompiler/ir/microinstruction.cpp b/src/shader_recompiler/ir/microinstruction.cpp index e35be8a7f..d6ef49cf7 100644 --- a/src/shader_recompiler/ir/microinstruction.cpp +++ b/src/shader_recompiler/ir/microinstruction.cpp @@ -56,9 +56,6 @@ bool Inst::MayHaveSideEffects() const noexcept { case Opcode::StoreBufferF32x3: case Opcode::StoreBufferF32x4: case Opcode::StoreBufferFormatF32: - case Opcode::StoreBufferFormatF32x2: - case Opcode::StoreBufferFormatF32x3: - case Opcode::StoreBufferFormatF32x4: case Opcode::StoreBufferU32: case Opcode::BufferAtomicIAdd32: case Opcode::BufferAtomicSMin32: @@ -70,7 +67,7 @@ bool Inst::MayHaveSideEffects() const noexcept { case Opcode::BufferAtomicAnd32: case Opcode::BufferAtomicOr32: case Opcode::BufferAtomicXor32: - case Opcode::BufferAtomicExchange32: + case Opcode::BufferAtomicSwap32: case Opcode::WriteSharedU128: case Opcode::WriteSharedU64: case Opcode::WriteSharedU32: diff --git a/src/shader_recompiler/ir/opcodes.h b/src/shader_recompiler/ir/opcodes.h index 66b602219..06f1a6117 100644 --- a/src/shader_recompiler/ir/opcodes.h +++ b/src/shader_recompiler/ir/opcodes.h @@ -101,7 +101,7 @@ struct fmt::formatter { return ctx.begin(); } template - auto format(const Shader::IR::Opcode& op, FormatContext& ctx) const { + auto format(const Shader::IR::Opcode op, FormatContext& ctx) const { return fmt::format_to(ctx.out(), "{}", Shader::IR::NameOf(op)); } }; diff --git a/src/shader_recompiler/ir/opcodes.inc b/src/shader_recompiler/ir/opcodes.inc index e9ecd4350..40dcfa441 100644 --- a/src/shader_recompiler/ir/opcodes.inc +++ b/src/shader_recompiler/ir/opcodes.inc @@ -58,7 +58,6 @@ OPCODE(SetAttribute, Void, Attr OPCODE(GetScc, U1, Void, ) OPCODE(GetExec, U1, Void, ) OPCODE(GetVcc, U1, Void, ) -OPCODE(GetSccLo, U32, Void, ) OPCODE(GetVccLo, U32, Void, ) OPCODE(GetVccHi, U32, Void, ) OPCODE(SetScc, Void, U1, ) @@ -80,23 +79,17 @@ OPCODE(LoadBufferF32, F32, Opaq OPCODE(LoadBufferF32x2, F32x2, Opaque, Opaque, ) OPCODE(LoadBufferF32x3, F32x3, Opaque, Opaque, ) OPCODE(LoadBufferF32x4, F32x4, Opaque, Opaque, ) -OPCODE(LoadBufferFormatF32, F32, Opaque, Opaque, ) -OPCODE(LoadBufferFormatF32x2, F32x2, Opaque, Opaque, ) -OPCODE(LoadBufferFormatF32x3, F32x3, Opaque, Opaque, ) -OPCODE(LoadBufferFormatF32x4, F32x4, Opaque, Opaque, ) +OPCODE(LoadBufferFormatF32, F32x4, Opaque, Opaque, ) OPCODE(LoadBufferU32, U32, Opaque, Opaque, ) OPCODE(StoreBufferF32, Void, Opaque, Opaque, F32, ) OPCODE(StoreBufferF32x2, Void, Opaque, Opaque, F32x2, ) OPCODE(StoreBufferF32x3, Void, Opaque, Opaque, F32x3, ) OPCODE(StoreBufferF32x4, Void, Opaque, Opaque, F32x4, ) -OPCODE(StoreBufferFormatF32, Void, Opaque, Opaque, F32, ) -OPCODE(StoreBufferFormatF32x2, Void, Opaque, Opaque, F32x2, ) -OPCODE(StoreBufferFormatF32x3, Void, Opaque, Opaque, F32x3, ) -OPCODE(StoreBufferFormatF32x4, Void, Opaque, Opaque, F32x4, ) +OPCODE(StoreBufferFormatF32, Void, Opaque, Opaque, F32x4, ) OPCODE(StoreBufferU32, Void, Opaque, Opaque, U32, ) // Buffer atomic operations -OPCODE(BufferAtomicIAdd32, U32, Opaque, Opaque, U32 ) +OPCODE(BufferAtomicIAdd32, U32, Opaque, Opaque, U32 ) OPCODE(BufferAtomicSMin32, U32, Opaque, Opaque, U32 ) OPCODE(BufferAtomicUMin32, U32, Opaque, Opaque, U32 ) OPCODE(BufferAtomicSMax32, U32, Opaque, Opaque, U32 ) @@ -106,7 +99,7 @@ OPCODE(BufferAtomicDec32, U32, Opaq OPCODE(BufferAtomicAnd32, U32, Opaque, Opaque, U32, ) OPCODE(BufferAtomicOr32, U32, Opaque, Opaque, U32, ) OPCODE(BufferAtomicXor32, U32, Opaque, Opaque, U32, ) -OPCODE(BufferAtomicExchange32, U32, Opaque, Opaque, U32, ) +OPCODE(BufferAtomicSwap32, U32, Opaque, Opaque, U32, ) // Vector utility OPCODE(CompositeConstructU32x2, U32x2, U32, U32, ) @@ -261,6 +254,7 @@ OPCODE(ShiftRightLogical64, U64, U64, OPCODE(ShiftRightArithmetic32, U32, U32, U32, ) OPCODE(ShiftRightArithmetic64, U64, U64, U32, ) OPCODE(BitwiseAnd32, U32, U32, U32, ) +OPCODE(BitwiseAnd64, U64, U64, U64, ) OPCODE(BitwiseOr32, U32, U32, U32, ) OPCODE(BitwiseOr64, U64, U64, U64, ) OPCODE(BitwiseXor32, U32, U32, U32, ) @@ -330,19 +324,22 @@ OPCODE(ImageRead, U32x4, Opaq OPCODE(ImageWrite, Void, Opaque, Opaque, U32x4, ) // Image atomic operations -OPCODE(ImageAtomicIAdd32, U32, Opaque, Opaque, U32, ) -OPCODE(ImageAtomicSMin32, U32, Opaque, Opaque, U32, ) -OPCODE(ImageAtomicUMin32, U32, Opaque, Opaque, U32, ) -OPCODE(ImageAtomicSMax32, U32, Opaque, Opaque, U32, ) -OPCODE(ImageAtomicUMax32, U32, Opaque, Opaque, U32, ) -OPCODE(ImageAtomicInc32, U32, Opaque, Opaque, U32, ) -OPCODE(ImageAtomicDec32, U32, Opaque, Opaque, U32, ) -OPCODE(ImageAtomicAnd32, U32, Opaque, Opaque, U32, ) -OPCODE(ImageAtomicOr32, U32, Opaque, Opaque, U32, ) -OPCODE(ImageAtomicXor32, U32, Opaque, Opaque, U32, ) -OPCODE(ImageAtomicExchange32, U32, Opaque, Opaque, U32, ) +OPCODE(ImageAtomicIAdd32, U32, Opaque, Opaque, U32, ) +OPCODE(ImageAtomicSMin32, U32, Opaque, Opaque, U32, ) +OPCODE(ImageAtomicUMin32, U32, Opaque, Opaque, U32, ) +OPCODE(ImageAtomicSMax32, U32, Opaque, Opaque, U32, ) +OPCODE(ImageAtomicUMax32, U32, Opaque, Opaque, U32, ) +OPCODE(ImageAtomicInc32, U32, Opaque, Opaque, U32, ) +OPCODE(ImageAtomicDec32, U32, Opaque, Opaque, U32, ) +OPCODE(ImageAtomicAnd32, U32, Opaque, Opaque, U32, ) +OPCODE(ImageAtomicOr32, U32, Opaque, Opaque, U32, ) +OPCODE(ImageAtomicXor32, U32, Opaque, Opaque, U32, ) +OPCODE(ImageAtomicExchange32, U32, Opaque, Opaque, U32, ) // Warp operations OPCODE(LaneId, U32, ) OPCODE(WarpId, U32, ) OPCODE(QuadShuffle, U32, U32, U32 ) +OPCODE(ReadFirstLane, U32, U32, ) +OPCODE(ReadLane, U32, U32, U32 ) +OPCODE(WriteLane, U32, U32, U32, U32 ) diff --git a/src/shader_recompiler/ir/passes/constant_propogation_pass.cpp b/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp similarity index 95% rename from src/shader_recompiler/ir/passes/constant_propogation_pass.cpp rename to src/shader_recompiler/ir/passes/constant_propagation_pass.cpp index 94218b32f..87a069338 100644 --- a/src/shader_recompiler/ir/passes/constant_propogation_pass.cpp +++ b/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp @@ -250,6 +250,18 @@ void FoldCmpClass(IR::Inst& inst) { } } +void FoldReadLane(IR::Inst& inst) { + const u32 lane = inst.Arg(1).U32(); + IR::Inst* prod = inst.Arg(0).InstRecursive(); + while (prod->GetOpcode() == IR::Opcode::WriteLane) { + if (prod->Arg(2).U32() == lane) { + inst.ReplaceUsesWith(prod->Arg(1)); + return; + } + prod = prod->Arg(0).InstRecursive(); + } +} + void ConstantPropagation(IR::Block& block, IR::Inst& inst) { switch (inst.GetOpcode()) { case IR::Opcode::IAdd32: @@ -289,6 +301,8 @@ void ConstantPropagation(IR::Block& block, IR::Inst& inst) { case IR::Opcode::SelectF32: case IR::Opcode::SelectF64: return FoldSelect(inst); + case IR::Opcode::ReadLane: + return FoldReadLane(inst); case IR::Opcode::FPNeg32: FoldWhenAllImmediates(inst, [](f32 a) { return -a; }); return; @@ -338,9 +352,15 @@ void ConstantPropagation(IR::Block& block, IR::Inst& inst) { case IR::Opcode::BitwiseAnd32: FoldWhenAllImmediates(inst, [](u32 a, u32 b) { return a & b; }); return; + case IR::Opcode::BitwiseAnd64: + FoldWhenAllImmediates(inst, [](u64 a, u64 b) { return a & b; }); + return; case IR::Opcode::BitwiseOr32: FoldWhenAllImmediates(inst, [](u32 a, u32 b) { return a | b; }); return; + case IR::Opcode::BitwiseOr64: + FoldWhenAllImmediates(inst, [](u64 a, u64 b) { return a | b; }); + return; case IR::Opcode::BitwiseXor32: FoldWhenAllImmediates(inst, [](u32 a, u32 b) { return a ^ b; }); return; diff --git a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp index 20a66ad0c..f446ac476 100644 --- a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp +++ b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp @@ -3,6 +3,7 @@ #include #include +#include "common/alignment.h" #include "shader_recompiler/ir/basic_block.h" #include "shader_recompiler/ir/breadth_first_search.h" #include "shader_recompiler/ir/ir_emitter.h" @@ -32,7 +33,7 @@ bool IsBufferAtomic(const IR::Inst& inst) { case IR::Opcode::BufferAtomicAnd32: case IR::Opcode::BufferAtomicOr32: case IR::Opcode::BufferAtomicXor32: - case IR::Opcode::BufferAtomicExchange32: + case IR::Opcode::BufferAtomicSwap32: return true; default: return false; @@ -45,10 +46,6 @@ bool IsBufferStore(const IR::Inst& inst) { case IR::Opcode::StoreBufferF32x2: case IR::Opcode::StoreBufferF32x3: case IR::Opcode::StoreBufferF32x4: - case IR::Opcode::StoreBufferFormatF32: - case IR::Opcode::StoreBufferFormatF32x2: - case IR::Opcode::StoreBufferFormatF32x3: - case IR::Opcode::StoreBufferFormatF32x4: case IR::Opcode::StoreBufferU32: return true; default: @@ -62,10 +59,6 @@ bool IsBufferInstruction(const IR::Inst& inst) { case IR::Opcode::LoadBufferF32x2: case IR::Opcode::LoadBufferF32x3: case IR::Opcode::LoadBufferF32x4: - case IR::Opcode::LoadBufferFormatF32: - case IR::Opcode::LoadBufferFormatF32x2: - case IR::Opcode::LoadBufferFormatF32x3: - case IR::Opcode::LoadBufferFormatF32x4: case IR::Opcode::LoadBufferU32: case IR::Opcode::ReadConstBuffer: case IR::Opcode::ReadConstBufferU32: @@ -75,6 +68,11 @@ bool IsBufferInstruction(const IR::Inst& inst) { } } +bool IsTextureBufferInstruction(const IR::Inst& inst) { + return inst.GetOpcode() == IR::Opcode::LoadBufferFormatF32 || + inst.GetOpcode() == IR::Opcode::StoreBufferFormatF32; +} + static bool UseFP16(AmdGpu::DataFormat data_format, AmdGpu::NumberFormat num_format) { switch (num_format) { case AmdGpu::NumberFormat::Float: @@ -100,28 +98,6 @@ static bool UseFP16(AmdGpu::DataFormat data_format, AmdGpu::NumberFormat num_for IR::Type BufferDataType(const IR::Inst& inst, AmdGpu::NumberFormat num_format) { switch (inst.GetOpcode()) { - case IR::Opcode::LoadBufferFormatF32: - case IR::Opcode::LoadBufferFormatF32x2: - case IR::Opcode::LoadBufferFormatF32x3: - case IR::Opcode::LoadBufferFormatF32x4: - case IR::Opcode::StoreBufferFormatF32: - case IR::Opcode::StoreBufferFormatF32x2: - case IR::Opcode::StoreBufferFormatF32x3: - case IR::Opcode::StoreBufferFormatF32x4: - switch (num_format) { - case AmdGpu::NumberFormat::Unorm: - case AmdGpu::NumberFormat::Snorm: - case AmdGpu::NumberFormat::Uscaled: - case AmdGpu::NumberFormat::Sscaled: - case AmdGpu::NumberFormat::Uint: - case AmdGpu::NumberFormat::Sint: - case AmdGpu::NumberFormat::SnormNz: - return IR::Type::U32; - case AmdGpu::NumberFormat::Float: - return IR::Type::F32; - default: - UNREACHABLE(); - } case IR::Opcode::LoadBufferF32: case IR::Opcode::LoadBufferF32x2: case IR::Opcode::LoadBufferF32x3: @@ -136,26 +112,15 @@ IR::Type BufferDataType(const IR::Inst& inst, AmdGpu::NumberFormat num_format) { case IR::Opcode::ReadConstBufferU32: case IR::Opcode::StoreBufferU32: case IR::Opcode::BufferAtomicIAdd32: + case IR::Opcode::BufferAtomicSwap32: return IR::Type::U32; default: UNREACHABLE(); } } -bool IsImageInstruction(const IR::Inst& inst) { +bool IsImageAtomicInstruction(const IR::Inst& inst) { switch (inst.GetOpcode()) { - case IR::Opcode::ImageSampleExplicitLod: - case IR::Opcode::ImageSampleImplicitLod: - case IR::Opcode::ImageSampleDrefExplicitLod: - case IR::Opcode::ImageSampleDrefImplicitLod: - case IR::Opcode::ImageFetch: - case IR::Opcode::ImageGather: - case IR::Opcode::ImageGatherDref: - case IR::Opcode::ImageQueryDimensions: - case IR::Opcode::ImageQueryLod: - case IR::Opcode::ImageGradient: - case IR::Opcode::ImageRead: - case IR::Opcode::ImageWrite: case IR::Opcode::ImageAtomicIAdd32: case IR::Opcode::ImageAtomicSMin32: case IR::Opcode::ImageAtomicUMin32: @@ -177,20 +142,27 @@ bool IsImageStorageInstruction(const IR::Inst& inst) { switch (inst.GetOpcode()) { case IR::Opcode::ImageWrite: case IR::Opcode::ImageRead: - case IR::Opcode::ImageAtomicIAdd32: - case IR::Opcode::ImageAtomicSMin32: - case IR::Opcode::ImageAtomicUMin32: - case IR::Opcode::ImageAtomicSMax32: - case IR::Opcode::ImageAtomicUMax32: - case IR::Opcode::ImageAtomicInc32: - case IR::Opcode::ImageAtomicDec32: - case IR::Opcode::ImageAtomicAnd32: - case IR::Opcode::ImageAtomicOr32: - case IR::Opcode::ImageAtomicXor32: - case IR::Opcode::ImageAtomicExchange32: return true; default: - return false; + return IsImageAtomicInstruction(inst); + } +} + +bool IsImageInstruction(const IR::Inst& inst) { + switch (inst.GetOpcode()) { + case IR::Opcode::ImageSampleExplicitLod: + case IR::Opcode::ImageSampleImplicitLod: + case IR::Opcode::ImageSampleDrefExplicitLod: + case IR::Opcode::ImageSampleDrefImplicitLod: + case IR::Opcode::ImageFetch: + case IR::Opcode::ImageGather: + case IR::Opcode::ImageGatherDref: + case IR::Opcode::ImageQueryDimensions: + case IR::Opcode::ImageQueryLod: + case IR::Opcode::ImageGradient: + return true; + default: + return IsImageStorageInstruction(inst); } } @@ -213,7 +185,8 @@ u32 ImageOffsetArgumentPosition(const IR::Inst& inst) { class Descriptors { public: explicit Descriptors(Info& info_) - : info{info_}, buffer_resources{info_.buffers}, image_resources{info_.images}, + : info{info_}, buffer_resources{info_.buffers}, + texture_buffer_resources{info_.texture_buffers}, image_resources{info_.images}, sampler_resources{info_.samplers} {} u32 Add(const BufferResource& desc) { @@ -223,13 +196,21 @@ public: desc.inline_cbuf == existing.inline_cbuf; })}; auto& buffer = buffer_resources[index]; - ASSERT(buffer.length == desc.length); - buffer.is_storage |= desc.is_storage; buffer.used_types |= desc.used_types; buffer.is_written |= desc.is_written; return index; } + u32 Add(const TextureBufferResource& desc) { + const u32 index{Add(texture_buffer_resources, desc, [&desc](const auto& existing) { + return desc.sgpr_base == existing.sgpr_base && + desc.dword_offset == existing.dword_offset; + })}; + auto& buffer = texture_buffer_resources[index]; + buffer.is_written |= desc.is_written; + return index; + } + u32 Add(const ImageResource& desc) { const u32 index{Add(image_resources, desc, [&desc](const auto& existing) { return desc.sgpr_base == existing.sgpr_base && @@ -246,10 +227,7 @@ public: return true; } // Samplers with different bindings might still be the same. - const auto old_sharp = - info.ReadUd(existing.sgpr_base, existing.dword_offset); - const auto new_sharp = info.ReadUd(desc.sgpr_base, desc.dword_offset); - return old_sharp == new_sharp; + return existing.GetSharp(info) == desc.GetSharp(info); })}; return index; } @@ -267,6 +245,7 @@ private: const Info& info; BufferResourceList& buffer_resources; + TextureBufferResourceList& texture_buffer_resources; ImageResourceList& image_resources; SamplerResourceList& sampler_resources; }; @@ -295,10 +274,11 @@ std::pair TryDisableAnisoLod0(const IR::Inst* inst) { return not_found; } - // The bits range is for lods + // The bits range is for lods (note that constants are changed after constant propagation pass) const auto* prod0_arg0 = prod0->Arg(0).InstRecursive(); if (prod0_arg0->GetOpcode() != IR::Opcode::BitFieldUExtract || - prod0_arg0->Arg(1).InstRecursive()->Arg(0).U32() != 0x0008000cu) { + !(prod0_arg0->Arg(1).IsIdentity() && prod0_arg0->Arg(1).U32() == 12) || + !(prod0_arg0->Arg(2).IsIdentity() && prod0_arg0->Arg(2).U32() == 8)) { return not_found; } @@ -345,6 +325,7 @@ SharpLocation TrackSharp(const IR::Inst* inst) { // Retrieve SGPR pair that holds sbase const auto pred1 = [](const IR::Inst* inst) -> std::optional { + ASSERT(inst->GetOpcode() != IR::Opcode::ReadConst); if (inst->GetOpcode() == IR::Opcode::GetUserData) { return inst->Arg(0).ScalarReg(); } @@ -361,33 +342,6 @@ SharpLocation TrackSharp(const IR::Inst* inst) { }; } -static constexpr size_t MaxUboSize = 65536; - -static bool IsLoadBufferFormat(const IR::Inst& inst) { - switch (inst.GetOpcode()) { - case IR::Opcode::LoadBufferFormatF32: - case IR::Opcode::LoadBufferFormatF32x2: - case IR::Opcode::LoadBufferFormatF32x3: - case IR::Opcode::LoadBufferFormatF32x4: - return true; - default: - return false; - } -} - -static u32 BufferLength(const AmdGpu::Buffer& buffer) { - const auto stride = buffer.GetStride(); - if (stride < sizeof(f32)) { - ASSERT(sizeof(f32) % stride == 0); - return (((buffer.num_records - 1) / sizeof(f32)) + 1) * stride; - } else if (stride == sizeof(f32)) { - return buffer.num_records; - } else { - ASSERT(stride % sizeof(f32) == 0); - return buffer.num_records * (stride / sizeof(f32)); - } -} - s32 TryHandleInlineCbuf(IR::Inst& inst, Info& info, Descriptors& descriptors, AmdGpu::Buffer& cbuf) { @@ -402,33 +356,20 @@ s32 TryHandleInlineCbuf(IR::Inst& inst, Info& info, Descriptors& descriptors, // is used to define an inline constant buffer IR::Inst* handle = inst.Arg(0).InstRecursive(); - IR::Inst* p0 = handle->Arg(0).InstRecursive(); - if (p0->GetOpcode() != IR::Opcode::IAdd32 || !p0->Arg(0).IsImmediate() || - !p0->Arg(1).IsImmediate()) { - return -1; - } - IR::Inst* p1 = handle->Arg(1).InstRecursive(); - if (p1->GetOpcode() != IR::Opcode::IAdd32) { - return -1; - } - if (!handle->Arg(3).IsImmediate() || !handle->Arg(2).IsImmediate()) { + if (!handle->AreAllArgsImmediates()) { return -1; } // We have found this pattern. Build the sharp. - std::array buffer; - buffer[0] = info.pgm_base + p0->Arg(0).U32() + p0->Arg(1).U32(); - buffer[1] = 0; - buffer[2] = handle->Arg(2).U32(); - buffer[3] = handle->Arg(3).U32(); + std::array buffer; + buffer[0] = info.pgm_base + (handle->Arg(0).U32() | u64(handle->Arg(1).U32()) << 32); + buffer[1] = handle->Arg(2).U32() | u64(handle->Arg(3).U32()) << 32; cbuf = std::bit_cast(buffer); // Assign a binding to this sharp. return descriptors.Add(BufferResource{ .sgpr_base = std::numeric_limits::max(), .dword_offset = 0, - .length = BufferLength(cbuf), .used_types = BufferDataType(inst, cbuf.GetNumberFmt()), .inline_cbuf = cbuf, - .is_storage = IsBufferStore(inst) || cbuf.GetSize() > MaxUboSize, }); } @@ -440,28 +381,17 @@ void PatchBufferInstruction(IR::Block& block, IR::Inst& inst, Info& info, IR::Inst* handle = inst.Arg(0).InstRecursive(); IR::Inst* producer = handle->Arg(0).InstRecursive(); const auto sharp = TrackSharp(producer); - const bool is_store = IsBufferStore(inst); buffer = info.ReadUd(sharp.sgpr_base, sharp.dword_offset); binding = descriptors.Add(BufferResource{ .sgpr_base = sharp.sgpr_base, .dword_offset = sharp.dword_offset, - .length = BufferLength(buffer), .used_types = BufferDataType(inst, buffer.GetNumberFmt()), - .is_storage = is_store || buffer.GetSize() > MaxUboSize, - .is_written = is_store, + .is_written = IsBufferStore(inst), }); } // Update buffer descriptor format. const auto inst_info = inst.Flags(); - auto& buffer_desc = info.buffers[binding]; - if (inst_info.is_typed) { - buffer_desc.dfmt = inst_info.dmft; - buffer_desc.nfmt = inst_info.nfmt; - } else { - buffer_desc.dfmt = buffer.GetDataFmt(); - buffer_desc.nfmt = buffer.GetNumberFmt(); - } // Replace handle with binding index in buffer resource list. IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; @@ -474,20 +404,7 @@ void PatchBufferInstruction(IR::Block& block, IR::Inst& inst, Info& info, return; } - if (IsLoadBufferFormat(inst)) { - if (UseFP16(buffer.GetDataFmt(), buffer.GetNumberFmt())) { - info.uses_fp16 = true; - } - } else { - const u32 stride = buffer.GetStride(); - if (stride < 4) { - LOG_WARNING(Render_Vulkan, - "non-formatting load_buffer_* is not implemented for stride {}", stride); - } - } - // Compute address of the buffer using the stride. - // Todo: What if buffer is rebound with different stride? IR::U32 address = ir.Imm32(inst_info.inst_offset.Value()); if (inst_info.index_enable) { const IR::U32 index = inst_info.offset_enable ? IR::U32{ir.CompositeExtract(inst.Arg(1), 0)} @@ -502,8 +419,31 @@ void PatchBufferInstruction(IR::Block& block, IR::Inst& inst, Info& info, inst.SetArg(1, address); } +void PatchTextureBufferInstruction(IR::Block& block, IR::Inst& inst, Info& info, + Descriptors& descriptors) { + const IR::Inst* handle = inst.Arg(0).InstRecursive(); + const IR::Inst* producer = handle->Arg(0).InstRecursive(); + const auto sharp = TrackSharp(producer); + const auto buffer = info.ReadUd(sharp.sgpr_base, sharp.dword_offset); + const s32 binding = descriptors.Add(TextureBufferResource{ + .sgpr_base = sharp.sgpr_base, + .dword_offset = sharp.dword_offset, + .nfmt = buffer.GetNumberFmt(), + .is_written = inst.GetOpcode() == IR::Opcode::StoreBufferFormatF32, + }); + + // Replace handle with binding index in texture buffer resource list. + IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; + inst.SetArg(0, ir.Imm32(binding)); + ASSERT(!buffer.swizzle_enable && !buffer.add_tid_enable); +} + IR::Value PatchCubeCoord(IR::IREmitter& ir, const IR::Value& s, const IR::Value& t, - const IR::Value& z) { + const IR::Value& z, bool is_storage) { + // When cubemap is written with imageStore it is treated like 2DArray. + if (is_storage) { + return ir.CompositeConstruct(s, t, z); + } // We need to fix x and y coordinate, // because the s and t coordinate will be scaled and plus 1.5 by v_madak_f32. // We already force the scale value to be 1.0 when handling v_cubema_f32, @@ -541,13 +481,15 @@ void PatchImageInstruction(IR::Block& block, IR::Inst& inst, Info& info, Descrip return; } ASSERT(image.GetType() != AmdGpu::ImageType::Invalid); + const bool is_storage = IsImageStorageInstruction(inst); u32 image_binding = descriptors.Add(ImageResource{ .sgpr_base = tsharp.sgpr_base, .dword_offset = tsharp.dword_offset, .type = image.GetType(), .nfmt = static_cast(image.GetNumberFmt()), - .is_storage = IsImageStorageInstruction(inst), + .is_storage = is_storage, .is_depth = bool(inst_info.is_depth), + .is_atomic = IsImageAtomicInstruction(inst), }); // Read sampler sharp. This doesn't exist for IMAGE_LOAD/IMAGE_STORE instructions @@ -604,7 +546,8 @@ void PatchImageInstruction(IR::Block& block, IR::Inst& inst, Info& info, Descrip case AmdGpu::ImageType::Color3D: // x, y, z return {ir.CompositeConstruct(body->Arg(0), body->Arg(1), body->Arg(2)), body->Arg(3)}; case AmdGpu::ImageType::Cube: // x, y, face - return {PatchCubeCoord(ir, body->Arg(0), body->Arg(1), body->Arg(2)), body->Arg(3)}; + return {PatchCubeCoord(ir, body->Arg(0), body->Arg(1), body->Arg(2), is_storage), + body->Arg(3)}; default: UNREACHABLE_MSG("Unknown image type {}", image.GetType()); } @@ -617,7 +560,11 @@ void PatchImageInstruction(IR::Block& block, IR::Inst& inst, Info& info, Descrip const IR::Value arg = inst.Arg(arg_pos); ASSERT_MSG(arg.Type() == IR::Type::U32, "Unexpected offset type"); - const auto read = [&](u32 offset) -> auto { + const auto read = [&](u32 offset) -> IR::U32 { + if (arg.IsImmediate()) { + const u16 comp = (arg.U32() >> offset) & 0x3F; + return ir.Imm32(s32(comp << 26) >> 26); + } return ir.BitFieldExtract(IR::U32{arg}, ir.Imm32(offset), ir.Imm32(6), true); }; @@ -637,7 +584,10 @@ void PatchImageInstruction(IR::Block& block, IR::Inst& inst, Info& info, Descrip UNREACHABLE(); } } - + if (inst_info.has_derivatives) { + ASSERT_MSG(image.GetType() == AmdGpu::ImageType::Color2D, + "User derivatives only supported for 2D images"); + } if (inst_info.has_lod_clamp) { const u32 arg_pos = [&]() -> u32 { switch (inst.GetOpcode()) { @@ -672,6 +622,10 @@ void ResourceTrackingPass(IR::Program& program) { PatchBufferInstruction(*block, inst, info, descriptors); continue; } + if (IsTextureBufferInstruction(inst)) { + PatchTextureBufferInstruction(*block, inst, info, descriptors); + continue; + } if (IsImageInstruction(inst)) { PatchImageInstruction(*block, inst, info, descriptors); } diff --git a/src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp b/src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp index 52087a653..7105f01f1 100644 --- a/src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp +++ b/src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp @@ -29,6 +29,12 @@ void Visit(Info& info, IR::Inst& inst) { case IR::Opcode::ImageWrite: info.has_storage_images = true; break; + case IR::Opcode::LoadBufferFormatF32: + info.has_texel_buffers = true; + break; + case IR::Opcode::StoreBufferFormatF32: + info.has_image_buffers = true; + break; case IR::Opcode::QuadShuffle: info.uses_group_quad = true; break; @@ -44,6 +50,9 @@ void Visit(Info& info, IR::Inst& inst) { case IR::Opcode::ImageQueryLod: info.has_image_query = true; break; + case IR::Opcode::LaneId: + info.uses_lane_id = true; + break; default: break; } diff --git a/src/shader_recompiler/ir/passes/ssa_rewrite_pass.cpp b/src/shader_recompiler/ir/passes/ssa_rewrite_pass.cpp index eef73a659..9edb157db 100644 --- a/src/shader_recompiler/ir/passes/ssa_rewrite_pass.cpp +++ b/src/shader_recompiler/ir/passes/ssa_rewrite_pass.cpp @@ -32,7 +32,6 @@ struct SccFlagTag : FlagTag {}; struct ExecFlagTag : FlagTag {}; struct VccFlagTag : FlagTag {}; struct VccLoTag : FlagTag {}; -struct SccLoTag : FlagTag {}; struct VccHiTag : FlagTag {}; struct GotoVariable : FlagTag { @@ -45,7 +44,7 @@ struct GotoVariable : FlagTag { }; using Variant = std::variant; + VccFlagTag, VccLoTag, VccHiTag>; using ValueMap = std::unordered_map; struct DefTable { @@ -84,13 +83,6 @@ struct DefTable { exec_flag.insert_or_assign(block, value); } - const IR::Value& Def(IR::Block* block, SccLoTag) { - return scc_lo_flag[block]; - } - void SetDef(IR::Block* block, SccLoTag, const IR::Value& value) { - scc_lo_flag.insert_or_assign(block, value); - } - const IR::Value& Def(IR::Block* block, VccLoTag) { return vcc_lo_flag[block]; } @@ -133,10 +125,6 @@ IR::Opcode UndefOpcode(const VccLoTag) noexcept { return IR::Opcode::UndefU32; } -IR::Opcode UndefOpcode(const SccLoTag) noexcept { - return IR::Opcode::UndefU32; -} - IR::Opcode UndefOpcode(const VccHiTag) noexcept { return IR::Opcode::UndefU32; } @@ -336,9 +324,6 @@ void VisitInst(Pass& pass, IR::Block* block, IR::Inst& inst) { case IR::Opcode::SetVcc: pass.WriteVariable(VccFlagTag{}, block, inst.Arg(0)); break; - case IR::Opcode::SetSccLo: - pass.WriteVariable(SccLoTag{}, block, inst.Arg(0)); - break; case IR::Opcode::SetVccLo: pass.WriteVariable(VccLoTag{}, block, inst.Arg(0)); break; @@ -371,9 +356,6 @@ void VisitInst(Pass& pass, IR::Block* block, IR::Inst& inst) { case IR::Opcode::GetVcc: inst.ReplaceUsesWith(pass.ReadVariable(VccFlagTag{}, block)); break; - case IR::Opcode::GetSccLo: - inst.ReplaceUsesWith(pass.ReadVariable(SccLoTag{}, block)); - break; case IR::Opcode::GetVccLo: inst.ReplaceUsesWith(pass.ReadVariable(VccLoTag{}, block)); break; diff --git a/src/shader_recompiler/ir/program.h b/src/shader_recompiler/ir/program.h index eff933f28..f7abba641 100644 --- a/src/shader_recompiler/ir/program.h +++ b/src/shader_recompiler/ir/program.h @@ -12,11 +12,13 @@ namespace Shader::IR { struct Program { + explicit Program(Info& info_) : info{info_} {} + AbstractSyntaxList syntax_list; BlockList blocks; BlockList post_order_blocks; std::vector ins_list; - Info info; + Info& info; }; [[nodiscard]] std::string DumpProgram(const Program& program); diff --git a/src/shader_recompiler/ir/reg.h b/src/shader_recompiler/ir/reg.h index e3d04260b..fba04f33e 100644 --- a/src/shader_recompiler/ir/reg.h +++ b/src/shader_recompiler/ir/reg.h @@ -58,6 +58,7 @@ union TextureInstInfo { BitField<4, 1, u32> explicit_lod; BitField<5, 1, u32> has_offset; BitField<6, 2, u32> gather_comp; + BitField<8, 1, u32> has_derivatives; }; union BufferInstInfo { @@ -65,9 +66,6 @@ union BufferInstInfo { BitField<0, 1, u32> index_enable; BitField<1, 1, u32> offset_enable; BitField<2, 12, u32> inst_offset; - BitField<14, 4, AmdGpu::DataFormat> dmft; - BitField<18, 3, AmdGpu::NumberFormat> nfmt; - BitField<21, 1, u32> is_typed; }; enum class ScalarReg : u32 { diff --git a/src/shader_recompiler/recompiler.cpp b/src/shader_recompiler/recompiler.cpp index 0f9fd6d41..dfcf9ed1b 100644 --- a/src/shader_recompiler/recompiler.cpp +++ b/src/shader_recompiler/recompiler.cpp @@ -29,7 +29,7 @@ IR::BlockList GenerateBlocks(const IR::AbstractSyntaxList& syntax_list) { IR::Program TranslateProgram(Common::ObjectPool& inst_pool, Common::ObjectPool& block_pool, std::span token, - const Info&& info, const Profile& profile) { + Info& info, const Profile& profile) { // Ensure first instruction is expected. constexpr u32 token_mov_vcchi = 0xBEEB03FF; ASSERT_MSG(token[0] == token_mov_vcchi, "First instruction is not s_mov_b32 vcc_hi, #imm"); @@ -38,7 +38,7 @@ IR::Program TranslateProgram(Common::ObjectPool& inst_pool, Gcn::GcnDecodeContext decoder; // Decode and save instructions - IR::Program program; + IR::Program program{info}; program.ins_list.reserve(token.size()); while (!slice.atEnd()) { program.ins_list.emplace_back(decoder.decodeInstruction(slice)); @@ -49,18 +49,17 @@ IR::Program TranslateProgram(Common::ObjectPool& inst_pool, Gcn::CFG cfg{gcn_block_pool, program.ins_list}; // Structurize control flow graph and create program. - program.info = std::move(info); program.syntax_list = Shader::Gcn::BuildASL(inst_pool, block_pool, cfg, program.info, profile); program.blocks = GenerateBlocks(program.syntax_list); program.post_order_blocks = Shader::IR::PostOrder(program.syntax_list.front()); // Run optimization passes Shader::Optimization::SsaRewritePass(program.post_order_blocks); - Shader::Optimization::ResourceTrackingPass(program); Shader::Optimization::ConstantPropagationPass(program.post_order_blocks); if (program.info.stage != Stage::Compute) { Shader::Optimization::LowerSharedMemToRegisters(program); } + Shader::Optimization::ResourceTrackingPass(program); Shader::Optimization::IdentityRemovalPass(program.blocks); Shader::Optimization::DeadCodeEliminationPass(program); Shader::Optimization::CollectShaderInfoPass(program); diff --git a/src/shader_recompiler/recompiler.h b/src/shader_recompiler/recompiler.h index 34e958a15..3a2295189 100644 --- a/src/shader_recompiler/recompiler.h +++ b/src/shader_recompiler/recompiler.h @@ -13,7 +13,7 @@ struct Profile; [[nodiscard]] IR::Program TranslateProgram(Common::ObjectPool& inst_pool, Common::ObjectPool& block_pool, - std::span code, const Info&& info, + std::span code, Info& info, const Profile& profile); } // namespace Shader diff --git a/src/shader_recompiler/runtime_info.h b/src/shader_recompiler/runtime_info.h index b1eb6aea7..77c57e947 100644 --- a/src/shader_recompiler/runtime_info.h +++ b/src/shader_recompiler/runtime_info.h @@ -4,6 +4,7 @@ #pragma once #include +#include #include #include "common/assert.h" #include "common/types.h" @@ -74,18 +75,29 @@ struct Info; struct BufferResource { u32 sgpr_base; u32 dword_offset; - u32 length; IR::Type used_types; AmdGpu::Buffer inline_cbuf; - AmdGpu::DataFormat dfmt; - AmdGpu::NumberFormat nfmt; - bool is_storage{}; bool is_instance_data{}; bool is_written{}; - constexpr AmdGpu::Buffer GetVsharp(const Info& info) const noexcept; + bool IsStorage(AmdGpu::Buffer buffer) const noexcept { + static constexpr size_t MaxUboSize = 65536; + return buffer.GetSize() > MaxUboSize || is_written; + } + + constexpr AmdGpu::Buffer GetSharp(const Info& info) const noexcept; }; -using BufferResourceList = boost::container::static_vector; +using BufferResourceList = boost::container::small_vector; + +struct TextureBufferResource { + u32 sgpr_base; + u32 dword_offset; + AmdGpu::NumberFormat nfmt; + bool is_written{}; + + constexpr AmdGpu::Buffer GetSharp(const Info& info) const noexcept; +}; +using TextureBufferResourceList = boost::container::small_vector; struct ImageResource { u32 sgpr_base; @@ -94,8 +106,11 @@ struct ImageResource { AmdGpu::NumberFormat nfmt; bool is_storage; bool is_depth; + bool is_atomic{}; + + constexpr AmdGpu::Image GetSharp(const Info& info) const noexcept; }; -using ImageResourceList = boost::container::static_vector; +using ImageResourceList = boost::container::small_vector; struct SamplerResource { u32 sgpr_base; @@ -104,9 +119,9 @@ struct SamplerResource { u32 associated_image : 4; u32 disable_aniso : 1; - constexpr AmdGpu::Sampler GetSsharp(const Info& info) const noexcept; + constexpr AmdGpu::Sampler GetSharp(const Info& info) const noexcept; }; -using SamplerResourceList = boost::container::static_vector; +using SamplerResourceList = boost::container::small_vector; struct PushData { static constexpr size_t BufOffsetIndex = 2; @@ -175,7 +190,11 @@ struct Info { AttributeFlags stores{}; boost::container::static_vector vs_outputs; + s8 vertex_offset_sgpr = -1; + s8 instance_offset_sgpr = -1; + BufferResourceList buffers; + TextureBufferResourceList texture_buffers; ImageResourceList images; SamplerResourceList samplers; @@ -191,9 +210,12 @@ struct Info { u64 pgm_hash{}; u32 shared_memory_size{}; bool has_storage_images{}; + bool has_image_buffers{}; + bool has_texel_buffers{}; bool has_discard{}; bool has_image_gather{}; bool has_image_query{}; + bool uses_lane_id{}; bool uses_group_quad{}; bool uses_shared{}; bool uses_fp16{}; @@ -210,13 +232,37 @@ struct Info { std::memcpy(&data, base + dword_offset, sizeof(T)); return data; } + + size_t NumBindings() const noexcept { + return buffers.size() + texture_buffers.size() + images.size() + samplers.size(); + } + + [[nodiscard]] std::pair GetDrawOffsets() const noexcept { + u32 vertex_offset = 0; + u32 instance_offset = 0; + if (vertex_offset_sgpr != -1) { + vertex_offset = user_data[vertex_offset_sgpr]; + } + if (instance_offset_sgpr != -1) { + instance_offset = user_data[instance_offset_sgpr]; + } + return {vertex_offset, instance_offset}; + } }; -constexpr AmdGpu::Buffer BufferResource::GetVsharp(const Info& info) const noexcept { +constexpr AmdGpu::Buffer BufferResource::GetSharp(const Info& info) const noexcept { return inline_cbuf ? inline_cbuf : info.ReadUd(sgpr_base, dword_offset); } -constexpr AmdGpu::Sampler SamplerResource::GetSsharp(const Info& info) const noexcept { +constexpr AmdGpu::Buffer TextureBufferResource::GetSharp(const Info& info) const noexcept { + return info.ReadUd(sgpr_base, dword_offset); +} + +constexpr AmdGpu::Image ImageResource::GetSharp(const Info& info) const noexcept { + return info.ReadUd(sgpr_base, dword_offset); +} + +constexpr AmdGpu::Sampler SamplerResource::GetSharp(const Info& info) const noexcept { return inline_sampler ? inline_sampler : info.ReadUd(sgpr_base, dword_offset); } diff --git a/src/video_core/amdgpu/default_context.cpp b/src/video_core/amdgpu/default_context.cpp new file mode 100644 index 000000000..01229e7b1 --- /dev/null +++ b/src/video_core/amdgpu/default_context.cpp @@ -0,0 +1,55 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/types.h" +#include "video_core/amdgpu/liverpool.h" + +#include + +namespace AmdGpu { + +// The following values are taken from fpPS4: +// https://github.com/red-prig/fpPS4/blob/436b43064be4c78229500f3d3c054fc76639247d/chip/pm4_pfp.pas#L410 +// +static constexpr std::array reg_array_default{ + 0x00000000u, 0x80000000u, 0x40004000u, 0xdeadbeefu, 0x00000000u, 0x40004000u, 0x00000000u, + 0x40004000u, 0x00000000u, 0x40004000u, 0x00000000u, 0x40004000u, 0xaa99aaaau, 0x00000000u, + 0xdeadbeefu, 0xdeadbeefu, 0x80000000u, 0x40004000u, 0x00000000u, 0x00000000u, 0x80000000u, + 0x40004000u, 0x80000000u, 0x40004000u, 0x80000000u, 0x40004000u, 0x80000000u, 0x40004000u, + 0x80000000u, 0x40004000u, 0x80000000u, 0x40004000u, 0x80000000u, 0x40004000u, 0x80000000u, + 0x40004000u, 0x80000000u, 0x40004000u, 0x80000000u, 0x40004000u, 0x80000000u, 0x40004000u, + 0x80000000u, 0x40004000u, 0x80000000u, 0x40004000u, 0x80000000u, 0x40004000u, 0x80000000u, + 0x40004000u, 0x80000000u, 0x40004000u, 0x00000000u, 0x3f800000u, 0x00000000u, 0x3f800000u, + 0x00000000u, 0x3f800000u, 0x00000000u, 0x3f800000u, 0x00000000u, 0x3f800000u, 0x00000000u, + 0x3f800000u, 0x00000000u, 0x3f800000u, 0x00000000u, 0x3f800000u, 0x00000000u, 0x3f800000u, + 0x00000000u, 0x3f800000u, 0x00000000u, 0x3f800000u, 0x00000000u, 0x3f800000u, 0x00000000u, + 0x3f800000u, 0x00000000u, 0x3f800000u, 0x00000000u, 0x3f800000u, 0x00000000u, 0x3f800000u, + 0x2a00161au, +}; + +void Liverpool::Regs::SetDefaults() { + std::memset(reg_array.data(), 0, reg_array.size() * sizeof(u32)); + + std::memcpy(®_array[ContextRegWordOffset + 0x80], reg_array_default.data(), + reg_array_default.size() * sizeof(u32)); + + // Individual context regs values + reg_array[ContextRegWordOffset + 0x000d] = 0x40004000u; + reg_array[ContextRegWordOffset + 0x01b6] = 0x00000002u; + reg_array[ContextRegWordOffset + 0x0204] = 0x00090000u; + reg_array[ContextRegWordOffset + 0x0205] = 0x00000004u; + reg_array[ContextRegWordOffset + 0x0295] = 0x00000100u; + reg_array[ContextRegWordOffset + 0x0296] = 0x00000080u; + reg_array[ContextRegWordOffset + 0x0297] = 0x00000002u; + reg_array[ContextRegWordOffset + 0x02aa] = 0x00001000u; + reg_array[ContextRegWordOffset + 0x02f7] = 0x00001000u; + reg_array[ContextRegWordOffset + 0x02f9] = 0x00000005u; + reg_array[ContextRegWordOffset + 0x02fa] = 0x3f800000u; + reg_array[ContextRegWordOffset + 0x02fb] = 0x3f800000u; + reg_array[ContextRegWordOffset + 0x02fc] = 0x3f800000u; + reg_array[ContextRegWordOffset + 0x02fd] = 0x3f800000u; + reg_array[ContextRegWordOffset + 0x0316] = 0x0000000eu; + reg_array[ContextRegWordOffset + 0x0317] = 0x00000010u; +} + +} // namespace AmdGpu diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp index dce2d4b42..2a595516d 100644 --- a/src/video_core/amdgpu/liverpool.cpp +++ b/src/video_core/amdgpu/liverpool.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "common/assert.h" +#include "common/config.h" #include "common/debug.h" #include "common/polyfill_thread.h" #include "common/thread.h" @@ -175,290 +176,358 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(dcb.data()); const u32 type = header->type; - if (type != 3) { - // No other types of packets were spotted so far - UNREACHABLE_MSG("Invalid PM4 type {}", type); - } - const u32 count = header->type3.NumWords(); - const PM4ItOpcode opcode = header->type3.opcode; - switch (opcode) { - case PM4ItOpcode::Nop: { - const auto* nop = reinterpret_cast(header); - if (nop->header.count.Value() == 0) { - break; - } + switch (type) { + case 0: + case 1: + UNREACHABLE_MSG("Unsupported PM4 type {}", type); + break; + case 2: + // Type-2 packet are used for padding purposes + dcb = dcb.subspan(1); + continue; + case 3: + const u32 count = header->type3.NumWords(); + const PM4ItOpcode opcode = header->type3.opcode; + switch (opcode) { + case PM4ItOpcode::Nop: { + const auto* nop = reinterpret_cast(header); + if (nop->header.count.Value() == 0) { + break; + } - switch (nop->data_block[0]) { - case PM4CmdNop::PayloadType::PatchedFlip: { - // There is no evidence that GPU CP drives flip events by parsing - // special NOP packets. For convenience lets assume that it does. - Platform::IrqC::Instance()->Signal(Platform::InterruptId::GfxFlip); + switch (nop->data_block[0]) { + case PM4CmdNop::PayloadType::PatchedFlip: { + // There is no evidence that GPU CP drives flip events by parsing + // special NOP packets. For convenience lets assume that it does. + Platform::IrqC::Instance()->Signal(Platform::InterruptId::GfxFlip); + break; + } + case PM4CmdNop::PayloadType::DebugMarkerPush: { + const auto marker_sz = nop->header.count.Value() * 2; + const std::string_view label{reinterpret_cast(&nop->data_block[1]), + marker_sz}; + rasterizer->ScopeMarkerBegin(label); + break; + } + case PM4CmdNop::PayloadType::DebugMarkerPop: { + rasterizer->ScopeMarkerEnd(); + break; + } + default: + break; + } break; } - case PM4CmdNop::PayloadType::DebugMarkerPush: { - const auto marker_sz = nop->header.count.Value() * 2; - const std::string_view label{reinterpret_cast(&nop->data_block[1]), - marker_sz}; - rasterizer->ScopeMarkerBegin(label); + case PM4ItOpcode::ContextControl: { break; } - case PM4CmdNop::PayloadType::DebugMarkerPop: { - rasterizer->ScopeMarkerEnd(); + case PM4ItOpcode::ClearState: { + regs.SetDefaults(); + break; + } + case PM4ItOpcode::SetConfigReg: { + const auto* set_data = reinterpret_cast(header); + const auto reg_addr = ConfigRegWordOffset + set_data->reg_offset; + const auto* payload = reinterpret_cast(header + 2); + std::memcpy(®s.reg_array[reg_addr], payload, (count - 1) * sizeof(u32)); + break; + } + case PM4ItOpcode::SetContextReg: { + const auto* set_data = reinterpret_cast(header); + const auto reg_addr = ContextRegWordOffset + set_data->reg_offset; + const auto* payload = reinterpret_cast(header + 2); + + std::memcpy(®s.reg_array[reg_addr], payload, (count - 1) * sizeof(u32)); + + // In the case of HW, render target memory has alignment as color block operates on + // tiles. There is no information of actual resource extents stored in CB context + // regs, so any deduction of it from slices/pitch will lead to a larger surface + // created. The same applies to the depth targets. Fortunately, the guest always + // sends a trailing NOP packet right after the context regs setup, so we can use the + // heuristic below and extract the hint to determine actual resource dims. + + switch (reg_addr) { + case ContextRegs::CbColor0Base: + case ContextRegs::CbColor1Base: + case ContextRegs::CbColor2Base: + case ContextRegs::CbColor3Base: + case ContextRegs::CbColor4Base: + case ContextRegs::CbColor5Base: + case ContextRegs::CbColor6Base: + case ContextRegs::CbColor7Base: { + const auto col_buf_id = (reg_addr - ContextRegs::CbColor0Base) / + (ContextRegs::CbColor1Base - ContextRegs::CbColor0Base); + ASSERT(col_buf_id < NumColorBuffers); + + const auto nop_offset = header->type3.count; + if (nop_offset == 0x0e || nop_offset == 0x0d || nop_offset == 0x0b) { + ASSERT_MSG(payload[nop_offset] == 0xc0001000, + "NOP hint is missing in CB setup sequence"); + last_cb_extent[col_buf_id].raw = payload[nop_offset + 1]; + } else { + last_cb_extent[col_buf_id].raw = 0; + } + break; + } + case ContextRegs::CbColor0Cmask: + case ContextRegs::CbColor1Cmask: + case ContextRegs::CbColor2Cmask: + case ContextRegs::CbColor3Cmask: + case ContextRegs::CbColor4Cmask: + case ContextRegs::CbColor5Cmask: + case ContextRegs::CbColor6Cmask: + case ContextRegs::CbColor7Cmask: { + const auto col_buf_id = + (reg_addr - ContextRegs::CbColor0Cmask) / + (ContextRegs::CbColor1Cmask - ContextRegs::CbColor0Cmask); + ASSERT(col_buf_id < NumColorBuffers); + + const auto nop_offset = header->type3.count; + if (nop_offset == 0x04) { + ASSERT_MSG(payload[nop_offset] == 0xc0001000, + "NOP hint is missing in CB setup sequence"); + last_cb_extent[col_buf_id].raw = payload[nop_offset + 1]; + } + break; + } + case ContextRegs::DbZInfo: { + if (header->type3.count == 8) { + ASSERT_MSG(payload[20] == 0xc0001000, + "NOP hint is missing in DB setup sequence"); + last_db_extent.raw = payload[21]; + } else { + last_db_extent.raw = 0; + } + break; + } + default: + break; + } + break; + } + case PM4ItOpcode::SetShReg: { + const auto* set_data = reinterpret_cast(header); + std::memcpy(®s.reg_array[ShRegWordOffset + set_data->reg_offset], header + 2, + (count - 1) * sizeof(u32)); + break; + } + case PM4ItOpcode::SetUconfigReg: { + const auto* set_data = reinterpret_cast(header); + std::memcpy(®s.reg_array[UconfigRegWordOffset + set_data->reg_offset], + header + 2, (count - 1) * sizeof(u32)); + break; + } + case PM4ItOpcode::IndexType: { + const auto* index_type = reinterpret_cast(header); + regs.index_buffer_type.raw = index_type->raw; + break; + } + case PM4ItOpcode::DrawIndex2: { + const auto* draw_index = reinterpret_cast(header); + regs.max_index_size = draw_index->max_size; + regs.index_base_address.base_addr_lo = draw_index->index_base_lo; + regs.index_base_address.base_addr_hi.Assign(draw_index->index_base_hi); + regs.num_indices = draw_index->index_count; + regs.draw_initiator = draw_index->draw_initiator; + if (rasterizer) { + const auto cmd_address = reinterpret_cast(header); + rasterizer->ScopeMarkerBegin(fmt::format("dcb:{}:DrawIndex2", cmd_address)); + rasterizer->Breadcrumb(u64(cmd_address)); + rasterizer->Draw(true); + rasterizer->ScopeMarkerEnd(); + } + break; + } + case PM4ItOpcode::DrawIndexOffset2: { + const auto* draw_index_off = + reinterpret_cast(header); + regs.max_index_size = draw_index_off->max_size; + regs.num_indices = draw_index_off->index_count; + regs.draw_initiator = draw_index_off->draw_initiator; + if (rasterizer) { + const auto cmd_address = reinterpret_cast(header); + rasterizer->ScopeMarkerBegin( + fmt::format("dcb:{}:DrawIndexOffset2", cmd_address)); + rasterizer->Breadcrumb(u64(cmd_address)); + rasterizer->Draw(true, draw_index_off->index_offset); + rasterizer->ScopeMarkerEnd(); + } + break; + } + case PM4ItOpcode::DrawIndexAuto: { + const auto* draw_index = reinterpret_cast(header); + regs.num_indices = draw_index->index_count; + regs.draw_initiator = draw_index->draw_initiator; + if (rasterizer) { + const auto cmd_address = reinterpret_cast(header); + rasterizer->ScopeMarkerBegin(fmt::format("dcb:{}:DrawIndexAuto", cmd_address)); + rasterizer->Breadcrumb(u64(cmd_address)); + rasterizer->Draw(false); + rasterizer->ScopeMarkerEnd(); + } + break; + } + case PM4ItOpcode::DrawIndirect: { + const auto* draw_indirect = reinterpret_cast(header); + const auto offset = draw_indirect->data_offset; + const auto ib_address = mapped_queues[GfxQueueId].indirect_args_addr; + const auto size = sizeof(PM4CmdDrawIndirect::DrawInstancedArgs); + if (rasterizer) { + const auto cmd_address = reinterpret_cast(header); + rasterizer->ScopeMarkerBegin(fmt::format("dcb:{}:DrawIndirect", cmd_address)); + rasterizer->Breadcrumb(u64(cmd_address)); + rasterizer->DrawIndirect(false, ib_address, offset, size); + rasterizer->ScopeMarkerEnd(); + } + break; + } + case PM4ItOpcode::DrawIndexIndirect: { + const auto* draw_index_indirect = + reinterpret_cast(header); + const auto offset = draw_index_indirect->data_offset; + const auto ib_address = mapped_queues[GfxQueueId].indirect_args_addr; + const auto size = sizeof(PM4CmdDrawIndexIndirect::DrawIndexInstancedArgs); + if (rasterizer) { + const auto cmd_address = reinterpret_cast(header); + rasterizer->ScopeMarkerBegin( + fmt::format("dcb:{}:DrawIndexIndirect", cmd_address)); + rasterizer->Breadcrumb(u64(cmd_address)); + rasterizer->DrawIndirect(true, ib_address, offset, size); + rasterizer->ScopeMarkerEnd(); + } + break; + } + case PM4ItOpcode::DispatchDirect: { + const auto* dispatch_direct = reinterpret_cast(header); + regs.cs_program.dim_x = dispatch_direct->dim_x; + regs.cs_program.dim_y = dispatch_direct->dim_y; + regs.cs_program.dim_z = dispatch_direct->dim_z; + regs.cs_program.dispatch_initiator = dispatch_direct->dispatch_initiator; + if (rasterizer && (regs.cs_program.dispatch_initiator & 1)) { + const auto cmd_address = reinterpret_cast(header); + rasterizer->ScopeMarkerBegin(fmt::format("dcb:{}:Dispatch", cmd_address)); + rasterizer->Breadcrumb(u64(cmd_address)); + rasterizer->DispatchDirect(); + rasterizer->ScopeMarkerEnd(); + } + break; + } + case PM4ItOpcode::DispatchIndirect: { + const auto* dispatch_indirect = + reinterpret_cast(header); + const auto offset = dispatch_indirect->data_offset; + const auto ib_address = mapped_queues[GfxQueueId].indirect_args_addr; + const auto size = sizeof(PM4CmdDispatchIndirect::GroupDimensions); + if (rasterizer && (regs.cs_program.dispatch_initiator & 1)) { + const auto cmd_address = reinterpret_cast(header); + rasterizer->ScopeMarkerBegin( + fmt::format("dcb:{}:DispatchIndirect", cmd_address)); + rasterizer->Breadcrumb(u64(cmd_address)); + rasterizer->DispatchIndirect(ib_address, offset, size); + rasterizer->ScopeMarkerEnd(); + } + break; + } + case PM4ItOpcode::NumInstances: { + const auto* num_instances = reinterpret_cast(header); + regs.num_instances.num_instances = num_instances->num_instances; + break; + } + case PM4ItOpcode::IndexBase: { + const auto* index_base = reinterpret_cast(header); + regs.index_base_address.base_addr_lo = index_base->addr_lo; + regs.index_base_address.base_addr_hi.Assign(index_base->addr_hi); + break; + } + case PM4ItOpcode::IndexBufferSize: { + const auto* index_size = reinterpret_cast(header); + regs.num_indices = index_size->num_indices; + break; + } + case PM4ItOpcode::SetBase: { + const auto* set_base = reinterpret_cast(header); + ASSERT(set_base->base_index == PM4CmdSetBase::BaseIndex::DrawIndexIndirPatchTable); + mapped_queues[GfxQueueId].indirect_args_addr = set_base->Address(); + break; + } + case PM4ItOpcode::EventWrite: { + // const auto* event = reinterpret_cast(header); + break; + } + case PM4ItOpcode::EventWriteEos: { + const auto* event_eos = reinterpret_cast(header); + event_eos->SignalFence(); + break; + } + case PM4ItOpcode::EventWriteEop: { + const auto* event_eop = reinterpret_cast(header); + event_eop->SignalFence(); + break; + } + case PM4ItOpcode::DmaData: { + const auto* dma_data = reinterpret_cast(header); + break; + } + case PM4ItOpcode::WriteData: { + const auto* write_data = reinterpret_cast(header); + ASSERT(write_data->dst_sel.Value() == 2 || write_data->dst_sel.Value() == 5); + const u32 data_size = (header->type3.count.Value() - 2) * 4; + u64* address = write_data->Address(); + if (!write_data->wr_one_addr.Value()) { + std::memcpy(address, write_data->data, data_size); + } else { + UNREACHABLE(); + } + break; + } + case PM4ItOpcode::AcquireMem: { + // const auto* acquire_mem = reinterpret_cast(header); + break; + } + case PM4ItOpcode::WaitRegMem: { + const auto* wait_reg_mem = reinterpret_cast(header); + // ASSERT(wait_reg_mem->engine.Value() == PM4CmdWaitRegMem::Engine::Me); + // Optimization: VO label waits are special because the emulator + // will write to the label when presentation is finished. So if + // there are no other submits to yield to we can sleep the thread + // instead and allow other tasks to run. + const u64* wait_addr = wait_reg_mem->Address(); + if (vo_port->IsVoLabel(wait_addr) && num_submits == 1) { + vo_port->WaitVoLabel([&] { return wait_reg_mem->Test(); }); + } + while (!wait_reg_mem->Test()) { + mapped_queues[GfxQueueId].cs_state = regs.cs_program; + TracyFiberLeave; + co_yield {}; + TracyFiberEnter(dcb_task_name); + regs.cs_program = mapped_queues[GfxQueueId].cs_state; + } + break; + } + case PM4ItOpcode::IncrementDeCounter: { + ++cblock.de_count; + break; + } + case PM4ItOpcode::WaitOnCeCounter: { + while (cblock.ce_count <= cblock.de_count) { + TracyFiberLeave; + ce_task.handle.resume(); + TracyFiberEnter(dcb_task_name); + } + break; + } + case PM4ItOpcode::PfpSyncMe: { + rasterizer->CpSync(); break; } default: - break; + UNREACHABLE_MSG("Unknown PM4 type 3 opcode {:#x} with count {}", + static_cast(opcode), count); } + dcb = dcb.subspan(header->type3.NumWords() + 1); break; } - case PM4ItOpcode::ContextControl: { - break; - } - case PM4ItOpcode::ClearState: { - break; - } - case PM4ItOpcode::SetConfigReg: { - const auto* set_data = reinterpret_cast(header); - const auto reg_addr = ConfigRegWordOffset + set_data->reg_offset; - const auto* payload = reinterpret_cast(header + 2); - std::memcpy(®s.reg_array[reg_addr], payload, (count - 1) * sizeof(u32)); - break; - } - case PM4ItOpcode::SetContextReg: { - const auto* set_data = reinterpret_cast(header); - const auto reg_addr = ContextRegWordOffset + set_data->reg_offset; - const auto* payload = reinterpret_cast(header + 2); - - std::memcpy(®s.reg_array[reg_addr], payload, (count - 1) * sizeof(u32)); - - // In the case of HW, render target memory has alignment as color block operates on - // tiles. There is no information of actual resource extents stored in CB context - // regs, so any deduction of it from slices/pitch will lead to a larger surface created. - // The same applies to the depth targets. Fortunately, the guest always sends - // a trailing NOP packet right after the context regs setup, so we can use the heuristic - // below and extract the hint to determine actual resource dims. - - switch (reg_addr) { - case ContextRegs::CbColor0Base: - case ContextRegs::CbColor1Base: - case ContextRegs::CbColor2Base: - case ContextRegs::CbColor3Base: - case ContextRegs::CbColor4Base: - case ContextRegs::CbColor5Base: - case ContextRegs::CbColor6Base: - case ContextRegs::CbColor7Base: { - const auto col_buf_id = (reg_addr - ContextRegs::CbColor0Base) / - (ContextRegs::CbColor1Base - ContextRegs::CbColor0Base); - ASSERT(col_buf_id < NumColorBuffers); - - const auto nop_offset = header->type3.count; - if (nop_offset == 0x0e || nop_offset == 0x0d || nop_offset == 0x0b) { - ASSERT_MSG(payload[nop_offset] == 0xc0001000, - "NOP hint is missing in CB setup sequence"); - last_cb_extent[col_buf_id].raw = payload[nop_offset + 1]; - } else { - last_cb_extent[col_buf_id].raw = 0; - } - break; - } - case ContextRegs::CbColor0Cmask: - case ContextRegs::CbColor1Cmask: - case ContextRegs::CbColor2Cmask: - case ContextRegs::CbColor3Cmask: - case ContextRegs::CbColor4Cmask: - case ContextRegs::CbColor5Cmask: - case ContextRegs::CbColor6Cmask: - case ContextRegs::CbColor7Cmask: { - const auto col_buf_id = (reg_addr - ContextRegs::CbColor0Cmask) / - (ContextRegs::CbColor1Cmask - ContextRegs::CbColor0Cmask); - ASSERT(col_buf_id < NumColorBuffers); - - const auto nop_offset = header->type3.count; - if (nop_offset == 0x04) { - ASSERT_MSG(payload[nop_offset] == 0xc0001000, - "NOP hint is missing in CB setup sequence"); - last_cb_extent[col_buf_id].raw = payload[nop_offset + 1]; - } - break; - } - case ContextRegs::DbZInfo: { - if (header->type3.count == 8) { - ASSERT_MSG(payload[20] == 0xc0001000, - "NOP hint is missing in DB setup sequence"); - last_db_extent.raw = payload[21]; - } else { - last_db_extent.raw = 0; - } - break; - } - default: - break; - } - break; - } - case PM4ItOpcode::SetShReg: { - const auto* set_data = reinterpret_cast(header); - std::memcpy(®s.reg_array[ShRegWordOffset + set_data->reg_offset], header + 2, - (count - 1) * sizeof(u32)); - break; - } - case PM4ItOpcode::SetUconfigReg: { - const auto* set_data = reinterpret_cast(header); - std::memcpy(®s.reg_array[UconfigRegWordOffset + set_data->reg_offset], header + 2, - (count - 1) * sizeof(u32)); - break; - } - case PM4ItOpcode::IndexType: { - const auto* index_type = reinterpret_cast(header); - regs.index_buffer_type.raw = index_type->raw; - break; - } - case PM4ItOpcode::DrawIndex2: { - const auto* draw_index = reinterpret_cast(header); - regs.max_index_size = draw_index->max_size; - regs.index_base_address.base_addr_lo = draw_index->index_base_lo; - regs.index_base_address.base_addr_hi.Assign(draw_index->index_base_hi); - regs.num_indices = draw_index->index_count; - regs.draw_initiator = draw_index->draw_initiator; - if (rasterizer) { - const auto cmd_address = reinterpret_cast(header); - rasterizer->ScopeMarkerBegin(fmt::format("dcb:{}:DrawIndex2", cmd_address)); - rasterizer->Breadcrumb(u64(cmd_address)); - rasterizer->Draw(true); - rasterizer->ScopeMarkerEnd(); - } - break; - } - case PM4ItOpcode::DrawIndexOffset2: { - const auto* draw_index_off = reinterpret_cast(header); - regs.max_index_size = draw_index_off->max_size; - regs.num_indices = draw_index_off->index_count; - regs.draw_initiator = draw_index_off->draw_initiator; - if (rasterizer) { - const auto cmd_address = reinterpret_cast(header); - rasterizer->ScopeMarkerBegin(fmt::format("dcb:{}:DrawIndexOffset2", cmd_address)); - rasterizer->Breadcrumb(u64(cmd_address)); - rasterizer->Draw(true, draw_index_off->index_offset); - rasterizer->ScopeMarkerEnd(); - } - break; - } - case PM4ItOpcode::DrawIndexAuto: { - const auto* draw_index = reinterpret_cast(header); - regs.num_indices = draw_index->index_count; - regs.draw_initiator = draw_index->draw_initiator; - if (rasterizer) { - const auto cmd_address = reinterpret_cast(header); - rasterizer->ScopeMarkerBegin(fmt::format("dcb:{}:DrawIndexAuto", cmd_address)); - rasterizer->Breadcrumb(u64(cmd_address)); - rasterizer->Draw(false); - rasterizer->ScopeMarkerEnd(); - } - break; - } - case PM4ItOpcode::DispatchDirect: { - const auto* dispatch_direct = reinterpret_cast(header); - regs.cs_program.dim_x = dispatch_direct->dim_x; - regs.cs_program.dim_y = dispatch_direct->dim_y; - regs.cs_program.dim_z = dispatch_direct->dim_z; - regs.cs_program.dispatch_initiator = dispatch_direct->dispatch_initiator; - if (rasterizer && (regs.cs_program.dispatch_initiator & 1)) { - const auto cmd_address = reinterpret_cast(header); - rasterizer->ScopeMarkerBegin(fmt::format("dcb:{}:Dispatch", cmd_address)); - rasterizer->Breadcrumb(u64(cmd_address)); - rasterizer->DispatchDirect(); - rasterizer->ScopeMarkerEnd(); - } - break; - } - case PM4ItOpcode::NumInstances: { - const auto* num_instances = reinterpret_cast(header); - regs.num_instances.num_instances = num_instances->num_instances; - break; - } - case PM4ItOpcode::IndexBase: { - const auto* index_base = reinterpret_cast(header); - regs.index_base_address.base_addr_lo = index_base->addr_lo; - regs.index_base_address.base_addr_hi.Assign(index_base->addr_hi); - break; - } - case PM4ItOpcode::IndexBufferSize: { - const auto* index_size = reinterpret_cast(header); - regs.num_indices = index_size->num_indices; - break; - } - case PM4ItOpcode::EventWrite: { - // const auto* event = reinterpret_cast(header); - break; - } - case PM4ItOpcode::EventWriteEos: { - const auto* event_eos = reinterpret_cast(header); - event_eos->SignalFence(); - break; - } - case PM4ItOpcode::EventWriteEop: { - const auto* event_eop = reinterpret_cast(header); - event_eop->SignalFence(); - break; - } - case PM4ItOpcode::DmaData: { - const auto* dma_data = reinterpret_cast(header); - break; - } - case PM4ItOpcode::WriteData: { - const auto* write_data = reinterpret_cast(header); - ASSERT(write_data->dst_sel.Value() == 2 || write_data->dst_sel.Value() == 5); - const u32 data_size = (header->type3.count.Value() - 2) * 4; - u64* address = write_data->Address(); - if (!write_data->wr_one_addr.Value()) { - std::memcpy(address, write_data->data, data_size); - } else { - UNREACHABLE(); - } - break; - } - case PM4ItOpcode::AcquireMem: { - // const auto* acquire_mem = reinterpret_cast(header); - break; - } - case PM4ItOpcode::WaitRegMem: { - const auto* wait_reg_mem = reinterpret_cast(header); - // ASSERT(wait_reg_mem->engine.Value() == PM4CmdWaitRegMem::Engine::Me); - // Optimization: VO label waits are special because the emulator - // will write to the label when presentation is finished. So if - // there are no other submits to yield to we can sleep the thread - // instead and allow other tasks to run. - const u64* wait_addr = wait_reg_mem->Address(); - if (vo_port->IsVoLabel(wait_addr) && num_submits == 1) { - vo_port->WaitVoLabel([&] { return wait_reg_mem->Test(); }); - } - while (!wait_reg_mem->Test()) { - mapped_queues[GfxQueueId].cs_state = regs.cs_program; - TracyFiberLeave; - co_yield {}; - TracyFiberEnter(dcb_task_name); - regs.cs_program = mapped_queues[GfxQueueId].cs_state; - } - break; - } - case PM4ItOpcode::IncrementDeCounter: { - ++cblock.de_count; - break; - } - case PM4ItOpcode::WaitOnCeCounter: { - while (cblock.ce_count <= cblock.de_count) { - TracyFiberLeave; - ce_task.handle.resume(); - TracyFiberEnter(dcb_task_name); - } - break; - } - default: - UNREACHABLE_MSG("Unknown PM4 type 3 opcode {:#x} with count {}", - static_cast(opcode), count); - } - dcb = dcb.subspan(header->type3.NumWords() + 1); } if (ce_task.handle) { @@ -564,9 +633,43 @@ Liverpool::Task Liverpool::ProcessCompute(std::span acb, int vqid) { TracyFiberLeave; } +std::pair, std::span> Liverpool::CopyCmdBuffers( + std::span dcb, std::span ccb) { + auto& queue = mapped_queues[GfxQueueId]; + + queue.dcb_buffer.resize( + std::max(queue.dcb_buffer.size(), queue.dcb_buffer_offset + dcb.size())); + queue.ccb_buffer.resize( + std::max(queue.ccb_buffer.size(), queue.ccb_buffer_offset + ccb.size())); + + u32 prev_dcb_buffer_offset = queue.dcb_buffer_offset; + u32 prev_ccb_buffer_offset = queue.ccb_buffer_offset; + if (!dcb.empty()) { + std::memcpy(queue.dcb_buffer.data() + queue.dcb_buffer_offset, dcb.data(), + dcb.size_bytes()); + queue.dcb_buffer_offset += dcb.size(); + dcb = std::span{queue.dcb_buffer.begin() + prev_dcb_buffer_offset, + queue.dcb_buffer.begin() + queue.dcb_buffer_offset}; + } + + if (!ccb.empty()) { + std::memcpy(queue.ccb_buffer.data() + queue.ccb_buffer_offset, ccb.data(), + ccb.size_bytes()); + queue.ccb_buffer_offset += ccb.size(); + ccb = std::span{queue.ccb_buffer.begin() + prev_ccb_buffer_offset, + queue.ccb_buffer.begin() + queue.ccb_buffer_offset}; + } + + return std::make_pair(dcb, ccb); +} + void Liverpool::SubmitGfx(std::span dcb, std::span ccb) { auto& queue = mapped_queues[GfxQueueId]; + if (Config::copyGPUCmdBuffers()) { + std::tie(dcb, ccb) = CopyCmdBuffers(dcb, ccb); + } + auto task = ProcessGraphics(dcb, ccb); { std::scoped_lock lock{queue.m_access}; diff --git a/src/video_core/amdgpu/liverpool.h b/src/video_core/amdgpu/liverpool.h index 896927df6..7f262e1f4 100644 --- a/src/video_core/amdgpu/liverpool.h +++ b/src/video_core/amdgpu/liverpool.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include "common/assert.h" @@ -166,7 +167,7 @@ struct Liverpool { static constexpr auto* GetBinaryInfo(const Shader& sh) { const auto* code = sh.template Address(); const auto* bininfo = std::bit_cast(code + (code[1] + 1) * 2); - ASSERT_MSG(bininfo->Valid(), "Invalid shader binary header"); + // ASSERT_MSG(bininfo->Valid(), "Invalid shader binary header"); return bininfo; } @@ -556,7 +557,7 @@ struct Liverpool { union { BitField<0, 15, s32> top_left_x; BitField<15, 15, s32> top_left_y; - BitField<30, 1, s32> window_offset_disble; + BitField<30, 1, s32> window_offset_disable; }; union { BitField<0, 15, s32> bottom_right_x; @@ -848,6 +849,7 @@ struct Liverpool { u32 raw; BitField<0, 1, u32> depth_clear_enable; BitField<1, 1, u32> stencil_clear_enable; + BitField<5, 1, u32> stencil_compress_disable; BitField<6, 1, u32> depth_compress_disable; }; @@ -1016,6 +1018,8 @@ struct Liverpool { } return nullptr; } + + void SetDefaults(); }; Regs regs{}; @@ -1044,6 +1048,8 @@ public: void SubmitDone() noexcept { std::scoped_lock lk{submit_mutex}; + mapped_queues[GfxQueueId].ccb_buffer_offset = 0; + mapped_queues[GfxQueueId].dcb_buffer_offset = 0; submit_done = true; submit_cv.notify_one(); } @@ -1105,6 +1111,8 @@ private: Handle handle; }; + std::pair, std::span> CopyCmdBuffers(std::span dcb, + std::span ccb); Task ProcessGraphics(std::span dcb, std::span ccb); Task ProcessCeUpdate(std::span ccb); Task ProcessCompute(std::span acb, int vqid); @@ -1113,8 +1121,13 @@ private: struct GpuQueue { std::mutex m_access{}; + std::atomic dcb_buffer_offset; + std::atomic ccb_buffer_offset; + std::vector dcb_buffer; + std::vector ccb_buffer; std::queue submits{}; ComputeProgram cs_state{}; + VAddr indirect_args_addr{}; }; std::array mapped_queues{}; diff --git a/src/video_core/amdgpu/pixel_format.h b/src/video_core/amdgpu/pixel_format.h index 1004ed7d2..53d30a7fd 100644 --- a/src/video_core/amdgpu/pixel_format.h +++ b/src/video_core/amdgpu/pixel_format.h @@ -61,6 +61,10 @@ enum class NumberFormat : u32 { Ubscaled = 13, }; +[[nodiscard]] constexpr bool IsInteger(NumberFormat nfmt) { + return nfmt == AmdGpu::NumberFormat::Sint || nfmt == AmdGpu::NumberFormat::Uint; +} + [[nodiscard]] std::string_view NameOf(DataFormat fmt); [[nodiscard]] std::string_view NameOf(NumberFormat fmt); diff --git a/src/video_core/amdgpu/pm4_cmds.h b/src/video_core/amdgpu/pm4_cmds.h index 5ab233fdc..58ade221b 100644 --- a/src/video_core/amdgpu/pm4_cmds.h +++ b/src/video_core/amdgpu/pm4_cmds.h @@ -253,20 +253,6 @@ struct PM4CmdDrawIndexAuto { u32 draw_initiator; }; -struct PM4CmdDrawIndirect { - PM4Type3Header header; ///< header - u32 data_offset; ///< DWORD aligned offset - union { - u32 dw2; - BitField<0, 16, u32> base_vtx_loc; ///< base vertex location - }; - union { - u32 dw3; - BitField<0, 16, u32> start_inst_loc; ///< start instance location - }; - u32 draw_initiator; ///< Draw Initiator Register -}; - enum class DataSelect : u32 { None = 0, Data32Low = 1, @@ -704,4 +690,87 @@ struct PM4CmdReleaseMem { } }; +struct PM4CmdSetBase { + enum class BaseIndex : u32 { + DisplayListPatchTable = 0b0000, + DrawIndexIndirPatchTable = 0b0001, + GdsPartition = 0b0010, + CePartition = 0b0011, + }; + + PM4Type3Header header; + union { + BitField<0, 4, BaseIndex> base_index; + u32 dw1; + }; + u32 address0; + u32 address1; + + template + T Address() const { + ASSERT(base_index == BaseIndex::DisplayListPatchTable || + base_index == BaseIndex::DrawIndexIndirPatchTable); + return reinterpret_cast(address0 | (u64(address1 & 0xffff) << 32u)); + } +}; + +struct PM4CmdDispatchIndirect { + struct GroupDimensions { + u32 dim_x; + u32 dim_y; + u32 dim_z; + }; + + PM4Type3Header header; + u32 data_offset; ///< Byte aligned offset where the required data structure starts + u32 dispatch_initiator; ///< Dispatch Initiator Register +}; + +struct PM4CmdDrawIndirect { + struct DrawInstancedArgs { + u32 vertex_count_per_instance; + u32 instance_count; + u32 start_vertex_location; + u32 start_instance_location; + }; + + PM4Type3Header header; ///< header + u32 data_offset; ///< Byte aligned offset where the required data structure starts + union { + u32 dw2; + BitField<0, 16, u32> base_vtx_loc; ///< Offset where the CP will write the + ///< BaseVertexLocation it fetched from memory + }; + union { + u32 dw3; + BitField<0, 16, u32> start_inst_loc; ///< Offset where the CP will write the + ///< StartInstanceLocation it fetched from memory + }; + u32 draw_initiator; ///< Draw Initiator Register +}; + +struct PM4CmdDrawIndexIndirect { + struct DrawIndexInstancedArgs { + u32 index_count_per_instance; + u32 instance_count; + u32 start_index_location; + u32 base_vertex_location; + u32 start_instance_location; + }; + + PM4Type3Header header; ///< header + u32 data_offset; ///< Byte aligned offset where the required data structure starts + union { + u32 dw2; + BitField<0, 16, u32> base_vtx_loc; ///< Offset where the CP will write the + ///< BaseVertexLocation it fetched from memory + }; + union { // NOTE: this one is undocumented in AMD spec, but Gnm driver writes this field + u32 dw3; + BitField<0, 16, u32> start_inst_loc; ///< Offset where the CP will write the + ///< StartInstanceLocation it fetched from memory + }; + u32 draw_initiator; ///< Draw Initiator Register +}; + } // namespace AmdGpu diff --git a/src/video_core/amdgpu/pm4_opcodes.h b/src/video_core/amdgpu/pm4_opcodes.h index fba0cbb9f..83c1deaa4 100644 --- a/src/video_core/amdgpu/pm4_opcodes.h +++ b/src/video_core/amdgpu/pm4_opcodes.h @@ -48,7 +48,7 @@ enum class PM4ItOpcode : u32 { EventWriteEop = 0x47, EventWriteEos = 0x48, ReleaseMem = 0x49, - PremableCntl = 0x4A, + PreambleCntl = 0x4A, DmaData = 0x50, ContextRegRmw = 0x51, AcquireMem = 0x58, diff --git a/src/video_core/amdgpu/resource.h b/src/video_core/amdgpu/resource.h index ef5bf1b66..b85a3788b 100644 --- a/src/video_core/amdgpu/resource.h +++ b/src/video_core/amdgpu/resource.h @@ -3,6 +3,7 @@ #pragma once +#include "common/alignment.h" #include "common/assert.h" #include "common/bit_field.h" #include "common/types.h" @@ -68,6 +69,10 @@ struct Buffer { return stride == 0 ? 1U : stride; } + u32 NumDwords() const noexcept { + return Common::AlignUp(GetSize(), sizeof(u32)) >> 2; + } + u32 GetSize() const noexcept { return GetStride() * num_records; } @@ -179,6 +184,10 @@ struct Image { return base_address << 8; } + operator bool() const noexcept { + return base_address != 0; + } + u32 DstSelect() const { return dst_sel_x | (dst_sel_y << 3) | (dst_sel_z << 6) | (dst_sel_w << 9); } diff --git a/src/video_core/buffer_cache/buffer.cpp b/src/video_core/buffer_cache/buffer.cpp index d112864d5..372b6f745 100644 --- a/src/video_core/buffer_cache/buffer.cpp +++ b/src/video_core/buffer_cache/buffer.cpp @@ -13,12 +13,6 @@ namespace VideoCore { -constexpr vk::BufferUsageFlags AllFlags = - vk::BufferUsageFlagBits::eTransferSrc | vk::BufferUsageFlagBits::eTransferDst | - vk::BufferUsageFlagBits::eUniformTexelBuffer | vk::BufferUsageFlagBits::eStorageTexelBuffer | - vk::BufferUsageFlagBits::eUniformBuffer | vk::BufferUsageFlagBits::eStorageBuffer | - vk::BufferUsageFlagBits::eIndexBuffer | vk::BufferUsageFlagBits::eVertexBuffer; - std::string_view BufferTypeName(MemoryUsage type) { switch (type) { case MemoryUsage::Upload: @@ -95,13 +89,13 @@ void UniqueBuffer::Create(const vk::BufferCreateInfo& buffer_ci, MemoryUsage usa } Buffer::Buffer(const Vulkan::Instance& instance_, MemoryUsage usage_, VAddr cpu_addr_, - u64 size_bytes_) + vk::BufferUsageFlags flags, u64 size_bytes_) : cpu_addr{cpu_addr_}, size_bytes{size_bytes_}, instance{&instance_}, usage{usage_}, buffer{instance->GetDevice(), instance->GetAllocator()} { // Create buffer object. const vk::BufferCreateInfo buffer_ci = { .size = size_bytes, - .usage = AllFlags, + .usage = flags, }; VmaAllocationInfo alloc_info{}; buffer.Create(buffer_ci, usage, &alloc_info); @@ -118,27 +112,35 @@ Buffer::Buffer(const Vulkan::Instance& instance_, MemoryUsage usage_, VAddr cpu_ is_coherent = property_flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; } -vk::BufferView Buffer::View(u32 offset, u32 size, AmdGpu::DataFormat dfmt, +vk::BufferView Buffer::View(u32 offset, u32 size, bool is_written, AmdGpu::DataFormat dfmt, AmdGpu::NumberFormat nfmt) { - const auto it{std::ranges::find_if(views, [offset, size, dfmt, nfmt](const BufferView& view) { - return offset == view.offset && size == view.size && dfmt == view.dfmt && nfmt == view.nfmt; + const auto it{std::ranges::find_if(views, [=](const BufferView& view) { + return offset == view.offset && size == view.size && is_written == view.is_written && + dfmt == view.dfmt && nfmt == view.nfmt; })}; if (it != views.end()) { - return it->handle; + return *it->handle; } + const vk::BufferUsageFlags2CreateInfoKHR usage_flags = { + .usage = is_written ? vk::BufferUsageFlagBits2KHR::eStorageTexelBuffer + : vk::BufferUsageFlagBits2KHR::eUniformTexelBuffer, + }; + const vk::BufferViewCreateInfo view_ci = { + .pNext = &usage_flags, + .buffer = buffer.buffer, + .format = Vulkan::LiverpoolToVK::SurfaceFormat(dfmt, nfmt), + .offset = offset, + .range = size, + }; views.push_back({ .offset = offset, .size = size, + .is_written = is_written, .dfmt = dfmt, .nfmt = nfmt, - .handle = instance->GetDevice().createBufferView({ - .buffer = buffer.buffer, - .format = Vulkan::LiverpoolToVK::SurfaceFormat(dfmt, nfmt), - .offset = offset, - .range = size, - }), + .handle = instance->GetDevice().createBufferViewUnique(view_ci), }); - return views.back().handle; + return *views.back().handle; } constexpr u64 WATCHES_INITIAL_RESERVE = 0x4000; @@ -146,7 +148,7 @@ constexpr u64 WATCHES_RESERVE_CHUNK = 0x1000; StreamBuffer::StreamBuffer(const Vulkan::Instance& instance, Vulkan::Scheduler& scheduler_, MemoryUsage usage, u64 size_bytes) - : Buffer{instance, usage, 0, size_bytes}, scheduler{scheduler_} { + : Buffer{instance, usage, 0, AllFlags, size_bytes}, scheduler{scheduler_} { ReserveWatches(current_watches, WATCHES_INITIAL_RESERVE); ReserveWatches(previous_watches, WATCHES_INITIAL_RESERVE); const auto device = instance.GetDevice(); diff --git a/src/video_core/buffer_cache/buffer.h b/src/video_core/buffer_cache/buffer.h index d373fbffb..26d48eaef 100644 --- a/src/video_core/buffer_cache/buffer.h +++ b/src/video_core/buffer_cache/buffer.h @@ -4,6 +4,7 @@ #pragma once #include +#include #include #include #include "common/types.h" @@ -30,6 +31,15 @@ enum class MemoryUsage { Stream, ///< Requests device local host visible buffer, falling back host memory. }; +constexpr vk::BufferUsageFlags ReadFlags = + vk::BufferUsageFlagBits::eTransferSrc | vk::BufferUsageFlagBits::eUniformTexelBuffer | + vk::BufferUsageFlagBits::eUniformBuffer | vk::BufferUsageFlagBits::eIndexBuffer | + vk::BufferUsageFlagBits::eVertexBuffer | vk::BufferUsageFlagBits::eIndirectBuffer; + +constexpr vk::BufferUsageFlags AllFlags = ReadFlags | vk::BufferUsageFlagBits::eTransferDst | + vk::BufferUsageFlagBits::eStorageTexelBuffer | + vk::BufferUsageFlagBits::eStorageBuffer; + struct UniqueBuffer { explicit UniqueBuffer(vk::Device device, VmaAllocator allocator); ~UniqueBuffer(); @@ -38,9 +48,9 @@ struct UniqueBuffer { UniqueBuffer& operator=(const UniqueBuffer&) = delete; UniqueBuffer(UniqueBuffer&& other) - : buffer{std::exchange(other.buffer, VK_NULL_HANDLE)}, - allocator{std::exchange(other.allocator, VK_NULL_HANDLE)}, - allocation{std::exchange(other.allocation, VK_NULL_HANDLE)} {} + : allocator{std::exchange(other.allocator, VK_NULL_HANDLE)}, + allocation{std::exchange(other.allocation, VK_NULL_HANDLE)}, + buffer{std::exchange(other.buffer, VK_NULL_HANDLE)} {} UniqueBuffer& operator=(UniqueBuffer&& other) { buffer = std::exchange(other.buffer, VK_NULL_HANDLE); allocator = std::exchange(other.allocator, VK_NULL_HANDLE); @@ -64,7 +74,7 @@ struct UniqueBuffer { class Buffer { public: explicit Buffer(const Vulkan::Instance& instance, MemoryUsage usage, VAddr cpu_addr_, - u64 size_bytes_); + vk::BufferUsageFlags flags, u64 size_bytes_); Buffer& operator=(const Buffer&) = delete; Buffer(const Buffer&) = delete; @@ -72,7 +82,8 @@ public: Buffer& operator=(Buffer&&) = default; Buffer(Buffer&&) = default; - vk::BufferView View(u32 offset, u32 size, AmdGpu::DataFormat dfmt, AmdGpu::NumberFormat nfmt); + vk::BufferView View(u32 offset, u32 size, bool is_written, AmdGpu::DataFormat dfmt, + AmdGpu::NumberFormat nfmt); /// Increases the likeliness of this being a stream buffer void IncreaseStreamScore(int score) noexcept { @@ -120,9 +131,10 @@ public: struct BufferView { u32 offset; u32 size; + bool is_written; AmdGpu::DataFormat dfmt; AmdGpu::NumberFormat nfmt; - vk::BufferView handle; + vk::UniqueBufferView handle; }; std::vector views; }; diff --git a/src/video_core/buffer_cache/buffer_cache.cpp b/src/video_core/buffer_cache/buffer_cache.cpp index 02d6b2ce4..71228786e 100644 --- a/src/video_core/buffer_cache/buffer_cache.cpp +++ b/src/video_core/buffer_cache/buffer_cache.cpp @@ -23,7 +23,7 @@ BufferCache::BufferCache(const Vulkan::Instance& instance_, Vulkan::Scheduler& s stream_buffer{instance, scheduler, MemoryUsage::Stream, UboStreamBufferSize}, memory_tracker{&tracker} { // Ensure the first slot is used for the null buffer - void(slot_buffers.insert(instance, MemoryUsage::DeviceLocal, 0, 1)); + void(slot_buffers.insert(instance, MemoryUsage::DeviceLocal, 0, ReadFlags, 1)); } BufferCache::~BufferCache() = default; @@ -228,11 +228,11 @@ u32 BufferCache::BindIndexBuffer(bool& is_indexed, u32 index_offset) { return regs.num_indices; } -std::pair BufferCache::ObtainBuffer(VAddr device_addr, u32 size, bool is_written) { - std::scoped_lock lk{mutex}; +std::pair BufferCache::ObtainBuffer(VAddr device_addr, u32 size, bool is_written, + bool is_texel_buffer) { static constexpr u64 StreamThreshold = CACHING_PAGESIZE; const bool is_gpu_dirty = memory_tracker.IsRegionGpuModified(device_addr, size); - if (!is_written && size < StreamThreshold && !is_gpu_dirty) { + if (!is_written && !is_texel_buffer && size <= StreamThreshold && !is_gpu_dirty) { // For small uniform buffers that have not been modified by gpu // use device local stream buffer to reduce renderpass breaks. const u64 offset = stream_buffer.Copy(device_addr, size, instance.UniformMinAlignment()); @@ -421,7 +421,7 @@ BufferId BufferCache::CreateBuffer(VAddr device_addr, u32 wanted_size) { const OverlapResult overlap = ResolveOverlaps(device_addr, wanted_size); const u32 size = static_cast(overlap.end - overlap.begin); const BufferId new_buffer_id = - slot_buffers.insert(instance, MemoryUsage::DeviceLocal, overlap.begin, size); + slot_buffers.insert(instance, MemoryUsage::DeviceLocal, overlap.begin, AllFlags, size); auto& new_buffer = slot_buffers[new_buffer_id]; const size_t size_bytes = new_buffer.SizeBytes(); const auto cmdbuf = scheduler.CommandBuffer(); @@ -460,6 +460,7 @@ void BufferCache::ChangeRegister(BufferId buffer_id) { } bool BufferCache::SynchronizeBuffer(Buffer& buffer, VAddr device_addr, u32 size) { + std::scoped_lock lk{mutex}; boost::container::small_vector copies; u64 total_size_bytes = 0; u64 largest_copy = 0; @@ -495,7 +496,8 @@ bool BufferCache::SynchronizeBuffer(Buffer& buffer, VAddr device_addr, u32 size) } else { // For large one time transfers use a temporary host buffer. // RenderDoc can lag quite a bit if the stream buffer is too large. - Buffer temp_buffer{instance, MemoryUsage::Upload, 0, total_size_bytes}; + Buffer temp_buffer{instance, MemoryUsage::Upload, 0, vk::BufferUsageFlagBits::eTransferSrc, + total_size_bytes}; src_buffer = temp_buffer.Handle(); u8* const staging = temp_buffer.mapped_data.data(); for (auto& copy : copies) { diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 2bcc4f0e8..b9002cea2 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -66,7 +66,8 @@ public: u32 BindIndexBuffer(bool& is_indexed, u32 index_offset); /// Obtains a buffer for the specified region. - [[nodiscard]] std::pair ObtainBuffer(VAddr gpu_addr, u32 size, bool is_written); + [[nodiscard]] std::pair ObtainBuffer(VAddr gpu_addr, u32 size, bool is_written, + bool is_texel_buffer = false); /// Obtains a temporary buffer for usage in texture cache. [[nodiscard]] std::pair ObtainTempBuffer(VAddr gpu_addr, u32 size); @@ -125,7 +126,7 @@ private: PageManager& tracker; StreamBuffer staging_buffer; StreamBuffer stream_buffer; - std::recursive_mutex mutex; + std::mutex mutex; Common::SlotVector slot_buffers; MemoryTracker memory_tracker; PageTable page_table; diff --git a/src/video_core/host_shaders/detile_m8x1.comp b/src/video_core/host_shaders/detile_m8x1.comp index 5ec48fae2..3ca2e64bd 100644 --- a/src/video_core/host_shaders/detile_m8x1.comp +++ b/src/video_core/host_shaders/detile_m8x1.comp @@ -4,7 +4,7 @@ #version 450 #extension GL_KHR_shader_subgroup_shuffle : require -// NOTE: Current subgroup utilization is subotimal on most GPUs, so +// NOTE: Current subgroup utilization is suboptimal on most GPUs, so // it will be nice to process two tiles at once here. layout (local_size_x = 16, local_size_y = 1, local_size_z = 1) in; diff --git a/src/video_core/host_shaders/detile_m8x2.comp b/src/video_core/host_shaders/detile_m8x2.comp index d27bc6e2d..ee9b72810 100644 --- a/src/video_core/host_shaders/detile_m8x2.comp +++ b/src/video_core/host_shaders/detile_m8x2.comp @@ -3,7 +3,7 @@ #version 450 -// NOTE: Current subgroup utilization is subotimal on most GPUs, so +// NOTE: Current subgroup utilization is suboptimal on most GPUs, so // it will be nice to process two tiles at once here. layout (local_size_x = 32, local_size_y = 1, local_size_z = 1) in; diff --git a/src/video_core/page_manager.cpp b/src/video_core/page_manager.cpp index 6225f11ba..18b8aee21 100644 --- a/src/video_core/page_manager.cpp +++ b/src/video_core/page_manager.cpp @@ -51,7 +51,8 @@ struct PageManager::Impl { if (ec == EXCEPTION_ACCESS_VIOLATION) { const auto info = pExp->ExceptionRecord->ExceptionInformation; if (info[0] == 1) { // Write violation - rasterizer->InvalidateMemory(info[1], sizeof(u64)); + const VAddr addr_aligned = Common::AlignDown(info[1], PAGESIZE); + rasterizer->InvalidateMemory(addr_aligned, PAGESIZE); return EXCEPTION_CONTINUE_EXECUTION; } /* else { UNREACHABLE(); @@ -199,7 +200,8 @@ struct PageManager::Impl { const greg_t err = ctx->uc_mcontext.gregs[REG_ERR]; #endif if (err & 0x2) { - rasterizer->InvalidateMemory(address, sizeof(u64)); + const VAddr addr_aligned = Common::AlignDown(address, PAGESIZE); + rasterizer->InvalidateMemory(addr_aligned, PAGESIZE); } else { // Read not supported! UNREACHABLE(); diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp index e86d0652f..358a00447 100644 --- a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp +++ b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp @@ -5,6 +5,8 @@ #include "video_core/amdgpu/pixel_format.h" #include "video_core/renderer_vulkan/liverpool_to_vk.h" +#include + namespace Vulkan::LiverpoolToVK { using DepthBuffer = Liverpool::DepthBuffer; @@ -285,8 +287,8 @@ vk::BorderColor BorderColor(AmdGpu::BorderColor color) { std::span GetAllFormats() { static constexpr std::array formats{ + vk::Format::eA2B10G10R10SnormPack32, vk::Format::eA2B10G10R10UnormPack32, - vk::Format::eA2R10G10B10SnormPack32, vk::Format::eA2R10G10B10UnormPack32, vk::Format::eB5G6R5UnormPack16, vk::Format::eB8G8R8A8Srgb, @@ -294,6 +296,7 @@ std::span GetAllFormats() { vk::Format::eB10G11R11UfloatPack32, vk::Format::eBc1RgbaSrgbBlock, vk::Format::eBc1RgbaUnormBlock, + vk::Format::eBc2SrgbBlock, vk::Format::eBc2UnormBlock, vk::Format::eBc3SrgbBlock, vk::Format::eBc3UnormBlock, @@ -304,10 +307,12 @@ std::span GetAllFormats() { vk::Format::eBc7UnormBlock, vk::Format::eD16Unorm, vk::Format::eD16UnormS8Uint, + vk::Format::eD24UnormS8Uint, vk::Format::eD32Sfloat, vk::Format::eD32SfloatS8Uint, vk::Format::eR4G4B4A4UnormPack16, vk::Format::eR5G6B5UnormPack16, + vk::Format::eR5G5B5A1UnormPack16, vk::Format::eR8G8B8A8Srgb, vk::Format::eR8G8B8A8Uint, vk::Format::eR8G8B8A8Unorm, @@ -317,6 +322,7 @@ std::span GetAllFormats() { vk::Format::eR8G8Uint, vk::Format::eR8G8Unorm, vk::Format::eR8Sint, + vk::Format::eR8Snorm, vk::Format::eR8Uint, vk::Format::eR8Unorm, vk::Format::eR8Srgb, @@ -381,6 +387,10 @@ vk::Format SurfaceFormat(AmdGpu::DataFormat data_format, AmdGpu::NumberFormat nu num_format == AmdGpu::NumberFormat::Unorm) { return vk::Format::eB5G6R5UnormPack16; } + if (data_format == AmdGpu::DataFormat::Format1_5_5_5 && + num_format == AmdGpu::NumberFormat::Unorm) { + return vk::Format::eR5G5B5A1UnormPack16; + } if (data_format == AmdGpu::DataFormat::Format8 && num_format == AmdGpu::NumberFormat::Unorm) { return vk::Format::eR8Unorm; } @@ -419,6 +429,10 @@ vk::Format SurfaceFormat(AmdGpu::DataFormat data_format, AmdGpu::NumberFormat nu num_format == AmdGpu::NumberFormat::Unorm) { return vk::Format::eA2B10G10R10UnormPack32; } + if (data_format == AmdGpu::DataFormat::Format2_10_10_10 && + num_format == AmdGpu::NumberFormat::Snorm) { + return vk::Format::eA2B10G10R10SnormPack32; + } if (data_format == AmdGpu::DataFormat::FormatBc7 && num_format == AmdGpu::NumberFormat::Srgb) { return vk::Format::eBc7SrgbBlock; } @@ -448,12 +462,18 @@ vk::Format SurfaceFormat(AmdGpu::DataFormat data_format, AmdGpu::NumberFormat nu if (data_format == AmdGpu::DataFormat::Format8_8 && num_format == AmdGpu::NumberFormat::Unorm) { return vk::Format::eR8G8Unorm; } + if (data_format == AmdGpu::DataFormat::Format8_8 && num_format == AmdGpu::NumberFormat::Uint) { + return vk::Format::eR8G8Uint; + } if (data_format == AmdGpu::DataFormat::Format8_8 && num_format == AmdGpu::NumberFormat::Snorm) { return vk::Format::eR8G8Snorm; } if (data_format == AmdGpu::DataFormat::FormatBc7 && num_format == AmdGpu::NumberFormat::Unorm) { return vk::Format::eBc7UnormBlock; } + if (data_format == AmdGpu::DataFormat::FormatBc2 && num_format == AmdGpu::NumberFormat::Srgb) { + return vk::Format::eBc2SrgbBlock; + } if (data_format == AmdGpu::DataFormat::FormatBc2 && num_format == AmdGpu::NumberFormat::Unorm) { return vk::Format::eBc2UnormBlock; } @@ -461,14 +481,6 @@ vk::Format SurfaceFormat(AmdGpu::DataFormat data_format, AmdGpu::NumberFormat nu num_format == AmdGpu::NumberFormat::Snorm) { return vk::Format::eR16G16Snorm; } - if (data_format == AmdGpu::DataFormat::Format2_10_10_10 && - num_format == AmdGpu::NumberFormat::Unorm) { - return vk::Format::eA2R10G10B10UnormPack32; - } - if (data_format == AmdGpu::DataFormat::Format2_10_10_10 && - num_format == AmdGpu::NumberFormat::Snorm) { - return vk::Format::eA2R10G10B10SnormPack32; - } if (data_format == AmdGpu::DataFormat::Format10_11_11 && num_format == AmdGpu::NumberFormat::Float) { return vk::Format::eB10G11R11UfloatPack32; @@ -588,6 +600,8 @@ vk::Format AdjustColorBufferFormat(vk::Format base_format, return is_vo_surface ? vk::Format::eB8G8R8A8Unorm : vk::Format::eB8G8R8A8Srgb; case vk::Format::eB8G8R8A8Srgb: return is_vo_surface ? vk::Format::eR8G8B8A8Unorm : vk::Format::eR8G8B8A8Srgb; + default: + break; } } else { if (is_vo_surface && base_format == vk::Format::eR8G8B8A8Srgb) { @@ -601,27 +615,29 @@ vk::Format AdjustColorBufferFormat(vk::Format base_format, } vk::Format DepthFormat(DepthBuffer::ZFormat z_format, DepthBuffer::StencilFormat stencil_format) { - if (z_format == DepthBuffer::ZFormat::Z32Float && - stencil_format == DepthBuffer::StencilFormat::Stencil8) { + using ZFormat = DepthBuffer::ZFormat; + using StencilFormat = DepthBuffer::StencilFormat; + + if (z_format == ZFormat::Z32Float && stencil_format == StencilFormat::Stencil8) { return vk::Format::eD32SfloatS8Uint; } - if (z_format == DepthBuffer::ZFormat::Z32Float && - stencil_format == DepthBuffer::StencilFormat::Invalid) { + if (z_format == ZFormat::Z32Float && stencil_format == StencilFormat::Invalid) { return vk::Format::eD32Sfloat; } - if (z_format == DepthBuffer::ZFormat::Z16 && - stencil_format == DepthBuffer::StencilFormat::Invalid) { + if (z_format == ZFormat::Z16 && stencil_format == StencilFormat::Invalid) { return vk::Format::eD16Unorm; } - if (z_format == DepthBuffer::ZFormat::Z16 && - stencil_format == DepthBuffer::StencilFormat::Stencil8) { + if (z_format == ZFormat::Z16 && stencil_format == StencilFormat::Stencil8) { return vk::Format::eD16UnormS8Uint; } - if (z_format == DepthBuffer::ZFormat::Invalid && - stencil_format == DepthBuffer::StencilFormat::Invalid) { + if (z_format == ZFormat::Invalid && stencil_format == StencilFormat::Stencil8) { + return vk::Format::eD32SfloatS8Uint; + } + if (z_format == ZFormat::Invalid && stencil_format == StencilFormat::Invalid) { return vk::Format::eUndefined; } - UNREACHABLE(); + UNREACHABLE_MSG("Unsupported depth/stencil format. depth = {} stencil = {}", + magic_enum::enum_name(z_format), magic_enum::enum_name(stencil_format)); } void EmitQuadToTriangleListIndices(u8* out_ptr, u32 num_vertices) { diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index c78d629e4..b12708088 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -258,7 +258,7 @@ void RendererVulkan::Present(Frame* frame) { { auto* profiler_ctx = instance.GetProfilerContext(); TracyVkNamedZoneC(profiler_ctx, renderer_gpu_zone, cmdbuf, "Host frame", - MarkersPallete::GpuMarkerColor, profiler_ctx != nullptr); + MarkersPalette::GpuMarkerColor, profiler_ctx != nullptr); const vk::Extent2D extent = swapchain.GetExtent(); const std::array pre_barriers{ @@ -380,7 +380,7 @@ Frame* RendererVulkan::GetRenderFrame() { // Reset fence for next queue submission. device.resetFences(frame->present_done); - // If the window dimentions changed, recreate this frame + // If the window dimensions changed, recreate this frame if (frame->width != window.getWidth() || frame->height != window.getHeight()) { RecreateFrame(frame, window.getWidth(), window.getHeight()); } diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp index 62b50eeb1..1d9001238 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp @@ -12,9 +12,9 @@ namespace Vulkan { ComputePipeline::ComputePipeline(const Instance& instance_, Scheduler& scheduler_, - vk::PipelineCache pipeline_cache, const Shader::Info* info_, - u64 compute_key_, vk::ShaderModule module) - : instance{instance_}, scheduler{scheduler_}, compute_key{compute_key_}, info{*info_} { + vk::PipelineCache pipeline_cache, u64 compute_key_, + const Shader::Info& info_, vk::ShaderModule module) + : instance{instance_}, scheduler{scheduler_}, compute_key{compute_key_}, info{&info_} { const vk::PipelineShaderStageCreateInfo shader_ci = { .stage = vk::ShaderStageFlagBits::eCompute, .module = module, @@ -23,16 +23,26 @@ ComputePipeline::ComputePipeline(const Instance& instance_, Scheduler& scheduler u32 binding{}; boost::container::small_vector bindings; - for (const auto& buffer : info.buffers) { + for (const auto& buffer : info->buffers) { + const auto sharp = buffer.GetSharp(*info); bindings.push_back({ .binding = binding++, - .descriptorType = buffer.is_storage ? vk::DescriptorType::eStorageBuffer - : vk::DescriptorType::eUniformBuffer, + .descriptorType = buffer.IsStorage(sharp) ? vk::DescriptorType::eStorageBuffer + : vk::DescriptorType::eUniformBuffer, .descriptorCount = 1, .stageFlags = vk::ShaderStageFlagBits::eCompute, }); } - for (const auto& image : info.images) { + for (const auto& tex_buffer : info->texture_buffers) { + bindings.push_back({ + .binding = binding++, + .descriptorType = tex_buffer.is_written ? vk::DescriptorType::eStorageTexelBuffer + : vk::DescriptorType::eUniformTexelBuffer, + .descriptorCount = 1, + .stageFlags = vk::ShaderStageFlagBits::eCompute, + }); + } + for (const auto& image : info->images) { bindings.push_back({ .binding = binding++, .descriptorType = image.is_storage ? vk::DescriptorType::eStorageImage @@ -41,7 +51,7 @@ ComputePipeline::ComputePipeline(const Instance& instance_, Scheduler& scheduler .stageFlags = vk::ShaderStageFlagBits::eCompute, }); } - for (const auto& sampler : info.samplers) { + for (const auto& sampler : info->samplers) { bindings.push_back({ .binding = binding++, .descriptorType = vk::DescriptorType::eSampler, @@ -90,22 +100,24 @@ ComputePipeline::~ComputePipeline() = default; bool ComputePipeline::BindResources(VideoCore::BufferCache& buffer_cache, VideoCore::TextureCache& texture_cache) const { // Bind resource buffers and textures. + boost::container::static_vector buffer_views; boost::container::static_vector buffer_infos; boost::container::static_vector image_infos; boost::container::small_vector set_writes; Shader::PushData push_data{}; u32 binding{}; - for (const auto& buffer : info.buffers) { - const auto vsharp = buffer.GetVsharp(info); + for (const auto& desc : info->buffers) { + const auto vsharp = desc.GetSharp(*info); + const bool is_storage = desc.IsStorage(vsharp); const VAddr address = vsharp.base_address; // Most of the time when a metadata is updated with a shader it gets cleared. It means we // can skip the whole dispatch and update the tracked state instead. Also, it is not // intended to be consumed and in such rare cases (e.g. HTile introspection, CRAA) we will // need its full emulation anyways. For cases of metadata read a warning will be logged. - if (buffer.is_storage) { + if (desc.is_written) { if (texture_cache.TouchMeta(address, true)) { - LOG_WARNING(Render_Vulkan, "Metadata update skipped"); + LOG_TRACE(Render_Vulkan, "Metadata update skipped"); return false; } } else { @@ -114,13 +126,12 @@ bool ComputePipeline::BindResources(VideoCore::BufferCache& buffer_cache, } } const u32 size = vsharp.GetSize(); - if (buffer.is_written) { - texture_cache.InvalidateMemory(address, size, true); + if (desc.is_written) { + texture_cache.InvalidateMemory(address, size); } const u32 alignment = - buffer.is_storage ? instance.StorageMinAlignment() : instance.UniformMinAlignment(); - const auto [vk_buffer, offset] = - buffer_cache.ObtainBuffer(address, size, buffer.is_written); + is_storage ? instance.StorageMinAlignment() : instance.UniformMinAlignment(); + const auto [vk_buffer, offset] = buffer_cache.ObtainBuffer(address, size, desc.is_written); const u32 offset_aligned = Common::AlignDown(offset, alignment); const u32 adjust = offset - offset_aligned; if (adjust != 0) { @@ -133,20 +144,68 @@ bool ComputePipeline::BindResources(VideoCore::BufferCache& buffer_cache, .dstBinding = binding++, .dstArrayElement = 0, .descriptorCount = 1, - .descriptorType = buffer.is_storage ? vk::DescriptorType::eStorageBuffer - : vk::DescriptorType::eUniformBuffer, + .descriptorType = is_storage ? vk::DescriptorType::eStorageBuffer + : vk::DescriptorType::eUniformBuffer, .pBufferInfo = &buffer_infos.back(), }); } - for (const auto& image_desc : info.images) { - const auto tsharp = - info.ReadUd(image_desc.sgpr_base, image_desc.dword_offset); - VideoCore::ImageInfo image_info{tsharp}; - VideoCore::ImageViewInfo view_info{tsharp, image_desc.is_storage}; - const auto& image_view = texture_cache.FindTexture(image_info, view_info); - const auto& image = texture_cache.GetImage(image_view.image_id); - image_infos.emplace_back(VK_NULL_HANDLE, *image_view.image_view, image.layout); + for (const auto& desc : info->texture_buffers) { + const auto vsharp = desc.GetSharp(*info); + vk::BufferView& buffer_view = buffer_views.emplace_back(VK_NULL_HANDLE); + if (vsharp.GetDataFmt() != AmdGpu::DataFormat::FormatInvalid) { + const VAddr address = vsharp.base_address; + const u32 size = vsharp.GetSize(); + if (desc.is_written) { + if (texture_cache.TouchMeta(address, true)) { + LOG_TRACE(Render_Vulkan, "Metadata update skipped"); + return false; + } + } else { + if (texture_cache.IsMeta(address)) { + LOG_WARNING(Render_Vulkan, "Unexpected metadata read by a CS shader (buffer)"); + } + } + if (desc.is_written) { + texture_cache.InvalidateMemory(address, size); + } + const u32 alignment = instance.TexelBufferMinAlignment(); + const auto [vk_buffer, offset] = + buffer_cache.ObtainBuffer(address, size, desc.is_written, true); + const u32 fmt_stride = AmdGpu::NumBits(vsharp.GetDataFmt()) >> 3; + ASSERT_MSG(fmt_stride == vsharp.GetStride(), + "Texel buffer stride must match format stride"); + const u32 offset_aligned = Common::AlignDown(offset, alignment); + const u32 adjust = offset - offset_aligned; + if (adjust != 0) { + ASSERT(adjust % fmt_stride == 0); + push_data.AddOffset(binding, adjust / fmt_stride); + } + buffer_view = vk_buffer->View(offset_aligned, size + adjust, desc.is_written, + vsharp.GetDataFmt(), vsharp.GetNumberFmt()); + } + set_writes.push_back({ + .dstSet = VK_NULL_HANDLE, + .dstBinding = binding++, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = desc.is_written ? vk::DescriptorType::eStorageTexelBuffer + : vk::DescriptorType::eUniformTexelBuffer, + .pTexelBufferView = &buffer_view, + }); + } + + for (const auto& image_desc : info->images) { + const auto tsharp = image_desc.GetSharp(*info); + if (tsharp.GetDataFmt() != AmdGpu::DataFormat::FormatInvalid) { + VideoCore::ImageInfo image_info{tsharp}; + VideoCore::ImageViewInfo view_info{tsharp, image_desc.is_storage}; + const auto& image_view = texture_cache.FindTexture(image_info, view_info); + const auto& image = texture_cache.GetImage(image_view.image_id); + image_infos.emplace_back(VK_NULL_HANDLE, *image_view.image_view, image.layout); + } else { + image_infos.emplace_back(VK_NULL_HANDLE, VK_NULL_HANDLE, vk::ImageLayout::eGeneral); + } set_writes.push_back({ .dstSet = VK_NULL_HANDLE, .dstBinding = binding++, @@ -161,8 +220,8 @@ bool ComputePipeline::BindResources(VideoCore::BufferCache& buffer_cache, LOG_WARNING(Render_Vulkan, "Unexpected metadata read by a CS shader (texture)"); } } - for (const auto& sampler : info.samplers) { - const auto ssharp = sampler.GetSsharp(info); + for (const auto& sampler : info->samplers) { + const auto ssharp = sampler.GetSharp(*info); const auto vk_sampler = texture_cache.GetSampler(ssharp); image_infos.emplace_back(vk_sampler, VK_NULL_HANDLE, vk::ImageLayout::eGeneral); set_writes.push_back({ diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.h b/src/video_core/renderer_vulkan/vk_compute_pipeline.h index 16de5635f..0132066c5 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.h @@ -3,6 +3,7 @@ #pragma once +#include #include "shader_recompiler/runtime_info.h" #include "video_core/renderer_vulkan/vk_common.h" @@ -19,8 +20,8 @@ class Scheduler; class ComputePipeline { public: explicit ComputePipeline(const Instance& instance, Scheduler& scheduler, - vk::PipelineCache pipeline_cache, const Shader::Info* info, - u64 compute_key, vk::ShaderModule module); + vk::PipelineCache pipeline_cache, u64 compute_key, + const Shader::Info& info, vk::ShaderModule module); ~ComputePipeline(); [[nodiscard]] vk::Pipeline Handle() const noexcept { @@ -37,7 +38,7 @@ private: vk::UniquePipelineLayout pipeline_layout; vk::UniqueDescriptorSetLayout desc_layout; u64 compute_key; - Shader::Info info{}; + const Shader::Info* info; }; } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 04486290e..1ab65737c 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -20,15 +20,10 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul const GraphicsPipelineKey& key_, vk::PipelineCache pipeline_cache, std::span infos, - std::array modules) + std::span modules) : instance{instance_}, scheduler{scheduler_}, key{key_} { const vk::Device device = instance.GetDevice(); - for (u32 i = 0; i < MaxShaderStages; i++) { - if (!infos[i]) { - continue; - } - stages[i] = *infos[i]; - } + std::ranges::copy(infos, stages.begin()); BuildDescSetLayout(); const vk::PushConstantRange push_constants = { @@ -49,14 +44,14 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul boost::container::static_vector bindings; boost::container::static_vector attributes; const auto& vs_info = stages[u32(Shader::Stage::Vertex)]; - for (const auto& input : vs_info.vs_inputs) { + for (const auto& input : vs_info->vs_inputs) { if (input.instance_step_rate == Shader::Info::VsInput::InstanceIdType::OverStepRate0 || input.instance_step_rate == Shader::Info::VsInput::InstanceIdType::OverStepRate1) { // Skip attribute binding as the data will be pulled by shader continue; } - const auto buffer = vs_info.ReadUd(input.sgpr_base, input.dword_offset); + const auto buffer = vs_info->ReadUd(input.sgpr_base, input.dword_offset); attributes.push_back({ .location = input.binding, .binding = input.binding, @@ -95,7 +90,7 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul .depthClampEnable = false, .rasterizerDiscardEnable = false, .polygonMode = LiverpoolToVK::PolygonMode(key.polygon_mode), - .cullMode = vk::CullModeFlagBits::eNone /*LiverpoolToVK::CullMode(key.cull_mode)*/, + .cullMode = LiverpoolToVK::CullMode(key.cull_mode), .frontFace = key.front_face == Liverpool::FrontFace::Clockwise ? vk::FrontFace::eClockwise : vk::FrontFace::eCounterClockwise, @@ -192,21 +187,23 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul .maxDepthBounds = key.depth_bounds_max, }; - u32 shader_count{}; auto stage = u32(Shader::Stage::Vertex); - std::array shader_stages; - shader_stages[shader_count++] = vk::PipelineShaderStageCreateInfo{ - .stage = vk::ShaderStageFlagBits::eVertex, - .module = modules[stage], - .pName = "main", - }; + boost::container::static_vector + shader_stages; + if (infos[stage]) { + shader_stages.emplace_back(vk::PipelineShaderStageCreateInfo{ + .stage = vk::ShaderStageFlagBits::eVertex, + .module = modules[stage], + .pName = "main", + }); + } stage = u32(Shader::Stage::Fragment); - if (modules[stage]) { - shader_stages[shader_count++] = vk::PipelineShaderStageCreateInfo{ + if (infos[stage]) { + shader_stages.emplace_back(vk::PipelineShaderStageCreateInfo{ .stage = vk::ShaderStageFlagBits::eFragment, .module = modules[stage], .pName = "main", - }; + }); } const auto it = std::ranges::find(key.color_formats, vk::Format::eUndefined); @@ -215,8 +212,7 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul .colorAttachmentCount = num_color_formats, .pColorAttachmentFormats = key.color_formats.data(), .depthAttachmentFormat = key.depth_format, - .stencilAttachmentFormat = - key.depth.stencil_enable ? key.depth_format : vk::Format::eUndefined, + .stencilAttachmentFormat = key.stencil_format, }; std::array attachments; @@ -280,7 +276,7 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul const vk::GraphicsPipelineCreateInfo pipeline_info = { .pNext = &pipeline_rendering_ci, - .stageCount = shader_count, + .stageCount = static_cast(shader_stages.size()), .pStages = shader_stages.data(), .pVertexInputState = &vertex_input_info, .pInputAssemblyState = &input_assembly, @@ -306,17 +302,30 @@ GraphicsPipeline::~GraphicsPipeline() = default; void GraphicsPipeline::BuildDescSetLayout() { u32 binding{}; boost::container::small_vector bindings; - for (const auto& stage : stages) { - for (const auto& buffer : stage.buffers) { + for (const auto* stage : stages) { + if (!stage) { + continue; + } + for (const auto& buffer : stage->buffers) { + const auto sharp = buffer.GetSharp(*stage); bindings.push_back({ .binding = binding++, - .descriptorType = buffer.is_storage ? vk::DescriptorType::eStorageBuffer - : vk::DescriptorType::eUniformBuffer, + .descriptorType = buffer.IsStorage(sharp) ? vk::DescriptorType::eStorageBuffer + : vk::DescriptorType::eUniformBuffer, .descriptorCount = 1, .stageFlags = vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment, }); } - for (const auto& image : stage.images) { + for (const auto& tex_buffer : stage->texture_buffers) { + bindings.push_back({ + .binding = binding++, + .descriptorType = tex_buffer.is_written ? vk::DescriptorType::eStorageTexelBuffer + : vk::DescriptorType::eUniformTexelBuffer, + .descriptorCount = 1, + .stageFlags = vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment, + }); + } + for (const auto& image : stage->images) { bindings.push_back({ .binding = binding++, .descriptorType = image.is_storage ? vk::DescriptorType::eStorageImage @@ -325,7 +334,7 @@ void GraphicsPipeline::BuildDescSetLayout() { .stageFlags = vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment, }); } - for (const auto& sampler : stage.samplers) { + for (const auto& sampler : stage->samplers) { bindings.push_back({ .binding = binding++, .descriptorType = vk::DescriptorType::eSampler, @@ -346,27 +355,32 @@ void GraphicsPipeline::BindResources(const Liverpool::Regs& regs, VideoCore::BufferCache& buffer_cache, VideoCore::TextureCache& texture_cache) const { // Bind resource buffers and textures. - boost::container::static_vector buffer_infos; + boost::container::static_vector buffer_views; + boost::container::static_vector buffer_infos; boost::container::static_vector image_infos; boost::container::small_vector set_writes; Shader::PushData push_data{}; u32 binding{}; - for (const auto& stage : stages) { - if (stage.uses_step_rates) { + for (const auto* stage : stages) { + if (!stage) { + continue; + } + if (stage->uses_step_rates) { push_data.step0 = regs.vgt_instance_step_rate_0; push_data.step1 = regs.vgt_instance_step_rate_1; } - for (const auto& buffer : stage.buffers) { - const auto vsharp = buffer.GetVsharp(stage); + for (const auto& buffer : stage->buffers) { + const auto vsharp = buffer.GetSharp(*stage); + const bool is_storage = buffer.IsStorage(vsharp); if (vsharp) { const VAddr address = vsharp.base_address; if (texture_cache.IsMeta(address)) { LOG_WARNING(Render_Vulkan, "Unexpected metadata read by a PS shader (buffer)"); } const u32 size = vsharp.GetSize(); - const u32 alignment = buffer.is_storage ? instance.StorageMinAlignment() - : instance.UniformMinAlignment(); + const u32 alignment = + is_storage ? instance.StorageMinAlignment() : instance.UniformMinAlignment(); const auto [vk_buffer, offset] = buffer_cache.ObtainBuffer(address, size, buffer.is_written); const u32 offset_aligned = Common::AlignDown(offset, alignment); @@ -384,21 +398,57 @@ void GraphicsPipeline::BindResources(const Liverpool::Regs& regs, .dstBinding = binding++, .dstArrayElement = 0, .descriptorCount = 1, - .descriptorType = buffer.is_storage ? vk::DescriptorType::eStorageBuffer - : vk::DescriptorType::eUniformBuffer, + .descriptorType = is_storage ? vk::DescriptorType::eStorageBuffer + : vk::DescriptorType::eUniformBuffer, .pBufferInfo = &buffer_infos.back(), }); } + for (const auto& tex_buffer : stage->texture_buffers) { + const auto vsharp = tex_buffer.GetSharp(*stage); + vk::BufferView& buffer_view = buffer_views.emplace_back(VK_NULL_HANDLE); + if (vsharp.GetDataFmt() != AmdGpu::DataFormat::FormatInvalid) { + const VAddr address = vsharp.base_address; + const u32 size = vsharp.GetSize(); + const u32 alignment = instance.TexelBufferMinAlignment(); + const auto [vk_buffer, offset] = + buffer_cache.ObtainBuffer(address, size, tex_buffer.is_written, true); + const u32 fmt_stride = AmdGpu::NumBits(vsharp.GetDataFmt()) >> 3; + ASSERT_MSG(fmt_stride == vsharp.GetStride(), + "Texel buffer stride must match format stride"); + const u32 offset_aligned = Common::AlignDown(offset, alignment); + const u32 adjust = offset - offset_aligned; + if (adjust != 0) { + ASSERT(adjust % fmt_stride == 0); + push_data.AddOffset(binding, adjust / fmt_stride); + } + buffer_view = vk_buffer->View(offset_aligned, size + adjust, tex_buffer.is_written, + vsharp.GetDataFmt(), vsharp.GetNumberFmt()); + } + set_writes.push_back({ + .dstSet = VK_NULL_HANDLE, + .dstBinding = binding++, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = tex_buffer.is_written ? vk::DescriptorType::eStorageTexelBuffer + : vk::DescriptorType::eUniformTexelBuffer, + .pTexelBufferView = &buffer_view, + }); + } + boost::container::static_vector tsharps; - for (const auto& image_desc : stage.images) { - const auto& tsharp = tsharps.emplace_back( - stage.ReadUd(image_desc.sgpr_base, image_desc.dword_offset)); - VideoCore::ImageInfo image_info{tsharp}; - VideoCore::ImageViewInfo view_info{tsharp, image_desc.is_storage}; - const auto& image_view = texture_cache.FindTexture(image_info, view_info); - const auto& image = texture_cache.GetImage(image_view.image_id); - image_infos.emplace_back(VK_NULL_HANDLE, *image_view.image_view, image.layout); + for (const auto& image_desc : stage->images) { + const auto tsharp = image_desc.GetSharp(*stage); + if (tsharp) { + tsharps.emplace_back(tsharp); + VideoCore::ImageInfo image_info{tsharp}; + VideoCore::ImageViewInfo view_info{tsharp, image_desc.is_storage}; + const auto& image_view = texture_cache.FindTexture(image_info, view_info); + const auto& image = texture_cache.GetImage(image_view.image_id); + image_infos.emplace_back(VK_NULL_HANDLE, *image_view.image_view, image.layout); + } else { + image_infos.emplace_back(VK_NULL_HANDLE, VK_NULL_HANDLE, vk::ImageLayout::eGeneral); + } set_writes.push_back({ .dstSet = VK_NULL_HANDLE, .dstBinding = binding++, @@ -413,8 +463,8 @@ void GraphicsPipeline::BindResources(const Liverpool::Regs& regs, LOG_WARNING(Render_Vulkan, "Unexpected metadata read by a PS shader (texture)"); } } - for (const auto& sampler : stage.samplers) { - auto ssharp = sampler.GetSsharp(stage); + for (const auto& sampler : stage->samplers) { + auto ssharp = sampler.GetSharp(*stage); if (sampler.disable_aniso) { const auto& tsharp = tsharps[sampler.associated_image]; if (tsharp.base_level == 0 && tsharp.last_level == 0) { diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index bc8e9913c..3e51e6529 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h @@ -3,9 +3,9 @@ #include #include "common/types.h" -#include "shader_recompiler/runtime_info.h" #include "video_core/renderer_vulkan/liverpool_to_vk.h" #include "video_core/renderer_vulkan/vk_common.h" +#include "video_core/renderer_vulkan/vk_compute_pipeline.h" namespace VideoCore { class BufferCache; @@ -26,6 +26,7 @@ struct GraphicsPipelineKey { std::array stage_hashes; std::array color_formats; vk::Format depth_format; + vk::Format stencil_format; Liverpool::DepthControl depth; float depth_bounds_min; @@ -58,8 +59,8 @@ class GraphicsPipeline { public: explicit GraphicsPipeline(const Instance& instance, Scheduler& scheduler, const GraphicsPipelineKey& key, vk::PipelineCache pipeline_cache, - std::span infos, - std::array modules); + std::span stages, + std::span modules); ~GraphicsPipeline(); void BindResources(const Liverpool::Regs& regs, VideoCore::BufferCache& buffer_cache, @@ -74,7 +75,7 @@ public: } const Shader::Info& GetStage(Shader::Stage stage) const noexcept { - return stages[u32(stage)]; + return *stages[u32(stage)]; } bool IsEmbeddedVs() const noexcept { @@ -99,7 +100,7 @@ private: vk::UniquePipeline pipeline; vk::UniquePipelineLayout pipeline_layout; vk::UniqueDescriptorSetLayout desc_layout; - std::array stages{}; + std::array stages{}; GraphicsPipelineKey key; }; diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index 66da030f1..34727d27e 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -178,7 +178,7 @@ bool Instance::CreateDevice() { return false; } - boost::container::static_vector enabled_extensions; + boost::container::static_vector enabled_extensions; const auto add_extension = [&](std::string_view extension) -> bool { const auto result = std::find_if(available_extensions.begin(), available_extensions.end(), @@ -210,19 +210,22 @@ bool Instance::CreateDevice() { color_write_en &= add_extension(VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME); const bool calibrated_timestamps = add_extension(VK_EXT_CALIBRATED_TIMESTAMPS_EXTENSION_NAME); const bool robustness = add_extension(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME); + const bool topology_restart = + add_extension(VK_EXT_PRIMITIVE_TOPOLOGY_LIST_RESTART_EXTENSION_NAME); // These extensions are promoted by Vulkan 1.3, but for greater compatibility we use Vulkan 1.2 // with extensions. tooling_info = add_extension(VK_EXT_TOOLING_INFO_EXTENSION_NAME); const bool maintenance4 = add_extension(VK_KHR_MAINTENANCE_4_EXTENSION_NAME); + const bool maintenance5 = add_extension(VK_KHR_MAINTENANCE_5_EXTENSION_NAME); add_extension(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME); add_extension(VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_EXTENSION_NAME); - const bool has_sync2 = add_extension(VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME); - if (has_sync2) { - has_nv_checkpoints = Config::isMarkersEnabled() - ? add_extension(VK_NV_DEVICE_DIAGNOSTIC_CHECKPOINTS_EXTENSION_NAME) - : false; + if (Config::isMarkersEnabled()) { + const bool has_sync2 = add_extension(VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME); + if (has_sync2) { + has_nv_checkpoints = add_extension(VK_NV_DEVICE_DIAGNOSTIC_CHECKPOINTS_EXTENSION_NAME); + } } #ifdef __APPLE__ @@ -272,8 +275,10 @@ bool Instance::CreateDevice() { .independentBlend = features.independentBlend, .geometryShader = features.geometryShader, .logicOp = features.logicOp, + .depthBiasClamp = features.depthBiasClamp, .multiViewport = features.multiViewport, .samplerAnisotropy = features.samplerAnisotropy, + .vertexPipelineStoresAndAtomics = features.vertexPipelineStoresAndAtomics, .fragmentStoresAndAtomics = features.fragmentStoresAndAtomics, .shaderImageGatherExtended = features.shaderImageGatherExtended, .shaderStorageImageExtendedFormats = features.shaderStorageImageExtendedFormats, @@ -296,6 +301,9 @@ bool Instance::CreateDevice() { vk::PhysicalDeviceMaintenance4FeaturesKHR{ .maintenance4 = true, }, + vk::PhysicalDeviceMaintenance5FeaturesKHR{ + .maintenance5 = true, + }, vk::PhysicalDeviceDynamicRenderingFeaturesKHR{ .dynamicRendering = true, }, @@ -330,6 +338,9 @@ bool Instance::CreateDevice() { vk::PhysicalDeviceVertexInputDynamicStateFeaturesEXT{ .vertexInputDynamicState = true, }, + vk::PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT{ + .primitiveTopologyListRestart = true, + }, #ifdef __APPLE__ feature_chain.get(), #endif @@ -338,6 +349,9 @@ bool Instance::CreateDevice() { if (!maintenance4) { device_chain.unlink(); } + if (!maintenance5) { + device_chain.unlink(); + } if (!custom_border_color) { device_chain.unlink(); } @@ -351,6 +365,9 @@ bool Instance::CreateDevice() { if (!workgroup_memory_explicit_layout) { device_chain.unlink(); } + if (!topology_restart) { + device_chain.unlink(); + } if (robustness) { device_chain.get().nullDescriptor = feature_chain.get().nullDescriptor; @@ -481,6 +498,8 @@ bool Instance::IsFormatSupported(const vk::Format format) const { vk::Format Instance::GetAlternativeFormat(const vk::Format format) const { if (format == vk::Format::eB5G6R5UnormPack16) { return vk::Format::eR5G6B5UnormPack16; + } else if (format == vk::Format::eD16UnormS8Uint) { + return vk::Format::eD24UnormS8Uint; } return format; } diff --git a/src/video_core/renderer_vulkan/vk_instance.h b/src/video_core/renderer_vulkan/vk_instance.h index 4cb4741a5..5f985d4ae 100644 --- a/src/video_core/renderer_vulkan/vk_instance.h +++ b/src/video_core/renderer_vulkan/vk_instance.h @@ -192,6 +192,11 @@ public: return properties.limits.minStorageBufferOffsetAlignment; } + /// Returns the minimum required alignment for texel buffers + vk::DeviceSize TexelBufferMinAlignment() const { + return properties.limits.minTexelBufferOffsetAlignment; + } + /// Returns the minimum alignemt required for accessing host-mapped device memory vk::DeviceSize NonCoherentAtomSize() const { return properties.limits.nonCoherentAtomSize; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index c11705e7c..f8de5ffeb 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -1,149 +1,78 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "common/config.h" -#include "common/io_file.h" -#include "common/path_util.h" -#include "shader_recompiler/backend/spirv/emit_spirv.h" -#include "shader_recompiler/exception.h" -#include "shader_recompiler/recompiler.h" #include "shader_recompiler/runtime_info.h" #include "video_core/renderer_vulkan/renderer_vulkan.h" #include "video_core/renderer_vulkan/vk_instance.h" #include "video_core/renderer_vulkan/vk_pipeline_cache.h" #include "video_core/renderer_vulkan/vk_scheduler.h" -#include "video_core/renderer_vulkan/vk_shader_util.h" +#include "video_core/renderer_vulkan/vk_shader_cache.h" extern std::unique_ptr renderer; namespace Vulkan { -using Shader::VsOutput; - -void BuildVsOutputs(Shader::Info& info, const AmdGpu::Liverpool::VsOutputControl& ctl) { - const auto add_output = [&](VsOutput x, VsOutput y, VsOutput z, VsOutput w) { - if (x != VsOutput::None || y != VsOutput::None || z != VsOutput::None || - w != VsOutput::None) { - info.vs_outputs.emplace_back(Shader::VsOutputMap{x, y, z, w}); - } - }; - // VS_OUT_MISC_VEC - add_output(ctl.use_vtx_point_size ? VsOutput::PointSprite : VsOutput::None, - ctl.use_vtx_edge_flag - ? VsOutput::EdgeFlag - : (ctl.use_vtx_gs_cut_flag ? VsOutput::GsCutFlag : VsOutput::None), - ctl.use_vtx_kill_flag - ? VsOutput::KillFlag - : (ctl.use_vtx_render_target_idx ? VsOutput::GsMrtIndex : VsOutput::None), - ctl.use_vtx_viewport_idx ? VsOutput::GsVpIndex : VsOutput::None); - // VS_OUT_CCDIST0 - add_output(ctl.IsClipDistEnabled(0) - ? VsOutput::ClipDist0 - : (ctl.IsCullDistEnabled(0) ? VsOutput::CullDist0 : VsOutput::None), - ctl.IsClipDistEnabled(1) - ? VsOutput::ClipDist1 - : (ctl.IsCullDistEnabled(1) ? VsOutput::CullDist1 : VsOutput::None), - ctl.IsClipDistEnabled(2) - ? VsOutput::ClipDist2 - : (ctl.IsCullDistEnabled(2) ? VsOutput::CullDist2 : VsOutput::None), - ctl.IsClipDistEnabled(3) - ? VsOutput::ClipDist3 - : (ctl.IsCullDistEnabled(3) ? VsOutput::CullDist3 : VsOutput::None)); - // VS_OUT_CCDIST1 - add_output(ctl.IsClipDistEnabled(4) - ? VsOutput::ClipDist4 - : (ctl.IsCullDistEnabled(4) ? VsOutput::CullDist4 : VsOutput::None), - ctl.IsClipDistEnabled(5) - ? VsOutput::ClipDist5 - : (ctl.IsCullDistEnabled(5) ? VsOutput::CullDist5 : VsOutput::None), - ctl.IsClipDistEnabled(6) - ? VsOutput::ClipDist6 - : (ctl.IsCullDistEnabled(6) ? VsOutput::CullDist6 : VsOutput::None), - ctl.IsClipDistEnabled(7) - ? VsOutput::ClipDist7 - : (ctl.IsCullDistEnabled(7) ? VsOutput::CullDist7 : VsOutput::None)); -} - -Shader::Info MakeShaderInfo(Shader::Stage stage, std::span user_data, - const AmdGpu::Liverpool::Regs& regs) { - Shader::Info info{}; - info.user_data = user_data; - info.stage = stage; - switch (stage) { - case Shader::Stage::Vertex: { - info.num_user_data = regs.vs_program.settings.num_user_regs; - info.num_input_vgprs = regs.vs_program.settings.vgpr_comp_cnt; - BuildVsOutputs(info, regs.vs_output_control); - break; - } - case Shader::Stage::Fragment: { - info.num_user_data = regs.ps_program.settings.num_user_regs; - for (u32 i = 0; i < regs.num_interp; i++) { - info.ps_inputs.push_back({ - .param_index = regs.ps_inputs[i].input_offset.Value(), - .is_default = bool(regs.ps_inputs[i].use_default), - .is_flat = bool(regs.ps_inputs[i].flat_shade), - .default_value = regs.ps_inputs[i].default_value, - }); - } - break; - } - case Shader::Stage::Compute: { - const auto& cs_pgm = regs.cs_program; - info.num_user_data = cs_pgm.settings.num_user_regs; - info.workgroup_size = {cs_pgm.num_thread_x.full, cs_pgm.num_thread_y.full, - cs_pgm.num_thread_z.full}; - info.tgid_enable = {cs_pgm.IsTgidEnabled(0), cs_pgm.IsTgidEnabled(1), - cs_pgm.IsTgidEnabled(2)}; - info.shared_memory_size = cs_pgm.SharedMemSize(); - break; - } - default: - break; - } - return info; -} - PipelineCache::PipelineCache(const Instance& instance_, Scheduler& scheduler_, AmdGpu::Liverpool* liverpool_) - : instance{instance_}, scheduler{scheduler_}, liverpool{liverpool_}, inst_pool{8192}, - block_pool{512} { + : instance{instance_}, scheduler{scheduler_}, liverpool{liverpool_}, + shader_cache{std::make_unique(instance, liverpool)} { pipeline_cache = instance.GetDevice().createPipelineCacheUnique({}); - profile = Shader::Profile{ - .supported_spirv = 0x00010600U, - .subgroup_size = instance.SubgroupSize(), - .support_explicit_workgroup_layout = true, - }; } +PipelineCache::~PipelineCache() = default; + const GraphicsPipeline* PipelineCache::GetGraphicsPipeline() { + const auto& regs = liverpool->regs; // Tessellation is unsupported so skip the draw to avoid locking up the driver. - if (liverpool->regs.primitive_type == Liverpool::PrimitiveType::PatchPrimitive) { + if (regs.primitive_type == Liverpool::PrimitiveType::PatchPrimitive) { + return nullptr; + } + // There are several cases (e.g. FCE, FMask/HTile decompression) where we don't need to do an + // actual draw hence can skip pipeline creation. + if (regs.color_control.mode == Liverpool::ColorControl::OperationMode::EliminateFastClear) { + LOG_TRACE(Render_Vulkan, "FCE pass skipped"); + return nullptr; + } + if (regs.color_control.mode == Liverpool::ColorControl::OperationMode::FmaskDecompress) { + // TODO: check for a valid MRT1 to promote the draw to the resolve pass. + LOG_TRACE(Render_Vulkan, "FMask decompression pass skipped"); + return nullptr; + } + if (!RefreshGraphicsKey()) { return nullptr; } - RefreshGraphicsKey(); const auto [it, is_new] = graphics_pipelines.try_emplace(graphics_key); if (is_new) { - it.value() = CreateGraphicsPipeline(); + it.value() = std::make_unique(instance, scheduler, graphics_key, + *pipeline_cache, infos, modules); } const GraphicsPipeline* pipeline = it->second.get(); return pipeline; } const ComputePipeline* PipelineCache::GetComputePipeline() { - const auto& cs_pgm = liverpool->regs.cs_program; - ASSERT(cs_pgm.Address() != nullptr); - const auto* bininfo = Liverpool::GetBinaryInfo(cs_pgm); - compute_key = bininfo->shader_hash; + if (!RefreshComputeKey()) { + return nullptr; + } const auto [it, is_new] = compute_pipelines.try_emplace(compute_key); if (is_new) { - it.value() = CreateComputePipeline(); + it.value() = std::make_unique(instance, scheduler, *pipeline_cache, + compute_key, *infos[0], modules[0]); } const ComputePipeline* pipeline = it->second.get(); return pipeline; } -void PipelineCache::RefreshGraphicsKey() { +bool ShouldSkipShader(u64 shader_hash, const char* shader_type) { + static constexpr std::array skip_hashes = {}; + if (std::ranges::contains(skip_hashes, shader_hash)) { + LOG_WARNING(Render_Vulkan, "Skipped {} shader hash {:#x}.", shader_type, shader_hash); + return true; + } + return false; +} + +bool PipelineCache::RefreshGraphicsKey() { auto& regs = liverpool->regs; auto& key = graphics_key; @@ -176,11 +105,26 @@ void PipelineCache::RefreshGraphicsKey() { key.num_samples = regs.aa_config.NumSamples(); const auto& db = regs.depth_buffer; - key.depth_format = LiverpoolToVK::DepthFormat(db.z_info.format, db.stencil_info.format); + const auto ds_format = LiverpoolToVK::DepthFormat(db.z_info.format, db.stencil_info.format); + + if (db.z_info.format != AmdGpu::Liverpool::DepthBuffer::ZFormat::Invalid) { + key.depth_format = ds_format; + } else { + key.depth_format = vk::Format::eUndefined; + } if (key.depth.depth_enable) { key.depth.depth_enable.Assign(key.depth_format != vk::Format::eUndefined); } + if (db.stencil_info.format != AmdGpu::Liverpool::DepthBuffer::StencilFormat::Invalid) { + key.stencil_format = key.depth_format; + } else { + key.stencil_format = vk::Format::eUndefined; + } + if (key.depth.stencil_enable) { + key.depth.stencil_enable.Assign(key.stencil_format != vk::Format::eUndefined); + } + const auto skip_cb_binding = regs.color_control.mode == AmdGpu::Liverpool::ColorControl::OperationMode::Disable; @@ -210,154 +154,45 @@ void PipelineCache::RefreshGraphicsKey() { ++remapped_cb; } + u32 binding{}; for (u32 i = 0; i < MaxShaderStages; i++) { if (!regs.stage_enable.IsStageEnabled(i)) { key.stage_hashes[i] = 0; + infos[i] = nullptr; continue; } auto* pgm = regs.ProgramForStage(i); if (!pgm || !pgm->Address()) { key.stage_hashes[i] = 0; + infos[i] = nullptr; continue; } const auto* bininfo = Liverpool::GetBinaryInfo(*pgm); if (!bininfo->Valid()) { key.stage_hashes[i] = 0; + infos[i] = nullptr; continue; } - key.stage_hashes[i] = bininfo->shader_hash; - } -} - -std::unique_ptr PipelineCache::CreateGraphicsPipeline() { - const auto& regs = liverpool->regs; - - // There are several cases (e.g. FCE, FMask/HTile decompression) where we don't need to do an - // actual draw hence can skip pipeline creation. - if (regs.color_control.mode == Liverpool::ColorControl::OperationMode::EliminateFastClear) { - LOG_TRACE(Render_Vulkan, "FCE pass skipped"); - return {}; - } - - if (regs.color_control.mode == Liverpool::ColorControl::OperationMode::FmaskDecompress) { - // TODO: check for a valid MRT1 to promote the draw to the resolve pass. - LOG_TRACE(Render_Vulkan, "FMask decompression pass skipped"); - return {}; - } - - u32 binding{}; - std::array programs; - std::array infos{}; - - for (u32 i = 0; i < MaxShaderStages; i++) { - if (!graphics_key.stage_hashes[i]) { - stages[i] = VK_NULL_HANDLE; - continue; + if (ShouldSkipShader(bininfo->shader_hash, "graphics")) { + return false; } - auto* pgm = regs.ProgramForStage(i); - const auto code = pgm->Code(); - - const auto it = module_map.find(graphics_key.stage_hashes[i]); - if (it != module_map.end()) { - stages[i] = *it->second; - continue; - } - - // Dump shader code if requested. const auto stage = Shader::Stage{i}; - const u64 hash = graphics_key.stage_hashes[i]; - if (Config::dumpShaders()) { - DumpShader(code, hash, stage, "bin"); - } - - block_pool.ReleaseContents(); - inst_pool.ReleaseContents(); - - if (stage != Shader::Stage::Compute && stage != Shader::Stage::Fragment && - stage != Shader::Stage::Vertex) { - LOG_ERROR(Render_Vulkan, "Unsupported shader stage {}. PL creation skipped.", stage); - return {}; - } - - // Recompile shader to IR. - try { - LOG_INFO(Render_Vulkan, "Compiling {} shader {:#x}", stage, hash); - Shader::Info info = MakeShaderInfo(stage, pgm->user_data, regs); - info.pgm_base = pgm->Address(); - info.pgm_hash = hash; - programs[i] = - Shader::TranslateProgram(inst_pool, block_pool, code, std::move(info), profile); - - // Compile IR to SPIR-V - auto spv_code = Shader::Backend::SPIRV::EmitSPIRV(profile, programs[i], binding); - if (Config::dumpShaders()) { - DumpShader(spv_code, hash, stage, "spv"); - } - stages[i] = CompileSPV(spv_code, instance.GetDevice()); - infos[i] = &programs[i].info; - } catch (const Shader::Exception& e) { - UNREACHABLE_MSG("{}", e.what()); - } - - // Set module name to hash in renderdoc - const auto name = fmt::format("{}_{:#x}", stage, hash); - Vulkan::SetObjectName(instance.GetDevice(), stages[i], name); + const GuestProgram guest_pgm{pgm, stage}; + std::tie(infos[i], modules[i], key.stage_hashes[i]) = + shader_cache->GetProgram(guest_pgm, binding); } - - return std::make_unique(instance, scheduler, graphics_key, *pipeline_cache, - infos, stages); + return true; } -std::unique_ptr PipelineCache::CreateComputePipeline() { - const auto& cs_pgm = liverpool->regs.cs_program; - const auto code = cs_pgm.Code(); - - // Dump shader code if requested. - if (Config::dumpShaders()) { - DumpShader(code, compute_key, Shader::Stage::Compute, "bin"); +bool PipelineCache::RefreshComputeKey() { + u32 binding{}; + const auto* cs_pgm = &liverpool->regs.cs_program; + const GuestProgram guest_pgm{cs_pgm, Shader::Stage::Compute}; + if (ShouldSkipShader(guest_pgm.hash, "compute")) { + return false; } - - block_pool.ReleaseContents(); - inst_pool.ReleaseContents(); - - // Recompile shader to IR. - try { - LOG_INFO(Render_Vulkan, "Compiling cs shader {:#x}", compute_key); - Shader::Info info = - MakeShaderInfo(Shader::Stage::Compute, cs_pgm.user_data, liverpool->regs); - info.pgm_base = cs_pgm.Address(); - info.pgm_hash = compute_key; - auto program = - Shader::TranslateProgram(inst_pool, block_pool, code, std::move(info), profile); - - // Compile IR to SPIR-V - u32 binding{}; - const auto spv_code = Shader::Backend::SPIRV::EmitSPIRV(profile, program, binding); - if (Config::dumpShaders()) { - DumpShader(spv_code, compute_key, Shader::Stage::Compute, "spv"); - } - const auto module = CompileSPV(spv_code, instance.GetDevice()); - // Set module name to hash in renderdoc - const auto name = fmt::format("cs_{:#x}", compute_key); - Vulkan::SetObjectName(instance.GetDevice(), module, name); - return std::make_unique(instance, scheduler, *pipeline_cache, - &program.info, compute_key, module); - } catch (const Shader::Exception& e) { - UNREACHABLE_MSG("{}", e.what()); - return nullptr; - } -} - -void PipelineCache::DumpShader(std::span code, u64 hash, Shader::Stage stage, - std::string_view ext) { - using namespace Common::FS; - const auto dump_dir = GetUserPath(PathType::ShaderDir) / "dumps"; - if (!std::filesystem::exists(dump_dir)) { - std::filesystem::create_directories(dump_dir); - } - const auto filename = fmt::format("{}_{:#018x}.{}", stage, hash, ext); - const auto file = IOFile{dump_dir / filename, FileAccessMode::Write}; - file.WriteSpan(code); + std::tie(infos[0], modules[0], compute_key) = shader_cache->GetProgram(guest_pgm, binding); + return true; } } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h index d41723ec8..40853b746 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h @@ -4,8 +4,6 @@ #pragma once #include -#include "shader_recompiler/ir/basic_block.h" -#include "shader_recompiler/profile.h" #include "video_core/renderer_vulkan/vk_compute_pipeline.h" #include "video_core/renderer_vulkan/vk_graphics_pipeline.h" @@ -17,6 +15,7 @@ namespace Vulkan { class Instance; class Scheduler; +class ShaderCache; class PipelineCache { static constexpr size_t MaxShaderStages = 5; @@ -24,18 +23,15 @@ class PipelineCache { public: explicit PipelineCache(const Instance& instance, Scheduler& scheduler, AmdGpu::Liverpool* liverpool); - ~PipelineCache() = default; + ~PipelineCache(); const GraphicsPipeline* GetGraphicsPipeline(); const ComputePipeline* GetComputePipeline(); private: - void RefreshGraphicsKey(); - void DumpShader(std::span code, u64 hash, Shader::Stage stage, std::string_view ext); - - std::unique_ptr CreateGraphicsPipeline(); - std::unique_ptr CreateComputePipeline(); + bool RefreshGraphicsKey(); + bool RefreshComputeKey(); private: const Instance& instance; @@ -43,15 +39,13 @@ private: AmdGpu::Liverpool* liverpool; vk::UniquePipelineCache pipeline_cache; vk::UniquePipelineLayout pipeline_layout; - tsl::robin_map module_map; - std::array stages{}; + std::unique_ptr shader_cache; tsl::robin_map> compute_pipelines; tsl::robin_map> graphics_pipelines; - Shader::Profile profile{}; + std::array infos{}; + std::array modules{}; GraphicsPipelineKey graphics_key{}; u64 compute_key{}; - Common::ObjectPool inst_pool; - Common::ObjectPool block_pool; }; } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 542624a0e..cadce01eb 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -10,6 +10,7 @@ #include "video_core/renderer_vulkan/vk_scheduler.h" #include "video_core/texture_cache/image_view.h" #include "video_core/texture_cache/texture_cache.h" +#include "vk_rasterizer.h" namespace Vulkan { @@ -28,6 +29,19 @@ Rasterizer::Rasterizer(const Instance& instance_, Scheduler& scheduler_, Rasterizer::~Rasterizer() = default; +void Rasterizer::CpSync() { + scheduler.EndRendering(); + auto cmdbuf = scheduler.CommandBuffer(); + + const vk::MemoryBarrier ib_barrier{ + .srcAccessMask = vk::AccessFlagBits::eShaderWrite, + .dstAccessMask = vk::AccessFlagBits::eIndirectCommandRead, + }; + cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eComputeShader, + vk::PipelineStageFlagBits::eDrawIndirect, + vk::DependencyFlagBits::eByRegion, ib_barrier, {}, {}); +} + void Rasterizer::Draw(bool is_indexed, u32 index_offset) { RENDERER_TRACE; @@ -51,13 +65,56 @@ void Rasterizer::Draw(bool is_indexed, u32 index_offset) { BeginRendering(); UpdateDynamicState(*pipeline); + const auto [vertex_offset, instance_offset] = vs_info.GetDrawOffsets(); + if (is_indexed) { - cmdbuf.drawIndexed(num_indices, regs.num_instances.NumInstances(), 0, 0, 0); + cmdbuf.drawIndexed(num_indices, regs.num_instances.NumInstances(), 0, s32(vertex_offset), + instance_offset); } else { const u32 num_vertices = regs.primitive_type == AmdGpu::Liverpool::PrimitiveType::RectList ? 4 : regs.num_indices; - cmdbuf.draw(num_vertices, regs.num_instances.NumInstances(), 0, 0); + cmdbuf.draw(num_vertices, regs.num_instances.NumInstances(), vertex_offset, + instance_offset); + } +} + +void Rasterizer::DrawIndirect(bool is_indexed, VAddr address, u32 offset, u32 size) { + RENDERER_TRACE; + + const auto cmdbuf = scheduler.CommandBuffer(); + const auto& regs = liverpool->regs; + const GraphicsPipeline* pipeline = pipeline_cache.GetGraphicsPipeline(); + if (!pipeline) { + return; + } + + ASSERT_MSG(regs.primitive_type != AmdGpu::Liverpool::PrimitiveType::RectList, + "Unsupported primitive type for indirect draw"); + + try { + pipeline->BindResources(regs, buffer_cache, texture_cache); + } catch (...) { + UNREACHABLE(); + } + + const auto& vs_info = pipeline->GetStage(Shader::Stage::Vertex); + buffer_cache.BindVertexBuffers(vs_info); + const u32 num_indices = buffer_cache.BindIndexBuffer(is_indexed, 0); + + BeginRendering(); + UpdateDynamicState(*pipeline); + + const auto [buffer, base] = buffer_cache.ObtainBuffer(address, size, true); + const auto total_offset = base + offset; + + // We can safely ignore both SGPR UD indices and results of fetch shader parsing, as vertex and + // instance offsets will be automatically applied by Vulkan from indirect args buffer. + + if (is_indexed) { + cmdbuf.drawIndexedIndirect(buffer->Handle(), total_offset, 1, 0); + } else { + cmdbuf.drawIndirect(buffer->Handle(), total_offset, 1, 0); } } @@ -85,6 +142,32 @@ void Rasterizer::DispatchDirect() { cmdbuf.dispatch(cs_program.dim_x, cs_program.dim_y, cs_program.dim_z); } +void Rasterizer::DispatchIndirect(VAddr address, u32 offset, u32 size) { + RENDERER_TRACE; + + const auto cmdbuf = scheduler.CommandBuffer(); + const auto& cs_program = liverpool->regs.cs_program; + const ComputePipeline* pipeline = pipeline_cache.GetComputePipeline(); + if (!pipeline) { + return; + } + + try { + const auto has_resources = pipeline->BindResources(buffer_cache, texture_cache); + if (!has_resources) { + return; + } + } catch (...) { + UNREACHABLE(); + } + + scheduler.EndRendering(); + cmdbuf.bindPipeline(vk::PipelineBindPoint::eCompute, pipeline->Handle()); + const auto [buffer, base] = buffer_cache.ObtainBuffer(address, size, true); + const auto total_offset = base + offset; + cmdbuf.dispatchIndirect(buffer->Handle(), total_offset); +} + u64 Rasterizer::Flush() { const u64 current_tick = scheduler.CurrentTick(); SubmitInfo info{}; @@ -129,8 +212,12 @@ void Rasterizer::BeginRendering() { texture_cache.TouchMeta(col_buf.CmaskAddress(), false); } - if (regs.depth_buffer.z_info.format != Liverpool::DepthBuffer::ZFormat::Invalid && - regs.depth_buffer.Address() != 0) { + using ZFormat = AmdGpu::Liverpool::DepthBuffer::ZFormat; + using StencilFormat = AmdGpu::Liverpool::DepthBuffer::StencilFormat; + if (regs.depth_buffer.Address() != 0 && + ((regs.depth_control.depth_enable && regs.depth_buffer.z_info.format != ZFormat::Invalid) || + (regs.depth_control.stencil_enable && + regs.depth_buffer.stencil_info.format != StencilFormat::Invalid))) { const auto htile_address = regs.depth_htile_data_base.GetAddress(); const bool is_clear = regs.depth_render_control.depth_clear_enable || texture_cache.IsMetaCleared(htile_address); @@ -152,8 +239,10 @@ void Rasterizer::BeginRendering() { .stencil = regs.stencil_clear}}, }; texture_cache.TouchMeta(htile_address, false); - state.has_depth = true; - state.has_stencil = image.info.usage.stencil; + state.has_depth = + regs.depth_buffer.z_info.format != AmdGpu::Liverpool::DepthBuffer::ZFormat::Invalid; + state.has_stencil = regs.depth_buffer.stencil_info.format != + AmdGpu::Liverpool::DepthBuffer::StencilFormat::Invalid; } scheduler.BeginRendering(state); } @@ -232,7 +321,7 @@ void Rasterizer::UpdateDepthStencilState() { } void Rasterizer::ScopeMarkerBegin(const std::string_view& str) { - if (!Config::isMarkersEnabled()) { + if (Config::nullGpu() || !Config::isMarkersEnabled()) { return; } @@ -243,7 +332,7 @@ void Rasterizer::ScopeMarkerBegin(const std::string_view& str) { } void Rasterizer::ScopeMarkerEnd() { - if (!Config::isMarkersEnabled()) { + if (Config::nullGpu() || !Config::isMarkersEnabled()) { return; } @@ -252,7 +341,7 @@ void Rasterizer::ScopeMarkerEnd() { } void Rasterizer::ScopedMarkerInsert(const std::string_view& str) { - if (!Config::isMarkersEnabled()) { + if (Config::nullGpu() || !Config::isMarkersEnabled()) { return; } @@ -263,7 +352,7 @@ void Rasterizer::ScopedMarkerInsert(const std::string_view& str) { } void Rasterizer::Breadcrumb(u64 id) { - if (!instance.HasNvCheckpoints()) { + if (Config::nullGpu() || !instance.HasNvCheckpoints()) { return; } scheduler.CommandBuffer().setCheckpointNV(id); diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index a151ebc27..c38fe6ee9 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -32,8 +32,10 @@ public: } void Draw(bool is_indexed, u32 index_offset = 0); + void DrawIndirect(bool is_indexed, VAddr address, u32 offset, u32 size); void DispatchDirect(); + void DispatchIndirect(VAddr address, u32 offset, u32 size); void ScopeMarkerBegin(const std::string_view& str); void ScopeMarkerEnd(); @@ -44,6 +46,7 @@ public: void MapMemory(VAddr addr, u64 size); void UnmapMemory(VAddr addr, u64 size); + void CpSync(); u64 Flush(); private: diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 10ee6ea62..2f1f13d72 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -29,15 +29,22 @@ void Scheduler::BeginRendering(const RenderState& new_state) { is_rendering = true; render_state = new_state; + const auto witdh = + render_state.width != std::numeric_limits::max() ? render_state.width : 1; + const auto height = + render_state.height != std::numeric_limits::max() ? render_state.height : 1; + const vk::RenderingInfo rendering_info = { .renderArea = { .offset = {0, 0}, - .extent = {render_state.width, render_state.height}, + .extent = {witdh, height}, }, .layerCount = 1, .colorAttachmentCount = render_state.num_color_attachments, - .pColorAttachments = render_state.color_attachments.data(), + .pColorAttachments = render_state.num_color_attachments > 0 + ? render_state.color_attachments.data() + : nullptr, .pDepthAttachment = render_state.has_depth ? &render_state.depth_attachment : nullptr, .pStencilAttachment = render_state.has_stencil ? &render_state.depth_attachment : nullptr, }; @@ -72,7 +79,7 @@ void Scheduler::EndRendering() { }, }); } - if (render_state.has_depth) { + if (render_state.has_depth || render_state.has_stencil) { barriers.push_back(vk::ImageMemoryBarrier{ .srcAccessMask = vk::AccessFlagBits::eDepthStencilAttachmentWrite, .dstAccessMask = vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite, @@ -138,7 +145,7 @@ void Scheduler::AllocateWorkerCommandBuffers() { auto* profiler_ctx = instance.GetProfilerContext(); if (profiler_ctx) { static const auto scope_loc = - GPU_SCOPE_LOCATION("Guest Frame", MarkersPallete::GpuMarkerColor); + GPU_SCOPE_LOCATION("Guest Frame", MarkersPalette::GpuMarkerColor); new (profiler_scope) tracy::VkCtxScope{profiler_ctx, &scope_loc, current_cmdbuf, true}; } } diff --git a/src/video_core/renderer_vulkan/vk_shader_cache.cpp b/src/video_core/renderer_vulkan/vk_shader_cache.cpp new file mode 100644 index 000000000..9250f84ce --- /dev/null +++ b/src/video_core/renderer_vulkan/vk_shader_cache.cpp @@ -0,0 +1,192 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/config.h" +#include "common/io_file.h" +#include "common/path_util.h" +#include "shader_recompiler/backend/spirv/emit_spirv.h" +#include "shader_recompiler/recompiler.h" +#include "video_core/renderer_vulkan/vk_instance.h" +#include "video_core/renderer_vulkan/vk_platform.h" +#include "video_core/renderer_vulkan/vk_shader_cache.h" +#include "video_core/renderer_vulkan/vk_shader_util.h" + +namespace Vulkan { + +using Shader::VsOutput; + +void BuildVsOutputs(Shader::Info& info, const AmdGpu::Liverpool::VsOutputControl& ctl) { + const auto add_output = [&](VsOutput x, VsOutput y, VsOutput z, VsOutput w) { + if (x != VsOutput::None || y != VsOutput::None || z != VsOutput::None || + w != VsOutput::None) { + info.vs_outputs.emplace_back(Shader::VsOutputMap{x, y, z, w}); + } + }; + // VS_OUT_MISC_VEC + add_output(ctl.use_vtx_point_size ? VsOutput::PointSprite : VsOutput::None, + ctl.use_vtx_edge_flag + ? VsOutput::EdgeFlag + : (ctl.use_vtx_gs_cut_flag ? VsOutput::GsCutFlag : VsOutput::None), + ctl.use_vtx_kill_flag + ? VsOutput::KillFlag + : (ctl.use_vtx_render_target_idx ? VsOutput::GsMrtIndex : VsOutput::None), + ctl.use_vtx_viewport_idx ? VsOutput::GsVpIndex : VsOutput::None); + // VS_OUT_CCDIST0 + add_output(ctl.IsClipDistEnabled(0) + ? VsOutput::ClipDist0 + : (ctl.IsCullDistEnabled(0) ? VsOutput::CullDist0 : VsOutput::None), + ctl.IsClipDistEnabled(1) + ? VsOutput::ClipDist1 + : (ctl.IsCullDistEnabled(1) ? VsOutput::CullDist1 : VsOutput::None), + ctl.IsClipDistEnabled(2) + ? VsOutput::ClipDist2 + : (ctl.IsCullDistEnabled(2) ? VsOutput::CullDist2 : VsOutput::None), + ctl.IsClipDistEnabled(3) + ? VsOutput::ClipDist3 + : (ctl.IsCullDistEnabled(3) ? VsOutput::CullDist3 : VsOutput::None)); + // VS_OUT_CCDIST1 + add_output(ctl.IsClipDistEnabled(4) + ? VsOutput::ClipDist4 + : (ctl.IsCullDistEnabled(4) ? VsOutput::CullDist4 : VsOutput::None), + ctl.IsClipDistEnabled(5) + ? VsOutput::ClipDist5 + : (ctl.IsCullDistEnabled(5) ? VsOutput::CullDist5 : VsOutput::None), + ctl.IsClipDistEnabled(6) + ? VsOutput::ClipDist6 + : (ctl.IsCullDistEnabled(6) ? VsOutput::CullDist6 : VsOutput::None), + ctl.IsClipDistEnabled(7) + ? VsOutput::ClipDist7 + : (ctl.IsCullDistEnabled(7) ? VsOutput::CullDist7 : VsOutput::None)); +} + +Shader::Info MakeShaderInfo(const GuestProgram& pgm, const AmdGpu::Liverpool::Regs& regs) { + Shader::Info info{}; + info.user_data = pgm.user_data; + info.pgm_base = VAddr(pgm.code.data()); + info.pgm_hash = pgm.hash; + info.stage = pgm.stage; + switch (pgm.stage) { + case Shader::Stage::Vertex: { + info.num_user_data = regs.vs_program.settings.num_user_regs; + info.num_input_vgprs = regs.vs_program.settings.vgpr_comp_cnt; + BuildVsOutputs(info, regs.vs_output_control); + break; + } + case Shader::Stage::Fragment: { + info.num_user_data = regs.ps_program.settings.num_user_regs; + for (u32 i = 0; i < regs.num_interp; i++) { + info.ps_inputs.push_back({ + .param_index = regs.ps_inputs[i].input_offset.Value(), + .is_default = bool(regs.ps_inputs[i].use_default), + .is_flat = bool(regs.ps_inputs[i].flat_shade), + .default_value = regs.ps_inputs[i].default_value, + }); + } + break; + } + case Shader::Stage::Compute: { + const auto& cs_pgm = regs.cs_program; + info.num_user_data = cs_pgm.settings.num_user_regs; + info.workgroup_size = {cs_pgm.num_thread_x.full, cs_pgm.num_thread_y.full, + cs_pgm.num_thread_z.full}; + info.tgid_enable = {cs_pgm.IsTgidEnabled(0), cs_pgm.IsTgidEnabled(1), + cs_pgm.IsTgidEnabled(2)}; + info.shared_memory_size = cs_pgm.SharedMemSize(); + break; + } + default: + break; + } + return info; +} + +[[nodiscard]] inline u64 HashCombine(const u64 seed, const u64 hash) { + return seed ^ (hash + 0x9e3779b9 + (seed << 6) + (seed >> 2)); +} + +ShaderCache::ShaderCache(const Instance& instance_, AmdGpu::Liverpool* liverpool_) + : instance{instance_}, liverpool{liverpool_}, inst_pool{8192}, block_pool{512} { + profile = Shader::Profile{ + .supported_spirv = instance.ApiVersion() >= VK_API_VERSION_1_3 ? 0x00010600U : 0x00010500U, + .subgroup_size = instance.SubgroupSize(), + .support_explicit_workgroup_layout = true, + }; +} + +vk::ShaderModule ShaderCache::CompileModule(Shader::Info& info, std::span code, + size_t perm_idx, u32& binding) { + LOG_INFO(Render_Vulkan, "Compiling {} shader {:#x} {}", info.stage, info.pgm_hash, + perm_idx != 0 ? "(permutation)" : ""); + + if (Config::dumpShaders()) { + DumpShader(code, info.pgm_hash, info.stage, perm_idx, "bin"); + } + + block_pool.ReleaseContents(); + inst_pool.ReleaseContents(); + const auto ir_program = Shader::TranslateProgram(inst_pool, block_pool, code, info, profile); + + // Compile IR to SPIR-V + const auto spv = Shader::Backend::SPIRV::EmitSPIRV(profile, ir_program, binding); + if (Config::dumpShaders()) { + DumpShader(spv, info.pgm_hash, info.stage, perm_idx, "spv"); + } + + // Create module and set name to hash in renderdoc + const auto module = CompileSPV(spv, instance.GetDevice()); + ASSERT(module != VK_NULL_HANDLE); + const auto name = fmt::format("{}_{:#x}_{}", info.stage, info.pgm_hash, perm_idx); + Vulkan::SetObjectName(instance.GetDevice(), module, name); + return module; +} + +Program* ShaderCache::CreateProgram(const GuestProgram& pgm, u32& binding) { + Program* program = program_pool.Create(MakeShaderInfo(pgm, liverpool->regs)); + u32 start_binding = binding; + const auto module = CompileModule(program->info, pgm.code, 0, binding); + program->modules.emplace_back(module, StageSpecialization{program->info, start_binding}); + return program; +} + +std::tuple ShaderCache::GetProgram( + const GuestProgram& pgm, u32& binding) { + auto [it_pgm, new_program] = program_cache.try_emplace(pgm.hash); + if (new_program) { + auto program = CreateProgram(pgm, binding); + const auto module = program->modules.back().module; + it_pgm.value() = program; + return std::make_tuple(&program->info, module, HashCombine(pgm.hash, 0)); + } + + Program* program = it_pgm->second; + const auto& info = program->info; + size_t perm_idx = program->modules.size(); + StageSpecialization spec{info, binding}; + vk::ShaderModule module{}; + + const auto it = std::ranges::find(program->modules, spec, &Program::Module::spec); + if (it == program->modules.end()) { + auto new_info = MakeShaderInfo(pgm, liverpool->regs); + module = CompileModule(new_info, pgm.code, perm_idx, binding); + program->modules.emplace_back(module, std::move(spec)); + } else { + binding += info.NumBindings(); + module = it->module; + perm_idx = std::distance(program->modules.begin(), it); + } + return std::make_tuple(&info, module, HashCombine(pgm.hash, perm_idx)); +} + +void ShaderCache::DumpShader(std::span code, u64 hash, Shader::Stage stage, + size_t perm_idx, std::string_view ext) { + using namespace Common::FS; + const auto dump_dir = GetUserPath(PathType::ShaderDir) / "dumps"; + if (!std::filesystem::exists(dump_dir)) { + std::filesystem::create_directories(dump_dir); + } + const auto filename = fmt::format("{}_{:#018x}_{}.{}", stage, hash, perm_idx, ext); + const auto file = IOFile{dump_dir / filename, FileAccessMode::Write}; + file.WriteSpan(code); +} + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_shader_cache.h b/src/video_core/renderer_vulkan/vk_shader_cache.h new file mode 100644 index 000000000..191e1b08c --- /dev/null +++ b/src/video_core/renderer_vulkan/vk_shader_cache.h @@ -0,0 +1,156 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include "common/object_pool.h" +#include "shader_recompiler/ir/basic_block.h" +#include "shader_recompiler/profile.h" +#include "shader_recompiler/runtime_info.h" +#include "video_core/amdgpu/liverpool.h" +#include "video_core/renderer_vulkan/vk_common.h" + +namespace Vulkan { + +class Instance; + +struct BufferSpecialization { + u16 stride : 14; + u16 is_storage : 1; + + auto operator<=>(const BufferSpecialization&) const = default; +}; + +struct TextureBufferSpecialization { + bool is_integer; + + auto operator<=>(const TextureBufferSpecialization&) const = default; +}; + +struct ImageSpecialization { + AmdGpu::ImageType type; + bool is_integer; + + auto operator<=>(const ImageSpecialization&) const = default; +}; + +struct StageSpecialization { + static constexpr size_t MaxStageResources = 32; + + const Shader::Info* info; + std::bitset bitset{}; + boost::container::small_vector buffers; + boost::container::small_vector tex_buffers; + boost::container::small_vector images; + u32 start_binding{}; + + void ForEachSharp(u32& binding, auto& spec_list, auto& desc_list, auto&& func) { + for (const auto& desc : desc_list) { + auto& spec = spec_list.emplace_back(); + const auto sharp = desc.GetSharp(*info); + if (!sharp) { + binding++; + continue; + } + bitset.set(binding++); + func(spec, desc, sharp); + } + } + + StageSpecialization(const Shader::Info& info_, u32 start_binding_) + : info{&info_}, start_binding{start_binding_} { + u32 binding{}; + ForEachSharp(binding, buffers, info->buffers, + [](auto& spec, const auto& desc, AmdGpu::Buffer sharp) { + spec.stride = sharp.GetStride(); + spec.is_storage = desc.IsStorage(sharp); + }); + ForEachSharp(binding, tex_buffers, info->texture_buffers, + [](auto& spec, const auto& desc, AmdGpu::Buffer sharp) { + spec.is_integer = AmdGpu::IsInteger(sharp.GetNumberFmt()); + }); + ForEachSharp(binding, images, info->images, + [](auto& spec, const auto& desc, AmdGpu::Image sharp) { + spec.type = sharp.GetType(); + spec.is_integer = AmdGpu::IsInteger(sharp.GetNumberFmt()); + }); + } + + bool operator==(const StageSpecialization& other) const { + if (start_binding != other.start_binding) { + return false; + } + u32 binding{}; + for (u32 i = 0; i < buffers.size(); i++) { + if (other.bitset[binding++] && buffers[i] != other.buffers[i]) { + return false; + } + } + for (u32 i = 0; i < tex_buffers.size(); i++) { + if (other.bitset[binding++] && tex_buffers[i] != other.tex_buffers[i]) { + return false; + } + } + for (u32 i = 0; i < images.size(); i++) { + if (other.bitset[binding++] && images[i] != other.images[i]) { + return false; + } + } + return true; + } +}; + +struct Program { + struct Module { + vk::ShaderModule module; + StageSpecialization spec; + }; + + Shader::Info info; + boost::container::small_vector modules; + + explicit Program(const Shader::Info& info_) : info{info_} {} +}; + +struct GuestProgram { + Shader::Stage stage; + std::span user_data; + std::span code; + u64 hash; + + explicit GuestProgram(const auto* pgm, Shader::Stage stage_) + : stage{stage_}, user_data{pgm->user_data}, code{pgm->Code()} { + const auto* bininfo = AmdGpu::Liverpool::GetBinaryInfo(*pgm); + hash = bininfo->shader_hash; + } +}; + +class ShaderCache { +public: + explicit ShaderCache(const Instance& instance, AmdGpu::Liverpool* liverpool); + ~ShaderCache() = default; + + std::tuple GetProgram(const GuestProgram& pgm, + u32& binding); + +private: + void DumpShader(std::span code, u64 hash, Shader::Stage stage, size_t perm_idx, + std::string_view ext); + vk::ShaderModule CompileModule(Shader::Info& info, std::span code, size_t perm_idx, + u32& binding); + Program* CreateProgram(const GuestProgram& pgm, u32& binding); + +private: + const Instance& instance; + AmdGpu::Liverpool* liverpool; + Shader::Profile profile{}; + tsl::robin_map program_cache; + Common::ObjectPool inst_pool; + Common::ObjectPool block_pool; + Common::ObjectPool program_pool; +}; + +} // namespace Vulkan diff --git a/src/video_core/texture_cache/image.cpp b/src/video_core/texture_cache/image.cpp index bae4b89d4..0b725655b 100644 --- a/src/video_core/texture_cache/image.cpp +++ b/src/video_core/texture_cache/image.cpp @@ -73,14 +73,15 @@ static vk::ImageUsageFlags ImageUsageFlags(const ImageInfo& info) { if (!info.IsBlockCoded() && !info.IsPacked()) { usage |= vk::ImageUsageFlagBits::eColorAttachment; } + + // In cases where an image is created as a render/depth target and cleared with compute, + // we cannot predict whether it will be used as a storage image. A proper solution would + // involve re-creating the resource with a new configuration and copying previous content + // into it. However, for now, we will set storage usage for all images (if the format + // allows), sacrificing a bit of performance. Note use of ExtendedUsage flag set by default. + usage |= vk::ImageUsageFlagBits::eStorage; } - // In cases where an image is created as a render/depth target and cleared with compute, - // we cannot predict whether it will be used as a storage image. A proper solution would - // involve re-creating the resource with a new configuration and copying previous content into - // it. However, for now, we will set storage usage for all images (if the format allows), - // sacrificing a bit of performance. Note use of ExtendedUsage flag set by default. - usage |= vk::ImageUsageFlagBits::eStorage; return usage; } @@ -131,11 +132,19 @@ Image::Image(const Vulkan::Instance& instance_, Vulkan::Scheduler& scheduler_, usage = ImageUsageFlags(info); - if (info.pixel_format == vk::Format::eD32Sfloat) { + switch (info.pixel_format) { + case vk::Format::eD16Unorm: + case vk::Format::eD32Sfloat: + case vk::Format::eX8D24UnormPack32: aspect_mask = vk::ImageAspectFlagBits::eDepth; - } - if (info.pixel_format == vk::Format::eD32SfloatS8Uint) { + break; + case vk::Format::eD16UnormS8Uint: + case vk::Format::eD24UnormS8Uint: + case vk::Format::eD32SfloatS8Uint: aspect_mask = vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil; + break; + default: + break; } const vk::ImageCreateInfo image_ci = { diff --git a/src/video_core/texture_cache/image.h b/src/video_core/texture_cache/image.h index 5a888346f..3df8ddb70 100644 --- a/src/video_core/texture_cache/image.h +++ b/src/video_core/texture_cache/image.h @@ -43,9 +43,9 @@ struct UniqueImage { UniqueImage& operator=(const UniqueImage&) = delete; UniqueImage(UniqueImage&& other) - : image{std::exchange(other.image, VK_NULL_HANDLE)}, - allocator{std::exchange(other.allocator, VK_NULL_HANDLE)}, - allocation{std::exchange(other.allocation, VK_NULL_HANDLE)} {} + : allocator{std::exchange(other.allocator, VK_NULL_HANDLE)}, + allocation{std::exchange(other.allocation, VK_NULL_HANDLE)}, + image{std::exchange(other.image, VK_NULL_HANDLE)} {} UniqueImage& operator=(UniqueImage&& other) { image = std::exchange(other.image, VK_NULL_HANDLE); allocator = std::exchange(other.allocator, VK_NULL_HANDLE); diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp index 17b78a6d5..4ac4aee8f 100644 --- a/src/video_core/texture_cache/image_info.cpp +++ b/src/video_core/texture_cache/image_info.cpp @@ -34,6 +34,7 @@ static vk::ImageType ConvertImageType(AmdGpu::ImageType type) noexcept { case AmdGpu::ImageType::Color1DArray: return vk::ImageType::e1D; case AmdGpu::ImageType::Color2D: + case AmdGpu::ImageType::Color2DMsaa: case AmdGpu::ImageType::Cube: case AmdGpu::ImageType::Color2DArray: return vk::ImageType::e2D; @@ -219,7 +220,12 @@ ImageInfo::ImageInfo(const AmdGpu::Image& image) noexcept { guest_address = image.Address(); mips_layout.reserve(resources.levels); + tiling_idx = image.tiling_index; + UpdateSize(); +} +void ImageInfo::UpdateSize() { + mips_layout.clear(); MipInfo mip_info{}; guest_size_bytes = 0; for (auto mip = 0u; mip < resources.levels; ++mip) { @@ -265,7 +271,7 @@ ImageInfo::ImageInfo(const AmdGpu::Image& image) noexcept { ASSERT(!props.is_block); ASSERT(num_samples == 1); std::tie(mip_info.pitch, mip_info.size) = - ImageSizeMacroTiled(mip_w, mip_h, bpp, num_samples, image.tiling_index); + ImageSizeMacroTiled(mip_w, mip_h, bpp, num_samples, tiling_idx); break; } default: { diff --git a/src/video_core/texture_cache/image_info.h b/src/video_core/texture_cache/image_info.h index 9dad0dd67..ddad318d9 100644 --- a/src/video_core/texture_cache/image_info.h +++ b/src/video_core/texture_cache/image_info.h @@ -29,6 +29,8 @@ struct ImageInfo { bool IsPacked() const; bool IsDepthStencil() const; + void UpdateSize(); + struct { VAddr cmask_addr; VAddr fmask_addr; @@ -69,6 +71,7 @@ struct ImageInfo { boost::container::small_vector mips_layout; VAddr guest_address{0}; u32 guest_size_bytes{0}; + u32 tiling_idx{0}; // TODO: merge with existing! }; } // namespace VideoCore diff --git a/src/video_core/texture_cache/image_view.cpp b/src/video_core/texture_cache/image_view.cpp index cbf77f2d8..bcdc11ad9 100644 --- a/src/video_core/texture_cache/image_view.cpp +++ b/src/video_core/texture_cache/image_view.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" +#include "video_core/amdgpu/resource.h" #include "video_core/renderer_vulkan/liverpool_to_vk.h" #include "video_core/renderer_vulkan/vk_instance.h" #include "video_core/texture_cache/image.h" @@ -16,6 +17,7 @@ vk::ImageViewType ConvertImageViewType(AmdGpu::ImageType type) { case AmdGpu::ImageType::Color1DArray: return vk::ImageViewType::e1DArray; case AmdGpu::ImageType::Color2D: + case AmdGpu::ImageType::Color2DMsaa: return vk::ImageViewType::e2D; case AmdGpu::ImageType::Cube: return vk::ImageViewType::eCube; @@ -48,9 +50,9 @@ vk::ComponentSwizzle ConvertComponentSwizzle(u32 dst_sel) { } bool IsIdentityMapping(u32 dst_sel, u32 num_components) { - return (num_components == 1 && dst_sel == 0b100) || - (num_components == 2 && dst_sel == 0b101'100) || - (num_components == 3 && dst_sel == 0b110'101'100) || + return (num_components == 1 && dst_sel == 0b001'000'000'100) || + (num_components == 2 && dst_sel == 0b001'000'101'100) || + (num_components == 3 && dst_sel == 0b001'110'101'100) || (num_components == 4 && dst_sel == 0b111'110'101'100); } @@ -58,6 +60,9 @@ vk::Format TrySwizzleFormat(vk::Format format, u32 dst_sel) { if (format == vk::Format::eR8G8B8A8Unorm && dst_sel == 0b111100101110) { return vk::Format::eB8G8R8A8Unorm; } + if (format == vk::Format::eR8G8B8A8Srgb && dst_sel == 0b111100101110) { + return vk::Format::eB8G8R8A8Srgb; + } return format; } @@ -110,7 +115,7 @@ ImageViewInfo::ImageViewInfo(const AmdGpu::Liverpool::DepthBuffer& depth_buffer, ImageView::ImageView(const Vulkan::Instance& instance, const ImageViewInfo& info_, Image& image, ImageId image_id_, std::optional usage_override /*= {}*/) - : info{info_}, image_id{image_id_} { + : image_id{image_id_}, info{info_} { vk::ImageViewUsageCreateInfo usage_ci{}; if (usage_override) { usage_ci.usage = usage_override.value(); diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp index 6bc893b09..3354a8ecb 100644 --- a/src/video_core/texture_cache/texture_cache.cpp +++ b/src/video_core/texture_cache/texture_cache.cpp @@ -18,11 +18,15 @@ TextureCache::TextureCache(const Vulkan::Instance& instance_, Vulkan::Scheduler& BufferCache& buffer_cache_, PageManager& tracker_) : instance{instance_}, scheduler{scheduler_}, buffer_cache{buffer_cache_}, tracker{tracker_}, tile_manager{instance, scheduler} { - ImageInfo info; + ImageInfo info{}; info.pixel_format = vk::Format::eR8G8B8A8Unorm; info.type = vk::ImageType::e2D; + info.tiling_idx = u32(AmdGpu::TilingMode::Texture_MicroTiled); + info.num_bits = 32; + info.UpdateSize(); const ImageId null_id = slot_images.insert(instance, scheduler, info); ASSERT(null_id.index == 0); + slot_images[null_id].flags = ImageFlagBits{}; ImageViewInfo view_info; void(slot_image_views.insert(instance, view_info, slot_images[null_id], null_id)); @@ -30,10 +34,10 @@ TextureCache::TextureCache(const Vulkan::Instance& instance_, Vulkan::Scheduler& TextureCache::~TextureCache() = default; -void TextureCache::InvalidateMemory(VAddr address, size_t size, bool from_compute) { +void TextureCache::InvalidateMemory(VAddr address, size_t size) { std::unique_lock lock{mutex}; ForEachImageInRegion(address, size, [&](ImageId image_id, Image& image) { - if (from_compute && !image.Overlaps(address, size)) { + if (!image.Overlaps(address, size)) { return; } // Ensure image is reuploaded when accessed again. @@ -200,6 +204,7 @@ ImageView& TextureCache::FindDepthTarget(const ImageInfo& image_info, Image& image = slot_images[image_id]; image.flags |= ImageFlagBits::GpuModified; image.flags &= ~ImageFlagBits::CpuModified; + image.aspect_mask = vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil; const auto new_layout = view_info.is_storage ? vk::ImageLayout::eDepthStencilAttachmentOptimal : vk::ImageLayout::eDepthStencilReadOnlyOptimal; @@ -328,7 +333,6 @@ void TextureCache::UnregisterImage(ImageId image_id) { } image_ids.erase(vector_it); }); - slot_images.erase(image_id); } void TextureCache::TrackImage(Image& image, ImageId image_id) { diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 137b60141..31b1e3939 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -28,7 +28,7 @@ class TextureCache { using Entry = boost::container::small_vector; static constexpr size_t AddressSpaceBits = 39; static constexpr size_t FirstLevelBits = 9; - static constexpr size_t PageBits = 22; + static constexpr size_t PageBits = 20; }; using PageTable = MultiLevelPageTable; @@ -38,7 +38,7 @@ public: ~TextureCache(); /// Invalidates any image in the logical page range. - void InvalidateMemory(VAddr address, size_t size, bool from_compute = false); + void InvalidateMemory(VAddr address, size_t size); /// Evicts any images that overlap the unmapped range. void UnmapMemory(VAddr cpu_addr, size_t size); @@ -124,7 +124,7 @@ private: using FuncReturn = typename std::invoke_result::type; static constexpr bool BOOL_BREAK = std::is_same_v; boost::container::small_vector images; - ForEachPage(cpu_addr, size, [this, &images, cpu_addr, size, func](u64 page) { + ForEachPage(cpu_addr, size, [this, &images, func](u64 page) { const auto it = page_table.find(page); if (it == nullptr) { if constexpr (BOOL_BREAK) { diff --git a/src/video_core/texture_cache/tile_manager.cpp b/src/video_core/texture_cache/tile_manager.cpp index f08f2094c..8b0227624 100644 --- a/src/video_core/texture_cache/tile_manager.cpp +++ b/src/video_core/texture_cache/tile_manager.cpp @@ -187,6 +187,7 @@ vk::Format DemoteImageFormatForDetiling(vk::Format format) { case vk::Format::eR32Uint: case vk::Format::eR16G16Sfloat: case vk::Format::eR16G16Unorm: + case vk::Format::eB10G11R11UfloatPack32: return vk::Format::eR32Uint; case vk::Format::eBc1RgbaSrgbBlock: case vk::Format::eBc1RgbaUnormBlock: @@ -202,6 +203,7 @@ vk::Format DemoteImageFormatForDetiling(vk::Format format) { case vk::Format::eBc3SrgbBlock: case vk::Format::eBc3UnormBlock: case vk::Format::eBc5UnormBlock: + case vk::Format::eBc5SnormBlock: case vk::Format::eBc7SrgbBlock: case vk::Format::eBc7UnormBlock: case vk::Format::eBc6HUfloatBlock: @@ -249,11 +251,11 @@ struct DetilerParams { u32 sizes[14]; }; -static constexpr size_t StreamBufferSize = 128_MB; +static constexpr size_t StreamBufferSize = 1_GB; TileManager::TileManager(const Vulkan::Instance& instance, Vulkan::Scheduler& scheduler) : instance{instance}, scheduler{scheduler}, - stream_buffer{instance, scheduler, MemoryUsage::Stream, StreamBufferSize} { + stream_buffer{instance, scheduler, MemoryUsage::Upload, StreamBufferSize} { static const std::array detiler_shaders{ HostShaders::DETILE_M8X1_COMP, HostShaders::DETILE_M8X2_COMP, HostShaders::DETILE_M32X1_COMP, HostShaders::DETILE_M32X2_COMP, @@ -342,12 +344,6 @@ TileManager::ScratchBuffer TileManager::AllocBuffer(u32 size, bool is_storage /* .usage = usage, }; -#ifdef __APPLE__ - // Fix for detiler artifacts on macOS - const bool is_large_buffer = true; -#else - const bool is_large_buffer = size > 128_MB; -#endif VmaAllocationCreateInfo alloc_info{ .flags = !is_storage ? VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT @@ -462,7 +458,6 @@ std::optional TileManager::TryDetile(Image& image) { (m > 0 ? params.sizes[m - 1] : 0); } - auto pitch = image.info.pitch; cmdbuf.pushConstants(*detiler->pl_layout, vk::ShaderStageFlagBits::eCompute, 0u, sizeof(params), ¶ms);