├── .editorconfig ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ └── config.yml └── workflows │ ├── validate-external.yml │ ├── validate-internal.yml │ └── validate.yml ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── CMakePresets.json ├── COPYING ├── README.md ├── UnleashedRecomp ├── .gitignore ├── CMakeLists.txt ├── app.cpp ├── app.h ├── apu │ ├── audio.cpp │ ├── audio.h │ ├── driver │ │ └── sdl2_driver.cpp │ ├── embedded_player.cpp │ └── embedded_player.h ├── cpu │ ├── guest_stack_var.h │ ├── guest_thread.cpp │ ├── guest_thread.h │ └── ppc_context.h ├── decompressor.h ├── exports.cpp ├── exports.h ├── framework.h ├── gpu │ ├── cache │ │ ├── pipeline_state_cache.h │ │ ├── vertex_declaration_cache.h │ │ └── vertex_element_cache.h │ ├── imgui │ │ ├── imgui_common.cpp │ │ ├── imgui_common.h │ │ ├── imgui_font_builder.cpp │ │ ├── imgui_font_builder.h │ │ ├── imgui_snapshot.cpp │ │ └── imgui_snapshot.h │ ├── rhi │ │ ├── LICENSE │ │ ├── plume_d3d12.cpp │ │ ├── plume_d3d12.h │ │ ├── plume_render_interface.h │ │ ├── plume_render_interface_builders.h │ │ ├── plume_render_interface_types.h │ │ ├── plume_vulkan.cpp │ │ └── plume_vulkan.h │ ├── shader │ │ ├── .gitignore │ │ ├── blend_color_alpha_ps.hlsl │ │ ├── copy_color_ps.hlsl │ │ ├── copy_common.hlsli │ │ ├── copy_depth_ps.hlsl │ │ ├── copy_vs.hlsl │ │ ├── csd_filter_ps.hlsl │ │ ├── csd_no_tex_vs.hlsl │ │ ├── csd_vs.hlsl │ │ ├── enhanced_motion_blur_ps.hlsl │ │ ├── gamma_correction_ps.hlsl │ │ ├── gaussian_blur.hlsli │ │ ├── gaussian_blur_3x3.hlsl │ │ ├── gaussian_blur_5x5.hlsl │ │ ├── gaussian_blur_7x7.hlsl │ │ ├── gaussian_blur_9x9.hlsl │ │ ├── imgui_common.hlsli │ │ ├── imgui_ps.hlsl │ │ ├── imgui_vs.hlsl │ │ ├── movie_common.hlsli │ │ ├── movie_ps.hlsl │ │ ├── movie_vs.hlsl │ │ ├── resolve_msaa_color.hlsli │ │ ├── resolve_msaa_color_2x.hlsl │ │ ├── resolve_msaa_color_4x.hlsl │ │ ├── resolve_msaa_color_8x.hlsl │ │ ├── resolve_msaa_depth.hlsli │ │ ├── resolve_msaa_depth_2x.hlsl │ │ ├── resolve_msaa_depth_4x.hlsl │ │ └── resolve_msaa_depth_8x.hlsl │ ├── video.cpp │ └── video.h ├── hid │ ├── driver │ │ └── sdl_hid.cpp │ ├── hid.cpp │ └── hid.h ├── install │ ├── directory_file_system.h │ ├── hashes │ │ ├── apotos_shamar.cpp │ │ ├── apotos_shamar.h │ │ ├── chunnan.cpp │ │ ├── chunnan.h │ │ ├── empire_city_adabat.cpp │ │ ├── empire_city_adabat.h │ │ ├── game.cpp │ │ ├── game.h │ │ ├── holoska.cpp │ │ ├── holoska.h │ │ ├── mazuri.cpp │ │ ├── mazuri.h │ │ ├── spagonia.cpp │ │ ├── spagonia.h │ │ ├── update.cpp │ │ └── update.h │ ├── installer.cpp │ ├── installer.h │ ├── iso_file_system.cpp │ ├── iso_file_system.h │ ├── update_checker.cpp │ ├── update_checker.h │ ├── virtual_file_system.h │ ├── xcontent_file_system.cpp │ └── xcontent_file_system.h ├── kernel │ ├── freelist.h │ ├── function.h │ ├── heap.cpp │ ├── heap.h │ ├── imports.cpp │ ├── io │ │ ├── file_system.cpp │ │ └── file_system.h │ ├── memory.cpp │ ├── memory.h │ ├── xam.cpp │ ├── xam.h │ ├── xdbf.h │ ├── xdm.cpp │ └── xdm.h ├── locale │ ├── config_locale.cpp │ ├── locale.cpp │ └── locale.h ├── main.cpp ├── misc_impl.cpp ├── mod │ ├── ini_file.h │ ├── ini_file.inl │ ├── mod_loader.cpp │ └── mod_loader.h ├── mutex.h ├── natvis.natvis ├── os │ ├── .gitignore │ ├── linux │ │ ├── logger_linux.cpp │ │ ├── media_linux.cpp │ │ ├── process_linux.cpp │ │ ├── registry_linux.inl │ │ ├── user_linux.cpp │ │ └── version_linux.cpp │ ├── logger.h │ ├── media.h │ ├── process.h │ ├── registry.h │ ├── user.h │ ├── version.h │ └── win32 │ │ ├── logger_win32.cpp │ │ ├── media_win32.cpp │ │ ├── process_win32.cpp │ │ ├── registry_win32.inl │ │ ├── user_win32.cpp │ │ └── version_win32.cpp ├── patches │ ├── CGameModeStageTitle_patches.cpp │ ├── CHudPause_patches.cpp │ ├── CTitleStateIntro_patches.cpp │ ├── CTitleStateIntro_patches.h │ ├── CTitleStateMenu_patches.cpp │ ├── aspect_ratio_patches.cpp │ ├── aspect_ratio_patches.h │ ├── audio_patches.cpp │ ├── audio_patches.h │ ├── camera_patches.cpp │ ├── camera_patches.h │ ├── fps_patches.cpp │ ├── frontend_listener.cpp │ ├── input_patches.cpp │ ├── inspire_patches.cpp │ ├── inspire_patches.h │ ├── misc_patches.cpp │ ├── object_patches.cpp │ ├── player_patches.cpp │ ├── resident_patches.cpp │ └── video_patches.cpp ├── preload_executable.cpp ├── preload_executable.h ├── res │ ├── .gitignore │ ├── credits.h │ ├── version.cpp.template │ ├── version.h.template │ ├── version.txt │ └── win32 │ │ └── res.rc.template ├── sdl_events.h ├── sdl_listener.cpp ├── sdl_listener.h ├── stdafx.cpp ├── stdafx.h ├── ui │ ├── achievement_menu.cpp │ ├── achievement_menu.h │ ├── achievement_overlay.cpp │ ├── achievement_overlay.h │ ├── black_bar.cpp │ ├── black_bar.h │ ├── button_guide.cpp │ ├── button_guide.h │ ├── fader.cpp │ ├── fader.h │ ├── game_window.cpp │ ├── game_window.h │ ├── imgui_utils.cpp │ ├── imgui_utils.h │ ├── installer_wizard.cpp │ ├── installer_wizard.h │ ├── message_window.cpp │ ├── message_window.h │ ├── options_menu.cpp │ ├── options_menu.h │ ├── options_menu_thumbnails.cpp │ ├── options_menu_thumbnails.h │ ├── tv_static.cpp │ └── tv_static.h ├── user │ ├── achievement_data.cpp │ ├── achievement_data.h │ ├── achievement_manager.cpp │ ├── achievement_manager.h │ ├── config.cpp │ ├── config.h │ ├── config_def.h │ ├── paths.cpp │ ├── paths.h │ ├── persistent_data.cpp │ ├── persistent_data.h │ ├── persistent_storage_manager.cpp │ ├── persistent_storage_manager.h │ ├── registry.cpp │ └── registry.h ├── version.cmake └── xxHashMap.h ├── UnleashedRecompLib ├── CMakeLists.txt ├── config │ ├── SWA.toml │ └── SWA_switch_tables.toml ├── ppc │ └── .gitignore ├── private │ └── .gitignore └── shader │ ├── .gitignore │ └── shader_cache.h ├── docs ├── BUILDING.md ├── DUMPING-USB-en.md └── DUMPING-en.md ├── flatpak ├── README.md ├── io.github.hedge_dev.unleashedrecomp.desktop ├── io.github.hedge_dev.unleashedrecomp.json └── io.github.hedge_dev.unleashedrecomp.metainfo.xml ├── thirdparty ├── .gitignore ├── CMakeLists.txt └── o1heap │ ├── CMakeLists.txt │ ├── o1heap.c │ └── o1heap.h ├── toolchains └── linux-clang.cmake ├── tools ├── CMakeLists.txt ├── bc_diff │ ├── CMakeLists.txt │ ├── bc_diff.cpp │ └── bc_diff.h ├── file_to_c │ ├── CMakeLists.txt │ └── file_to_c.cpp ├── fshasher │ ├── CMakeLists.txt │ ├── fshasher.cpp │ └── plainargs.h └── x_decompress │ ├── CMakeLists.txt │ └── x_decompress.cpp ├── update_submodules.bat └── vcpkg.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | indent_style = space 8 | indent_size = 4 9 | insert_final_newline = true 10 | end_of_line = lf 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Report a bug, crash, or other type of error in Unleashed Recompiled 3 | type: Bug 4 | body: 5 | - id: validation 6 | type: checkboxes 7 | attributes: 8 | label: Validation 9 | options: 10 | - label: I have checked the [Issues](https://github.com/hedge-dev/UnleashedRecomp/issues) page to see if my problem has already been reported 11 | required: true 12 | - label: I have confirmed that this bug does not occur in the original game running on original Xbox 360 hardware 13 | required: false 14 | - id: dlc-installed 15 | type: checkboxes 16 | attributes: 17 | label: If you have DLC installed, please specify which ones you have. 18 | options: 19 | - label: Apotos & Shamar Adventure Pack 20 | required: false 21 | - label: Chun-nan Adventure Pack 22 | required: false 23 | - label: Empire City & Adabat Adventure Pack 24 | required: false 25 | - label: Holoska Adventure Pack 26 | required: false 27 | - label: Mazuri Adventure Pack 28 | required: false 29 | - label: Spagonia Adventure Pack 30 | required: false 31 | - id: mods-used 32 | type: textarea 33 | validations: 34 | required: false 35 | attributes: 36 | label: If you have mods enabled, please specify which ones you have. 37 | description: Provide a list of your enabled mods in Hedge Mod Manager here. You will not receive support for issues *caused* by mods. 38 | - id: codes-used 39 | type: textarea 40 | validations: 41 | required: false 42 | attributes: 43 | label: If you have codes enabled, please specify which ones you have. 44 | description: Provide a list of your enabled codes in Hedge Mod Manager here. 45 | - id: describe-bug 46 | type: textarea 47 | validations: 48 | required: true 49 | attributes: 50 | label: Describe the Bug 51 | description: A clear and concise description of what the bug is. 52 | - id: repro-steps 53 | type: textarea 54 | validations: 55 | required: true 56 | attributes: 57 | label: Steps to Reproduce 58 | description: Step-by-step instructions on how to reproduce the bug. 59 | placeholder: | 60 | 1. Go to '...' 61 | 2. etc. 62 | - id: expected-behavior 63 | type: textarea 64 | validations: 65 | required: true 66 | attributes: 67 | label: Expected Behavior 68 | description: A clear and concise description of what you expected to happen. 69 | - id: footage 70 | type: textarea 71 | validations: 72 | required: true 73 | attributes: 74 | label: Footage 75 | description: Attach a screenshot or video of the bug. If possible, please also provide footage of the expected behaviour on original Xbox 360 hardware. 76 | - id: specs-version 77 | type: input 78 | validations: 79 | required: true 80 | attributes: 81 | label: Version 82 | description: The version of Unleashed Recompiled you are running (e.g. **v1.0.0**). This can be found at the bottom right corner of the installer wizard, options menu, or in the application metadata. 83 | - id: specs-cpu 84 | type: input 85 | validations: 86 | required: true 87 | attributes: 88 | label: CPU 89 | description: The name of your CPU (e.g. **Intel Core [...]**, **AMD Ryzen [...]**, etc.). 90 | - id: specs-gpu 91 | type: input 92 | validations: 93 | required: true 94 | attributes: 95 | label: GPU 96 | description: The name of your GPU (e.g. **NVIDIA GeForce [...]**, **AMD Radeon [...]**, **Intel [...]**, etc.). 97 | - id: specs-gpu-driver 98 | type: input 99 | validations: 100 | required: true 101 | attributes: 102 | label: GPU Driver 103 | description: The version of your GPU driver (e.g. **NVIDIA Driver 572.XX**, **AMD Driver 25.X.X**, **Intel Driver 32.X.XXX.XXXX** etc.). 104 | - id: specs-ram 105 | type: input 106 | validations: 107 | required: true 108 | attributes: 109 | label: Memory 110 | description: The amount of system memory you have (e.g. **8 GB**, **16 GB**, etc.). 111 | - id: specs-os 112 | type: input 113 | validations: 114 | required: true 115 | attributes: 116 | label: Operating System 117 | description: The name of the operating system you are running (e.g. **Windows 10**, **Windows 11**, **Linux distro**). 118 | - id: additional-context 119 | type: textarea 120 | validations: 121 | required: false 122 | attributes: 123 | label: Additional Context 124 | description: Provide any other context about the problem here. 125 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true 2 | -------------------------------------------------------------------------------- /.github/workflows/validate-external.yml: -------------------------------------------------------------------------------- 1 | name: validate-external 2 | on: 3 | pull_request_target: 4 | types: [opened, synchronize] 5 | jobs: 6 | authorize: 7 | if: github.repository != github.event.pull_request.head.repo.full_name 8 | environment: 9 | ${{ github.event_name == 'pull_request_target' && 10 | github.event.pull_request.head.repo.full_name != github.repository && 11 | 'external' || 'internal' }} 12 | runs-on: ubuntu-24.04 13 | steps: 14 | - run: echo ✓ 15 | build: 16 | needs: authorize 17 | uses: ./.github/workflows/validate.yml 18 | secrets: 19 | ASSET_REPO: ${{ secrets.ASSET_REPO }} 20 | ASSET_REPO_TOKEN: ${{ secrets.ASSET_REPO_TOKEN }} 21 | -------------------------------------------------------------------------------- /.github/workflows/validate-internal.yml: -------------------------------------------------------------------------------- 1 | name: validate-internal 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: 7 | types: [opened, synchronize] 8 | jobs: 9 | build: 10 | if: github.event_name == 'push' || github.repository == github.event.pull_request.head.repo.full_name 11 | uses: ./.github/workflows/validate.yml 12 | secrets: inherit 13 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "tools/XenonRecomp"] 2 | path = tools/XenonRecomp 3 | url = https://github.com/hedge-dev/XenonRecomp.git 4 | [submodule "thirdparty/ddspp"] 5 | path = thirdparty/ddspp 6 | url = https://github.com/redorav/ddspp.git 7 | [submodule "tools/XenosRecomp"] 8 | path = tools/XenosRecomp 9 | url = https://github.com/hedge-dev/XenosRecomp.git 10 | [submodule "UnleashedRecompResources"] 11 | path = UnleashedRecompResources 12 | url = https://github.com/hedge-dev/UnleashedRecompResources.git 13 | [submodule "thirdparty/msdf-atlas-gen"] 14 | path = thirdparty/msdf-atlas-gen 15 | url = https://github.com/Chlumsky/msdf-atlas-gen.git 16 | [submodule "thirdparty/vcpkg"] 17 | path = thirdparty/vcpkg 18 | url = https://github.com/microsoft/vcpkg 19 | [submodule "thirdparty/volk"] 20 | path = thirdparty/volk 21 | url = https://github.com/zeux/volk 22 | [submodule "thirdparty/SDL"] 23 | path = thirdparty/SDL 24 | url = https://github.com/libsdl-org/SDL.git 25 | [submodule "thirdparty/Vulkan-Headers"] 26 | path = thirdparty/Vulkan-Headers 27 | url = https://github.com/KhronosGroup/Vulkan-Headers.git 28 | [submodule "thirdparty/VulkanMemoryAllocator"] 29 | path = thirdparty/VulkanMemoryAllocator 30 | url = https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git 31 | [submodule "thirdparty/D3D12MemoryAllocator"] 32 | path = thirdparty/D3D12MemoryAllocator 33 | url = https://github.com/GPUOpen-LibrariesAndSDKs/D3D12MemoryAllocator.git 34 | [submodule "thirdparty/stb"] 35 | path = thirdparty/stb 36 | url = https://github.com/nothings/stb.git 37 | [submodule "thirdparty/concurrentqueue"] 38 | path = thirdparty/concurrentqueue 39 | url = https://github.com/cameron314/concurrentqueue.git 40 | [submodule "thirdparty/magic_enum"] 41 | path = thirdparty/magic_enum 42 | url = https://github.com/Neargye/magic_enum.git 43 | [submodule "thirdparty/nativefiledialog-extended"] 44 | path = thirdparty/nativefiledialog-extended 45 | url = https://github.com/btzy/nativefiledialog-extended.git 46 | [submodule "thirdparty/imgui"] 47 | path = thirdparty/imgui 48 | url = https://github.com/ocornut/imgui.git 49 | [submodule "thirdparty/unordered_dense"] 50 | path = thirdparty/unordered_dense 51 | url = https://github.com/martinus/unordered_dense.git 52 | [submodule "thirdparty/SDL_mixer"] 53 | path = thirdparty/SDL_mixer 54 | url = https://github.com/libsdl-org/SDL_mixer 55 | [submodule "thirdparty/implot"] 56 | path = thirdparty/implot 57 | url = https://github.com/epezent/implot.git 58 | [submodule "thirdparty/json"] 59 | path = thirdparty/json 60 | url = https://github.com/nlohmann/json 61 | [submodule "UnleashedRecomp/api"] 62 | path = UnleashedRecomp/api 63 | url = https://github.com/hedge-dev/SWA.git 64 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.20) 2 | 3 | if(NOT DEFINED ENV{VCPKG_ROOT}) 4 | message(FATAL_ERROR "VCPKG_ROOT is not defined!") 5 | endif() 6 | 7 | include($ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake) 8 | set(UNLEASHED_RECOMP_THIRDPARTY_ROOT ${CMAKE_SOURCE_DIR}/thirdparty) 9 | set(UNLEASHED_RECOMP_TOOLS_ROOT ${CMAKE_SOURCE_DIR}/tools) 10 | set(CMAKE_CXX_STANDARD 20) 11 | set(BUILD_SHARED_LIBS OFF) 12 | 13 | # Enable Hot Reload for MSVC compilers if supported. 14 | if (POLICY CMP0141) 15 | cmake_policy(SET CMP0141 NEW) 16 | set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$,$>,$<$:EditAndContinue>,$<$:ProgramDatabase>>") 17 | endif() 18 | 19 | set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") 20 | 21 | # Target Sandy Bridge for all projects 22 | add_compile_options( 23 | -march=sandybridge 24 | ) 25 | 26 | add_subdirectory(${UNLEASHED_RECOMP_THIRDPARTY_ROOT}) 27 | add_subdirectory(${UNLEASHED_RECOMP_TOOLS_ROOT}) 28 | 29 | project("UnleashedRecomp-ALL") 30 | 31 | # Include sub-projects. 32 | add_subdirectory("UnleashedRecompLib") 33 | add_subdirectory("UnleashedRecomp") 34 | -------------------------------------------------------------------------------- /CMakePresets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 8, 3 | "configurePresets": [ 4 | { 5 | "name": "windows-base", 6 | "hidden": true, 7 | "generator": "Ninja", 8 | "binaryDir": "${sourceDir}/out/build/${presetName}", 9 | "installDir": "${sourceDir}/out/install/${presetName}", 10 | "cacheVariables": { 11 | "CMAKE_C_COMPILER": "clang-cl.exe", 12 | "CMAKE_CXX_COMPILER": "clang-cl.exe", 13 | "CMAKE_LINKER": "lld-link.exe", 14 | "CMAKE_TOOLCHAIN_FILE": { 15 | "value": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", 16 | "type": "FILEPATH" 17 | } 18 | }, 19 | "environment": { 20 | "VCPKG_ROOT": "${sourceDir}/thirdparty/vcpkg" 21 | }, 22 | "condition": { 23 | "type": "equals", 24 | "lhs": "${hostSystemName}", 25 | "rhs": "Windows" 26 | } 27 | }, 28 | { 29 | "name": "x64-Clang-Debug", 30 | "displayName": "Debug", 31 | "inherits": "windows-base", 32 | "architecture": { 33 | "value": "x64", 34 | "strategy": "external" 35 | }, 36 | "cacheVariables": { 37 | "CMAKE_BUILD_TYPE": "Debug", 38 | "VCPKG_TARGET_TRIPLET": { 39 | "value": "x64-windows-static", 40 | "type": "STRING" 41 | } 42 | } 43 | }, 44 | { 45 | "name": "x64-Clang-RelWithDebInfo", 46 | "displayName": "RelWithDebInfo", 47 | "inherits": "x64-Clang-Debug", 48 | "cacheVariables": { 49 | "CMAKE_BUILD_TYPE": "RelWithDebInfo" 50 | } 51 | }, 52 | { 53 | "name": "x64-Clang-Release", 54 | "displayName": "Release", 55 | "inherits": "x64-Clang-Debug", 56 | "cacheVariables": { 57 | "CMAKE_BUILD_TYPE": "Release", 58 | "CMAKE_INTERPROCEDURAL_OPTIMIZATION": true 59 | } 60 | }, 61 | { 62 | "name": "linux-base", 63 | "hidden": true, 64 | "generator": "Ninja", 65 | "binaryDir": "${sourceDir}/out/build/${presetName}", 66 | "installDir": "${sourceDir}/out/install/${presetName}", 67 | "cacheVariables": { 68 | "CMAKE_TOOLCHAIN_FILE": { 69 | "value": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", 70 | "type": "FILEPATH" 71 | }, 72 | "VCPKG_TARGET_TRIPLET": { 73 | "value": "x64-linux", 74 | "type": "STRING" 75 | }, 76 | "VCPKG_CHAINLOAD_TOOLCHAIN_FILE": "${sourceDir}/toolchains/linux-clang.cmake" 77 | }, 78 | "environment": { 79 | "VCPKG_ROOT": "${sourceDir}/thirdparty/vcpkg" 80 | }, 81 | "condition": { 82 | "type": "equals", 83 | "lhs": "${hostSystemName}", 84 | "rhs": "Linux" 85 | }, 86 | "vendor": { 87 | "microsoft.com/VisualStudioRemoteSettings/CMake/2.0": { 88 | "remoteSourceRootDir": "$env{HOME}/.vs/$ms{projectDirName}" 89 | } 90 | } 91 | }, 92 | { 93 | "name": "linux-debug", 94 | "displayName": "Linux-Debug", 95 | "inherits": "linux-base", 96 | "cacheVariables": { 97 | "CMAKE_BUILD_TYPE": "Debug" 98 | } 99 | }, 100 | { 101 | "name": "linux-relwithdebinfo", 102 | "displayName": "Linux-RelWithDebInfo", 103 | "inherits": "linux-base", 104 | "cacheVariables": { 105 | "CMAKE_BUILD_TYPE": "RelWithDebInfo" 106 | } 107 | }, 108 | { 109 | "name": "linux-release", 110 | "displayName": "Linux-Release", 111 | "inherits": "linux-base", 112 | "cacheVariables": { 113 | "CMAKE_BUILD_TYPE": "Release", 114 | "CMAKE_INTERPROCEDURAL_OPTIMIZATION": true 115 | } 116 | } 117 | ] 118 | } 119 | -------------------------------------------------------------------------------- /UnleashedRecomp/.gitignore: -------------------------------------------------------------------------------- 1 | /version.h 2 | /version.cpp -------------------------------------------------------------------------------- /UnleashedRecomp/app.cpp: -------------------------------------------------------------------------------- 1 | #include "app.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | void App::Restart(std::vector restartArgs) 15 | { 16 | os::process::StartProcess(os::process::GetExecutablePath(), restartArgs, os::process::GetWorkingDirectory()); 17 | Exit(); 18 | } 19 | 20 | void App::Exit() 21 | { 22 | Config::Save(); 23 | 24 | #ifdef _WIN32 25 | timeEndPeriod(1); 26 | #endif 27 | 28 | std::_Exit(0); 29 | } 30 | 31 | // SWA::CApplication::CApplication 32 | PPC_FUNC_IMPL(__imp__sub_824EB490); 33 | PPC_FUNC(sub_824EB490) 34 | { 35 | App::s_isInit = true; 36 | App::s_isMissingDLC = !Installer::checkAllDLC(GetGamePath()); 37 | App::s_language = Config::Language; 38 | 39 | SWA::SGlobals::Init(); 40 | Registry::Save(); 41 | 42 | __imp__sub_824EB490(ctx, base); 43 | } 44 | 45 | static std::thread::id g_mainThreadId = std::this_thread::get_id(); 46 | 47 | // SWA::CApplication::Update 48 | PPC_FUNC_IMPL(__imp__sub_822C1130); 49 | PPC_FUNC(sub_822C1130) 50 | { 51 | Video::WaitOnSwapChain(); 52 | 53 | // Correct small delta time errors. 54 | if (Config::FPS >= FPS_MIN && Config::FPS < FPS_MAX) 55 | { 56 | double targetDeltaTime = 1.0 / Config::FPS; 57 | 58 | if (abs(ctx.f1.f64 - targetDeltaTime) < 0.00001) 59 | ctx.f1.f64 = targetDeltaTime; 60 | } 61 | 62 | App::s_deltaTime = ctx.f1.f64; 63 | App::s_time += App::s_deltaTime; 64 | 65 | // This function can also be called by the loading thread, 66 | // which SDL does not like. To prevent the OS from thinking 67 | // the process is unresponsive, we will flush while waiting 68 | // for the pipelines to finish compiling in video.cpp. 69 | if (std::this_thread::get_id() == g_mainThreadId) 70 | { 71 | SDL_PumpEvents(); 72 | SDL_FlushEvents(SDL_FIRSTEVENT, SDL_LASTEVENT); 73 | GameWindow::Update(); 74 | } 75 | 76 | AudioPatches::Update(App::s_deltaTime); 77 | InspirePatches::Update(); 78 | 79 | // Apply subtitles option. 80 | if (auto pApplicationDocument = SWA::CApplicationDocument::GetInstance()) 81 | pApplicationDocument->m_InspireSubtitles = Config::Subtitles; 82 | 83 | if (Config::EnableEventCollisionDebugView) 84 | *SWA::SGlobals::ms_IsTriggerRender = true; 85 | 86 | if (Config::EnableGIMipLevelDebugView) 87 | *SWA::SGlobals::ms_VisualizeLoadedLevel = true; 88 | 89 | if (Config::EnableObjectCollisionDebugView) 90 | *SWA::SGlobals::ms_IsObjectCollisionRender = true; 91 | 92 | if (Config::EnableStageCollisionDebugView) 93 | *SWA::SGlobals::ms_IsCollisionRender = true; 94 | 95 | __imp__sub_822C1130(ctx, base); 96 | } 97 | 98 | -------------------------------------------------------------------------------- /UnleashedRecomp/app.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class App 6 | { 7 | public: 8 | static inline bool s_isInit; 9 | static inline bool s_isMissingDLC; 10 | static inline bool s_isLoading; 11 | static inline bool s_isSaving; 12 | static inline bool s_isWerehog; 13 | static inline bool s_isSaveDataCorrupt; 14 | 15 | static inline ELanguage s_language; 16 | 17 | static inline double s_deltaTime; 18 | static inline double s_time = 0.0; // How much time elapsed since the game started. 19 | 20 | static void Restart(std::vector restartArgs = {}); 21 | static void Exit(); 22 | }; 23 | 24 | -------------------------------------------------------------------------------- /UnleashedRecomp/apu/audio.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include "audio.h" 6 | #include 7 | 8 | #define AUDIO_DRIVER_KEY (uint32_t)('DAUD') 9 | 10 | // Use to dump raw audio captures to the game folder. 11 | //#define AUDIO_DUMP_SAMPLES_PATH "audio.pcm" 12 | 13 | #ifdef AUDIO_DUMP_SAMPLES_PATH 14 | std::ofstream g_audioDumpStream; 15 | #endif 16 | 17 | uint32_t XAudioRegisterRenderDriverClient(be* callback, be* driver) 18 | { 19 | #ifdef AUDIO_DUMP_SAMPLES_PATH 20 | g_audioDumpStream.open(AUDIO_DUMP_SAMPLES_PATH, std::ios::binary); 21 | #endif 22 | 23 | *driver = AUDIO_DRIVER_KEY; 24 | XAudioRegisterClient(g_memory.FindFunction(*callback), callback[1]); 25 | return 0; 26 | } 27 | 28 | uint32_t XAudioUnregisterRenderDriverClient(uint32_t driver) 29 | { 30 | return 0; 31 | } 32 | 33 | uint32_t XAudioSubmitRenderDriverFrame(uint32_t driver, void* samples) 34 | { 35 | #ifdef AUDIO_DUMP_SAMPLES_PATH 36 | static uint32_t xaudioSamplesBuffer[XAUDIO_NUM_SAMPLES * XAUDIO_NUM_CHANNELS]; 37 | for (size_t i = 0; i < XAUDIO_NUM_SAMPLES; i++) 38 | { 39 | for (size_t j = 0; j < XAUDIO_NUM_CHANNELS; j++) 40 | { 41 | xaudioSamplesBuffer[i * XAUDIO_NUM_CHANNELS + j] = ByteSwap(((uint32_t *)samples)[j * XAUDIO_NUM_SAMPLES + i]); 42 | } 43 | } 44 | 45 | g_audioDumpStream.write((const char *)(xaudioSamplesBuffer), sizeof(xaudioSamplesBuffer)); 46 | #endif 47 | 48 | XAudioSubmitFrame(samples); 49 | return 0; 50 | } 51 | -------------------------------------------------------------------------------- /UnleashedRecomp/apu/audio.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define XAUDIO_SAMPLES_HZ 48000 4 | #define XAUDIO_NUM_CHANNELS 6 5 | #define XAUDIO_SAMPLE_BITS 32 6 | 7 | // Number of samples in a frame 8 | #define XAUDIO_NUM_SAMPLES 256 9 | 10 | void XAudioInitializeSystem(); 11 | void XAudioRegisterClient(PPCFunc* callback, uint32_t param); 12 | void XAudioSubmitFrame(void* samples); 13 | 14 | uint32_t XAudioRegisterRenderDriverClient(be* callback, be* driver); 15 | uint32_t XAudioUnregisterRenderDriverClient(uint32_t driver); 16 | uint32_t XAudioSubmitRenderDriverFrame(uint32_t driver, void* samples); 17 | -------------------------------------------------------------------------------- /UnleashedRecomp/apu/embedded_player.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | enum class EmbeddedSound 15 | { 16 | SysWorldMapCursor, 17 | SysWorldMapFinalDecide, 18 | SysActStgPauseCansel, 19 | SysActStgPauseCursor, 20 | SysActStgPauseDecide, 21 | SysActStgPauseWinClose, 22 | SysActStgPauseWinOpen, 23 | Count, 24 | }; 25 | 26 | struct EmbeddedSoundData 27 | { 28 | Mix_Chunk* chunk{}; 29 | }; 30 | 31 | static std::array g_embeddedSoundData = {}; 32 | static const std::unordered_map g_embeddedSoundMap = 33 | { 34 | { "sys_worldmap_cursor", EmbeddedSound::SysWorldMapCursor }, 35 | { "sys_worldmap_finaldecide", EmbeddedSound::SysWorldMapFinalDecide }, 36 | { "sys_actstg_pausecansel", EmbeddedSound::SysActStgPauseCansel }, 37 | { "sys_actstg_pausecursor", EmbeddedSound::SysActStgPauseCursor }, 38 | { "sys_actstg_pausedecide", EmbeddedSound::SysActStgPauseDecide }, 39 | { "sys_actstg_pausewinclose", EmbeddedSound::SysActStgPauseWinClose }, 40 | { "sys_actstg_pausewinopen", EmbeddedSound::SysActStgPauseWinOpen }, 41 | }; 42 | 43 | static size_t g_channelIndex; 44 | 45 | static void PlayEmbeddedSound(EmbeddedSound s) 46 | { 47 | EmbeddedSoundData &data = g_embeddedSoundData[size_t(s)]; 48 | if (data.chunk == nullptr) 49 | { 50 | // The sound hasn't been created yet, create it and pick it. 51 | const void *soundData = nullptr; 52 | size_t soundDataSize = 0; 53 | switch (s) 54 | { 55 | case EmbeddedSound::SysWorldMapCursor: 56 | soundData = g_sys_worldmap_cursor; 57 | soundDataSize = sizeof(g_sys_worldmap_cursor); 58 | break; 59 | case EmbeddedSound::SysWorldMapFinalDecide: 60 | soundData = g_sys_worldmap_finaldecide; 61 | soundDataSize = sizeof(g_sys_worldmap_finaldecide); 62 | break; 63 | case EmbeddedSound::SysActStgPauseCansel: 64 | soundData = g_sys_actstg_pausecansel; 65 | soundDataSize = sizeof(g_sys_actstg_pausecansel); 66 | break; 67 | case EmbeddedSound::SysActStgPauseCursor: 68 | soundData = g_sys_actstg_pausecursor; 69 | soundDataSize = sizeof(g_sys_actstg_pausecursor); 70 | break; 71 | case EmbeddedSound::SysActStgPauseDecide: 72 | soundData = g_sys_actstg_pausedecide; 73 | soundDataSize = sizeof(g_sys_actstg_pausedecide); 74 | break; 75 | case EmbeddedSound::SysActStgPauseWinClose: 76 | soundData = g_sys_actstg_pausewinclose; 77 | soundDataSize = sizeof(g_sys_actstg_pausewinclose); 78 | break; 79 | case EmbeddedSound::SysActStgPauseWinOpen: 80 | soundData = g_sys_actstg_pausewinopen; 81 | soundDataSize = sizeof(g_sys_actstg_pausewinopen); 82 | break; 83 | default: 84 | assert(false && "Unknown embedded sound."); 85 | return; 86 | } 87 | 88 | data.chunk = Mix_LoadWAV_RW(SDL_RWFromConstMem(soundData, soundDataSize), 1); 89 | } 90 | 91 | Mix_VolumeChunk(data.chunk, Config::MasterVolume * Config::EffectsVolume * MIX_MAX_VOLUME); 92 | Mix_PlayChannel(g_channelIndex % MIX_CHANNELS, data.chunk, 0); 93 | ++g_channelIndex; 94 | } 95 | 96 | static Mix_Music* g_installerMusic; 97 | 98 | void EmbeddedPlayer::Init() 99 | { 100 | Mix_OpenAudio(XAUDIO_SAMPLES_HZ, AUDIO_F32SYS, 2, 4096); 101 | g_installerMusic = Mix_LoadMUS_RW(SDL_RWFromConstMem(g_installer_music, sizeof(g_installer_music)), 1); 102 | 103 | s_isActive = true; 104 | } 105 | 106 | void EmbeddedPlayer::Play(const char *name) 107 | { 108 | assert(s_isActive && "Playback shouldn't be requested if the Embedded Player isn't active."); 109 | 110 | auto it = g_embeddedSoundMap.find(name); 111 | if (it == g_embeddedSoundMap.end()) 112 | { 113 | return; 114 | } 115 | 116 | PlayEmbeddedSound(it->second); 117 | } 118 | 119 | void EmbeddedPlayer::PlayMusic() 120 | { 121 | if (!Mix_PlayingMusic()) 122 | { 123 | Mix_PlayMusic(g_installerMusic, INT_MAX); 124 | Mix_VolumeMusic(Config::MasterVolume * Config::MusicVolume * MUSIC_VOLUME * MIX_MAX_VOLUME); 125 | } 126 | } 127 | 128 | void EmbeddedPlayer::FadeOutMusic() 129 | { 130 | if (Mix_PlayingMusic()) 131 | Mix_FadeOutMusic(1000); 132 | } 133 | 134 | void EmbeddedPlayer::Shutdown() 135 | { 136 | for (EmbeddedSoundData &data : g_embeddedSoundData) 137 | { 138 | if (data.chunk != nullptr) 139 | Mix_FreeChunk(data.chunk); 140 | } 141 | 142 | Mix_HaltMusic(); 143 | Mix_FreeMusic(g_installerMusic); 144 | 145 | Mix_CloseAudio(); 146 | Mix_Quit(); 147 | 148 | s_isActive = false; 149 | } 150 | -------------------------------------------------------------------------------- /UnleashedRecomp/apu/embedded_player.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct EmbeddedPlayer 4 | { 5 | // Arbitrarily picked volume to match the mixing in the original game. 6 | static constexpr float MUSIC_VOLUME = 0.25f; 7 | 8 | static inline bool s_isActive = false; 9 | 10 | static void Init(); 11 | static void Play(const char *name); 12 | static void PlayMusic(); 13 | static void FadeOutMusic(); 14 | static void Shutdown(); 15 | }; 16 | -------------------------------------------------------------------------------- /UnleashedRecomp/cpu/guest_stack_var.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ppc_context.h" 4 | #include 5 | 6 | // DO NOT use this type as anything other than a local variable. 7 | // This includes returning. It'll cause memory to leak in the guest stack! 8 | 9 | template 10 | class guest_stack_var 11 | { 12 | private: 13 | uint32_t m_ptr = NULL; 14 | uint32_t m_oldStackPtr = NULL; 15 | 16 | void AllocGuestStackMemory() 17 | { 18 | auto ctx = GetPPCContext(); 19 | m_oldStackPtr = ctx->r1.u32; 20 | m_ptr = (ctx->r1.u32 - sizeof(T)) & ~(std::max(alignof(T), 8) - 1); 21 | ctx->r1.u32 = m_ptr; 22 | } 23 | 24 | public: 25 | T* get() 26 | { 27 | return reinterpret_cast(g_memory.Translate(m_ptr)); 28 | } 29 | 30 | const T* get() const 31 | { 32 | return reinterpret_cast(g_memory.Translate(m_ptr)); 33 | } 34 | 35 | template 36 | guest_stack_var(Args&&... args) 37 | { 38 | AllocGuestStackMemory(); 39 | 40 | if (Init) 41 | new (get()) T(std::forward(args)...); 42 | } 43 | 44 | guest_stack_var(const guest_stack_var& other) 45 | { 46 | AllocGuestStackMemory(); 47 | 48 | if (Init) 49 | new (get()) T(*other->get()); 50 | } 51 | 52 | guest_stack_var(guest_stack_var&& other) 53 | { 54 | AllocGuestStackMemory(); 55 | 56 | if (Init) 57 | new (get()) T(std::move(*other->get())); 58 | } 59 | 60 | ~guest_stack_var() 61 | { 62 | get()->~T(); 63 | 64 | auto ctx = GetPPCContext(); 65 | // This assert will fail if the type was used as anything other than a local variable. 66 | assert(ctx->r1.u32 == m_ptr); 67 | ctx->r1.u32 = m_oldStackPtr; 68 | } 69 | 70 | void operator=(const guest_stack_var& other) 71 | { 72 | if (this != &other) 73 | *get() = *other->get(); 74 | } 75 | 76 | void operator=(guest_stack_var&& other) 77 | { 78 | if (this != &other) 79 | *get() = std::move(*other->get()); 80 | } 81 | 82 | void operator=(const T& other) 83 | { 84 | if (get() != &other) 85 | *get() = *other; 86 | } 87 | 88 | void operator=(T&& other) 89 | { 90 | if (get() != &other) 91 | *get() = std::move(*other); 92 | } 93 | 94 | operator const T* () const 95 | { 96 | return get(); 97 | } 98 | 99 | operator T* () 100 | { 101 | return get(); 102 | } 103 | 104 | const T* operator->() const 105 | { 106 | return get(); 107 | } 108 | 109 | T* operator->() 110 | { 111 | return get(); 112 | } 113 | 114 | const T& operator*() const 115 | { 116 | return *get(); 117 | } 118 | 119 | T& operator*() 120 | { 121 | return *get(); 122 | } 123 | }; 124 | -------------------------------------------------------------------------------- /UnleashedRecomp/cpu/guest_thread.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define CURRENT_THREAD_HANDLE uint32_t(-2) 6 | 7 | struct GuestThreadContext 8 | { 9 | PPCContext ppcContext{}; 10 | uint8_t* thread = nullptr; 11 | 12 | GuestThreadContext(uint32_t cpuNumber); 13 | ~GuestThreadContext(); 14 | }; 15 | 16 | struct GuestThreadParams 17 | { 18 | uint32_t function; 19 | uint32_t value; 20 | uint32_t flags; 21 | }; 22 | 23 | struct GuestThreadHandle : KernelObject 24 | { 25 | GuestThreadParams params; 26 | std::atomic suspended; 27 | std::thread thread; 28 | 29 | GuestThreadHandle(const GuestThreadParams& params); 30 | ~GuestThreadHandle() override; 31 | 32 | uint32_t Wait(uint32_t timeout) override; 33 | }; 34 | 35 | struct GuestThread 36 | { 37 | static uint32_t Start(const GuestThreadParams& params); 38 | static GuestThreadHandle* Start(const GuestThreadParams& params, uint32_t* threadId); 39 | 40 | static uint32_t GetCurrentThreadId(); 41 | static void SetLastError(uint32_t error); 42 | 43 | #ifdef _WIN32 44 | static void SetThreadName(uint32_t threadId, const char* name); 45 | #endif 46 | }; 47 | -------------------------------------------------------------------------------- /UnleashedRecomp/cpu/ppc_context.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | inline thread_local PPCContext* g_ppcContext; 4 | 5 | inline PPCContext* GetPPCContext() 6 | { 7 | return g_ppcContext; 8 | } 9 | 10 | inline void SetPPCContext(PPCContext& ctx) 11 | { 12 | g_ppcContext = &ctx; 13 | } 14 | -------------------------------------------------------------------------------- /UnleashedRecomp/decompressor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | template 4 | inline std::unique_ptr decompressZstd(const uint8_t(&data)[N], size_t decompressedSize) 5 | { 6 | auto decompressedData = std::make_unique(decompressedSize); 7 | ZSTD_decompress(decompressedData.get(), decompressedSize, data, N); 8 | return decompressedData; 9 | } 10 | -------------------------------------------------------------------------------- /UnleashedRecomp/exports.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | void Game_PlaySound(const char* pName) 11 | { 12 | if (EmbeddedPlayer::s_isActive) 13 | { 14 | EmbeddedPlayer::Play(pName); 15 | } 16 | else 17 | { 18 | // Use EVENT category in cutscenes since SYSTEM gets muted by the game. 19 | uint32_t category = !InspirePatches::s_sceneName.empty() ? 10 : 7; 20 | 21 | guest_stack_var soundPlayer; 22 | GuestToHostFunction(sub_82B4DF50, soundPlayer.get(), ((be*)g_memory.Translate(0x83367900))->get(), category, 0, 0); 23 | 24 | auto soundPlayerVtable = (be*)g_memory.Translate(*(be*)soundPlayer->get()); 25 | uint32_t virtualFunction = *(soundPlayerVtable + 1); 26 | 27 | size_t strLen = strlen(pName); 28 | void *strAllocation = g_userHeap.Alloc(strLen + 1); 29 | memcpy(strAllocation, pName, strLen + 1); 30 | GuestToHostFunction(virtualFunction, soundPlayer->get(), strAllocation, 0); 31 | g_userHeap.Free(strAllocation); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /UnleashedRecomp/exports.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void Game_PlaySound(const char* pName); 4 | -------------------------------------------------------------------------------- /UnleashedRecomp/framework.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define PROC_ADDRESS(libraryName, procName) \ 4 | GetProcAddress(LoadLibrary(TEXT(libraryName)), procName) 5 | 6 | #define LIB_FUNCTION(returnType, libraryName, procName, ...) \ 7 | typedef returnType _##procName(__VA_ARGS__); \ 8 | _##procName* procName = (_##procName*)PROC_ADDRESS(libraryName, #procName); 9 | 10 | #define STR(x) #x 11 | 12 | template 13 | inline T RoundUp(const T& in_rValue, uint32_t in_round) 14 | { 15 | return (in_rValue + in_round - 1) & ~(in_round - 1); 16 | } 17 | 18 | template 19 | inline T RoundDown(const T& in_rValue, uint32_t in_round) 20 | { 21 | return in_rValue & ~(in_round - 1); 22 | } 23 | 24 | inline size_t StringHash(const std::string_view& str) 25 | { 26 | return XXH3_64bits(str.data(), str.size()); 27 | } 28 | 29 | template 30 | constexpr size_t FirstBitLow(TValue value) 31 | { 32 | constexpr size_t nbits = sizeof(TValue) * 8; 33 | constexpr auto zero = TValue{}; 34 | constexpr auto one = static_cast(1); 35 | 36 | for (size_t i = 0; i < nbits; i++) 37 | { 38 | if ((value & (one << i)) != zero) 39 | { 40 | return i; 41 | } 42 | } 43 | 44 | return 0; 45 | } 46 | 47 | inline std::unique_ptr ReadAllBytes(const char* filePath, size_t& fileSize) 48 | { 49 | FILE* file = fopen(filePath, "rb"); 50 | 51 | if (!file) 52 | return std::make_unique(0); 53 | 54 | fseek(file, 0, SEEK_END); 55 | 56 | fileSize = ftell(file); 57 | fseek(file, 0, SEEK_SET); 58 | 59 | auto data = std::make_unique(fileSize); 60 | fread(data.get(), 1, fileSize, file); 61 | 62 | fclose(file); 63 | 64 | return data; 65 | } 66 | -------------------------------------------------------------------------------- /UnleashedRecomp/gpu/cache/vertex_declaration_cache.h: -------------------------------------------------------------------------------- 1 | g_vertexElements_03CB3EF6B1C43B8C, 2 | g_vertexElements_0EC0CD05EE1B1636, 3 | g_vertexElements_0FB4544424558E4C, 4 | g_vertexElements_28FD2057B9BD5D1B, 5 | g_vertexElements_2A6D72391BFFFA3C, 6 | g_vertexElements_5A22D93C543DF925, 7 | g_vertexElements_5A2395E29F93DA3C, 8 | g_vertexElements_6196BF64CB935CA5, 9 | g_vertexElements_6538EB0019C3A29A, 10 | g_vertexElements_6FAE71C7134074A4, 11 | g_vertexElements_75A4FC397A05F4CA, 12 | g_vertexElements_7F12180DC3A24B53, 13 | g_vertexElements_82A1B9D74331DB2A, 14 | g_vertexElements_84BACD816D86543C, 15 | g_vertexElements_A81F28FA43A9B511, 16 | g_vertexElements_B22B7B7B968141C6, 17 | g_vertexElements_B7BBCC93738C9DE4, 18 | g_vertexElements_C64D046063DE2F63, 19 | g_vertexElements_D452411D3FB80A0D, 20 | g_vertexElements_DEB308DCDDF979C7, 21 | g_vertexElements_E6B3B3D286909AB9, 22 | g_vertexElements_EFD61AD3C5332AAE, 23 | g_vertexElements_F10787EFFEEC0153, 24 | g_vertexElements_FFFDDC62D86892F1, 25 | -------------------------------------------------------------------------------- /UnleashedRecomp/gpu/imgui/imgui_common.cpp: -------------------------------------------------------------------------------- 1 | #include "imgui_common.h" 2 | 3 | static std::vector> g_callbackData; 4 | static uint32_t g_callbackDataIndex = 0; 5 | 6 | ImGuiCallbackData* AddImGuiCallback(ImGuiCallback callback) 7 | { 8 | if (g_callbackDataIndex >= g_callbackData.size()) 9 | g_callbackData.emplace_back(std::make_unique()); 10 | 11 | auto& callbackData = g_callbackData[g_callbackDataIndex]; 12 | ++g_callbackDataIndex; 13 | 14 | ImGui::GetBackgroundDrawList()->AddCallback(reinterpret_cast(callback), callbackData.get()); 15 | 16 | return callbackData.get(); 17 | } 18 | 19 | void ResetImGuiCallbacks() 20 | { 21 | g_callbackDataIndex = 0; 22 | } 23 | -------------------------------------------------------------------------------- /UnleashedRecomp/gpu/imgui/imgui_common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define IMGUI_SHADER_MODIFIER_NONE 0 4 | #define IMGUI_SHADER_MODIFIER_SCANLINE 1 5 | #define IMGUI_SHADER_MODIFIER_CHECKERBOARD 2 6 | #define IMGUI_SHADER_MODIFIER_SCANLINE_BUTTON 3 7 | #define IMGUI_SHADER_MODIFIER_TEXT_SKEW 4 8 | #define IMGUI_SHADER_MODIFIER_HORIZONTAL_MARQUEE_FADE 5 9 | #define IMGUI_SHADER_MODIFIER_VERTICAL_MARQUEE_FADE 6 10 | #define IMGUI_SHADER_MODIFIER_GRAYSCALE 7 11 | #define IMGUI_SHADER_MODIFIER_TITLE_BEVEL 8 12 | #define IMGUI_SHADER_MODIFIER_CATEGORY_BEVEL 9 13 | #define IMGUI_SHADER_MODIFIER_RECTANGLE_BEVEL 10 14 | #define IMGUI_SHADER_MODIFIER_LOW_QUALITY_TEXT 11 15 | 16 | #ifdef __cplusplus 17 | 18 | enum class ImGuiCallback : int32_t 19 | { 20 | SetGradient = -1, 21 | SetShaderModifier = -2, 22 | SetOrigin = -3, 23 | SetScale = -4, 24 | SetMarqueeFade = -5, 25 | SetOutline = -6, 26 | SetProceduralOrigin = -7, 27 | // -8 is ImDrawCallback_ResetRenderState, don't use! 28 | SetAdditive = -9 29 | }; 30 | 31 | union ImGuiCallbackData 32 | { 33 | struct 34 | { 35 | float boundsMin[2]; 36 | float boundsMax[2]; 37 | uint32_t gradientTopLeft; 38 | uint32_t gradientTopRight; 39 | uint32_t gradientBottomRight; 40 | uint32_t gradientBottomLeft; 41 | } setGradient; 42 | 43 | struct 44 | { 45 | uint32_t shaderModifier; 46 | } setShaderModifier; 47 | 48 | struct 49 | { 50 | float origin[2]; 51 | } setOrigin; 52 | 53 | struct 54 | { 55 | float scale[2]; 56 | } setScale; 57 | 58 | struct 59 | { 60 | float boundsMin[2]; 61 | float boundsMax[2]; 62 | } setMarqueeFade; 63 | 64 | struct 65 | { 66 | float outline; 67 | } setOutline; 68 | 69 | struct 70 | { 71 | float proceduralOrigin[2]; 72 | } setProceduralOrigin; 73 | 74 | struct 75 | { 76 | bool enabled; 77 | } setAdditive; 78 | }; 79 | 80 | extern ImGuiCallbackData* AddImGuiCallback(ImGuiCallback callback); 81 | 82 | extern void ResetImGuiCallbacks(); 83 | 84 | #endif 85 | -------------------------------------------------------------------------------- /UnleashedRecomp/gpu/imgui/imgui_font_builder.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | extern ImFontBuilderIO g_fontBuilderIO; 4 | -------------------------------------------------------------------------------- /UnleashedRecomp/gpu/imgui/imgui_snapshot.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Undefine this to generate a font atlas file in working directory. 4 | // You also need to do this if you are testing localization, as only 5 | // characters in the locale get added to the atlas. 6 | #define ENABLE_IM_FONT_ATLAS_SNAPSHOT 7 | 8 | struct ImFontAtlasSnapshot 9 | { 10 | std::vector data; 11 | ankerl::unordered_dense::map objects; 12 | std::vector offsets; 13 | 14 | template 15 | void SnapPointer(size_t offset, const T1& value, const T2& ptr, size_t count); 16 | 17 | template 18 | void Traverse(size_t offset, const T& value); 19 | 20 | template 21 | size_t Snap(const T& value); 22 | 23 | void Snap(); 24 | 25 | static ImFontAtlas* Load(); 26 | 27 | static void GenerateGlyphRanges(); 28 | 29 | // When ENABLE_IM_FONT_ATLAS_SNAPSHOT is undefined, this creates the font runtime instead. 30 | static ImFont* GetFont(const char* name); 31 | }; 32 | -------------------------------------------------------------------------------- /UnleashedRecomp/gpu/rhi/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 renderbag and contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /UnleashedRecomp/gpu/shader/.gitignore: -------------------------------------------------------------------------------- 1 | *.hlsl.*.h 2 | -------------------------------------------------------------------------------- /UnleashedRecomp/gpu/shader/blend_color_alpha_ps.hlsl: -------------------------------------------------------------------------------- 1 | #include "../../../tools/XenosRecomp/XenosRecomp/shader_common.h" 2 | 3 | #ifdef __spirv__ 4 | 5 | #define g_SrcAlpha_DestAlpha vk::RawBufferLoad(g_PushConstants.PixelShaderConstants + 2400, 0x10) 6 | #define s0_Texture2DDescriptorIndex vk::RawBufferLoad(g_PushConstants.SharedConstants + 0) 7 | #define s0_SamplerDescriptorIndex vk::RawBufferLoad(g_PushConstants.SharedConstants + 192) 8 | 9 | #else 10 | 11 | cbuffer PixelShaderConstants : register(b1, space4) 12 | { 13 | float4 g_SrcAlpha_DestAlpha : packoffset(c150); 14 | }; 15 | 16 | cbuffer SharedConstants : register(b2, space4) 17 | { 18 | uint s0_Texture2DDescriptorIndex : packoffset(c0.x); 19 | uint s0_SamplerDescriptorIndex : packoffset(c12.x); 20 | DEFINE_SHARED_CONSTANTS(); 21 | }; 22 | 23 | #endif 24 | 25 | float4 main( 26 | in float4 iPos : SV_Position, 27 | in float4 iTexCoord0 : TEXCOORD0) : SV_Target0 28 | { 29 | Texture2D texture = g_Texture2DDescriptorHeap[s0_Texture2DDescriptorIndex]; 30 | SamplerState samplerState = g_SamplerDescriptorHeap[s0_SamplerDescriptorIndex]; 31 | 32 | float4 color = texture.Sample(samplerState, iTexCoord0.xy); 33 | 34 | if (any(or(iTexCoord0.xy < 0.0, iTexCoord0.xy > 1.0))) 35 | color = float4(0.0, 0.0, 0.0, 1.0); 36 | 37 | color.rgb *= color.a * g_SrcAlpha_DestAlpha.x; 38 | color.a = g_SrcAlpha_DestAlpha.y + (1.0 - color.a) * g_SrcAlpha_DestAlpha.x; 39 | 40 | return color; 41 | } 42 | -------------------------------------------------------------------------------- /UnleashedRecomp/gpu/shader/copy_color_ps.hlsl: -------------------------------------------------------------------------------- 1 | #include "copy_common.hlsli" 2 | 3 | Texture2D g_Texture2DDescriptorHeap[] : register(t0, space0); 4 | 5 | float4 main(in float4 position : SV_Position) : SV_Target 6 | { 7 | return g_Texture2DDescriptorHeap[g_PushConstants.ResourceDescriptorIndex].Load(int3(position.xy, 0)); 8 | } 9 | -------------------------------------------------------------------------------- /UnleashedRecomp/gpu/shader/copy_common.hlsli: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct PushConstants 4 | { 5 | uint ResourceDescriptorIndex; 6 | }; 7 | 8 | [[vk::push_constant]] ConstantBuffer g_PushConstants : register(b3, space4); 9 | -------------------------------------------------------------------------------- /UnleashedRecomp/gpu/shader/copy_depth_ps.hlsl: -------------------------------------------------------------------------------- 1 | #include "copy_common.hlsli" 2 | 3 | Texture2D g_Texture2DDescriptorHeap[] : register(t0, space0); 4 | 5 | float main(in float4 position : SV_Position) : SV_Depth 6 | { 7 | return g_Texture2DDescriptorHeap[g_PushConstants.ResourceDescriptorIndex].Load(int3(position.xy, 0)); 8 | } 9 | -------------------------------------------------------------------------------- /UnleashedRecomp/gpu/shader/copy_vs.hlsl: -------------------------------------------------------------------------------- 1 | void main(in uint vertexId : SV_VertexID, out float4 position : SV_Position, out float2 texCoord : TEXCOORD) 2 | { 3 | texCoord = float2((vertexId << 1) & 2, vertexId & 2); 4 | position = float4(texCoord * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0); 5 | } 6 | -------------------------------------------------------------------------------- /UnleashedRecomp/gpu/shader/csd_filter_ps.hlsl: -------------------------------------------------------------------------------- 1 | #include "../../../tools/XenosRecomp/XenosRecomp/shader_common.h" 2 | 3 | #ifdef __spirv__ 4 | 5 | #define s0_Texture2DDescriptorIndex vk::RawBufferLoad(g_PushConstants.SharedConstants + 0) 6 | #define s0_SamplerDescriptorIndex vk::RawBufferLoad(g_PushConstants.SharedConstants + 192) 7 | 8 | #else 9 | 10 | cbuffer SharedConstants : register(b2, space4) 11 | { 12 | uint s0_Texture2DDescriptorIndex : packoffset(c0.x); 13 | uint s0_SamplerDescriptorIndex : packoffset(c12.x); 14 | DEFINE_SHARED_CONSTANTS(); 15 | }; 16 | 17 | #endif 18 | 19 | float4 main( 20 | in float4 iPosition : SV_Position, 21 | in float4 iTexCoord0 : TEXCOORD0, 22 | in float4 iTexCoord1 : TEXCOORD1) : SV_Target 23 | { 24 | Texture2D texture = g_Texture2DDescriptorHeap[s0_Texture2DDescriptorIndex]; 25 | SamplerState samplerState = g_SamplerDescriptorHeap[s0_SamplerDescriptorIndex]; 26 | 27 | uint2 dimensions; 28 | texture.GetDimensions(dimensions.x, dimensions.y); 29 | 30 | // https://www.shadertoy.com/view/csX3RH 31 | float2 uvTexspace = iTexCoord1.xy * dimensions; 32 | float2 seam = floor(uvTexspace + 0.5); 33 | uvTexspace = (uvTexspace - seam) / fwidth(uvTexspace) + seam; 34 | uvTexspace = clamp(uvTexspace, seam - 0.5, seam + 0.5); 35 | float2 texCoord = uvTexspace / dimensions; 36 | 37 | float4 color = texture.Sample(samplerState, texCoord); 38 | color *= iTexCoord0; 39 | 40 | // The game enables alpha test for CSD, but the alpha threshold doesn't seem to be assigned anywhere? Weird. 41 | clip(color.a - g_AlphaThreshold); 42 | 43 | return color; 44 | } 45 | -------------------------------------------------------------------------------- /UnleashedRecomp/gpu/shader/csd_no_tex_vs.hlsl: -------------------------------------------------------------------------------- 1 | #include "../../../tools/XenosRecomp/XenosRecomp/shader_common.h" 2 | 3 | #ifdef __spirv__ 4 | 5 | #define g_ViewportSize vk::RawBufferLoad(g_PushConstants.VertexShaderConstants + 2880, 0x10) 6 | #define g_Z vk::RawBufferLoad(g_PushConstants.VertexShaderConstants + 3936, 0x10) 7 | 8 | #else 9 | 10 | cbuffer VertexShaderConstants : register(b0, space4) 11 | { 12 | float4 g_ViewportSize : packoffset(c180); 13 | float4 g_Z : packoffset(c246); 14 | }; 15 | 16 | cbuffer SharedConstants : register(b2, space4) 17 | { 18 | DEFINE_SHARED_CONSTANTS(); 19 | }; 20 | 21 | #endif 22 | 23 | void main( 24 | [[vk::location(0)]] in float4 iPosition0 : POSITION0, 25 | [[vk::location(8)]] in float4 iColor0 : COLOR0, 26 | out float4 oPos : SV_Position, 27 | out float4 oTexCoord0 : TEXCOORD0, 28 | out float4 oTexCoord1 : TEXCOORD1, 29 | out float4 oTexCoord2 : TEXCOORD2, 30 | out float4 oTexCoord3 : TEXCOORD3, 31 | out float4 oTexCoord4 : TEXCOORD4, 32 | out float4 oTexCoord5 : TEXCOORD5, 33 | out float4 oTexCoord6 : TEXCOORD6, 34 | out float4 oTexCoord7 : TEXCOORD7, 35 | out float4 oTexCoord8 : TEXCOORD8, 36 | out float4 oTexCoord9 : TEXCOORD9, 37 | out float4 oTexCoord10 : TEXCOORD10, 38 | out float4 oTexCoord11 : TEXCOORD11, 39 | out float4 oTexCoord12 : TEXCOORD12, 40 | out float4 oTexCoord13 : TEXCOORD13, 41 | out float4 oTexCoord14 : TEXCOORD14, 42 | out float4 oTexCoord15 : TEXCOORD15, 43 | out float4 oColor0 : COLOR0, 44 | out float4 oColor1 : COLOR1) 45 | { 46 | oPos.xy = iPosition0.xy * g_ViewportSize.zw * float2(2.0, -2.0) + float2(-1.0, 1.0); 47 | oPos.z = g_Z.x; 48 | oPos.w = 1.0; 49 | oTexCoord0 = iColor0.wxyz; 50 | oTexCoord1 = 0.0; 51 | oTexCoord2 = 0.0; 52 | oTexCoord3 = 0.0; 53 | oTexCoord4 = 0.0; 54 | oTexCoord5 = 0.0; 55 | oTexCoord6 = 0.0; 56 | oTexCoord7 = 0.0; 57 | oTexCoord8 = 0.0; 58 | oTexCoord9 = 0.0; 59 | oTexCoord10 = 0.0; 60 | oTexCoord11 = 0.0; 61 | oTexCoord12 = 0.0; 62 | oTexCoord13 = 0.0; 63 | oTexCoord14 = 0.0; 64 | oTexCoord15 = 0.0; 65 | oColor0 = 0.0; 66 | oColor1 = 0.0; 67 | } 68 | -------------------------------------------------------------------------------- /UnleashedRecomp/gpu/shader/csd_vs.hlsl: -------------------------------------------------------------------------------- 1 | #include "../../../tools/XenosRecomp/XenosRecomp/shader_common.h" 2 | 3 | #ifdef __spirv__ 4 | 5 | #define g_ViewportSize vk::RawBufferLoad(g_PushConstants.VertexShaderConstants + 2880, 0x10) 6 | #define g_Z vk::RawBufferLoad(g_PushConstants.VertexShaderConstants + 3936, 0x10) 7 | 8 | #else 9 | 10 | cbuffer VertexShaderConstants : register(b0, space4) 11 | { 12 | float4 g_ViewportSize : packoffset(c180); 13 | float4 g_Z : packoffset(c246); 14 | }; 15 | 16 | cbuffer SharedConstants : register(b2, space4) 17 | { 18 | DEFINE_SHARED_CONSTANTS(); 19 | }; 20 | 21 | #endif 22 | 23 | void main( 24 | [[vk::location(0)]] in float4 iPosition0 : POSITION0, 25 | [[vk::location(8)]] in float4 iColor0 : COLOR0, 26 | [[vk::location(4)]] in float4 iTexCoord0 : TEXCOORD0, 27 | out float4 oPos : SV_Position, 28 | out float4 oTexCoord0 : TEXCOORD0, 29 | out float4 oTexCoord1 : TEXCOORD1, 30 | out float4 oTexCoord2 : TEXCOORD2, 31 | out float4 oTexCoord3 : TEXCOORD3, 32 | out float4 oTexCoord4 : TEXCOORD4, 33 | out float4 oTexCoord5 : TEXCOORD5, 34 | out float4 oTexCoord6 : TEXCOORD6, 35 | out float4 oTexCoord7 : TEXCOORD7, 36 | out float4 oTexCoord8 : TEXCOORD8, 37 | out float4 oTexCoord9 : TEXCOORD9, 38 | out float4 oTexCoord10 : TEXCOORD10, 39 | out float4 oTexCoord11 : TEXCOORD11, 40 | out float4 oTexCoord12 : TEXCOORD12, 41 | out float4 oTexCoord13 : TEXCOORD13, 42 | out float4 oTexCoord14 : TEXCOORD14, 43 | out float4 oTexCoord15 : TEXCOORD15, 44 | out float4 oColor0 : COLOR0, 45 | out float4 oColor1 : COLOR1) 46 | { 47 | oPos.xy = iPosition0.xy * g_ViewportSize.zw * float2(2.0, -2.0) + float2(-1.0, 1.0); 48 | oPos.z = g_Z.x; 49 | oPos.w = 1.0; 50 | oTexCoord0 = iColor0.wxyz; 51 | oTexCoord1.xy = iTexCoord0.xy; 52 | oTexCoord1.zw = 0.0; 53 | oTexCoord2 = 0.0; 54 | oTexCoord3 = 0.0; 55 | oTexCoord4 = 0.0; 56 | oTexCoord5 = 0.0; 57 | oTexCoord6 = 0.0; 58 | oTexCoord7 = 0.0; 59 | oTexCoord8 = 0.0; 60 | oTexCoord9 = 0.0; 61 | oTexCoord10 = 0.0; 62 | oTexCoord11 = 0.0; 63 | oTexCoord12 = 0.0; 64 | oTexCoord13 = 0.0; 65 | oTexCoord14 = 0.0; 66 | oTexCoord15 = 0.0; 67 | oColor0 = 0.0; 68 | oColor1 = 0.0; 69 | } 70 | -------------------------------------------------------------------------------- /UnleashedRecomp/gpu/shader/enhanced_motion_blur_ps.hlsl: -------------------------------------------------------------------------------- 1 | #include "../../../tools/XenosRecomp/XenosRecomp/shader_common.h" 2 | 3 | #ifdef __spirv__ 4 | 5 | #define g_BlurRate vk::RawBufferLoad(g_PushConstants.PixelShaderConstants + 2400, 0x10) 6 | #define g_ViewportSize vk::RawBufferLoad(g_PushConstants.PixelShaderConstants + 384, 0x10) 7 | 8 | #define sampColor_Texture2DDescriptorIndex vk::RawBufferLoad(g_PushConstants.SharedConstants + 0) 9 | #define sampColor_SamplerDescriptorIndex vk::RawBufferLoad(g_PushConstants.SharedConstants + 192) 10 | 11 | #define sampVelocityMap_Texture2DDescriptorIndex vk::RawBufferLoad(g_PushConstants.SharedConstants + 4) 12 | #define sampVelocityMap_SamplerDescriptorIndex vk::RawBufferLoad(g_PushConstants.SharedConstants + 196) 13 | 14 | #define sampZBuffer_Texture2DDescriptorIndex vk::RawBufferLoad(g_PushConstants.SharedConstants + 8) 15 | #define sampZBuffer_SamplerDescriptorIndex vk::RawBufferLoad(g_PushConstants.SharedConstants + 200) 16 | 17 | #else 18 | 19 | cbuffer PixelShaderConstants : register(b1, space4) 20 | { 21 | float4 g_BlurRate : packoffset(c150); 22 | float4 g_ViewportSize : packoffset(c24); 23 | }; 24 | 25 | cbuffer SharedConstants : register(b2, space4) 26 | { 27 | uint sampColor_Texture2DDescriptorIndex : packoffset(c0.x); 28 | uint sampColor_SamplerDescriptorIndex : packoffset(c12.x); 29 | 30 | uint sampVelocityMap_Texture2DDescriptorIndex : packoffset(c0.y); 31 | uint sampVelocityMap_SamplerDescriptorIndex : packoffset(c12.y); 32 | 33 | uint sampZBuffer_Texture2DDescriptorIndex : packoffset(c0.z); 34 | uint sampZBuffer_SamplerDescriptorIndex : packoffset(c12.z); 35 | 36 | DEFINE_SHARED_CONSTANTS(); 37 | }; 38 | 39 | #endif 40 | 41 | float4 main(in float4 position : SV_Position, in float4 texCoord : TEXCOORD0) : SV_Target 42 | { 43 | Texture2D sampColor = g_Texture2DDescriptorHeap[sampColor_Texture2DDescriptorIndex]; 44 | Texture2D sampVelocityMap = g_Texture2DDescriptorHeap[sampVelocityMap_Texture2DDescriptorIndex]; 45 | Texture2D sampZBuffer = g_Texture2DDescriptorHeap[sampZBuffer_Texture2DDescriptorIndex]; 46 | 47 | SamplerState sampColor_s = g_SamplerDescriptorHeap[sampColor_SamplerDescriptorIndex]; 48 | SamplerState sampVelocityMap_s = g_SamplerDescriptorHeap[sampVelocityMap_SamplerDescriptorIndex]; 49 | SamplerState sampZBuffer_s = g_SamplerDescriptorHeap[sampZBuffer_SamplerDescriptorIndex]; 50 | 51 | float depth = sampZBuffer.SampleLevel(sampZBuffer_s, texCoord.xy, 0).x; 52 | float4 velocityMap = sampVelocityMap.SampleLevel(sampVelocityMap_s, texCoord.xy, 0); 53 | float2 velocity = (velocityMap.xz + velocityMap.yw / 255.0) * 2.0 - 1.0; 54 | 55 | int sampleCount = min(64, round(length(velocity * g_ViewportSize.xy))); 56 | float2 sampleOffset = velocity / (float) sampleCount; 57 | 58 | float3 color = sampColor.SampleLevel(sampColor_s, texCoord.xy, 0).rgb; 59 | int count = 1; 60 | 61 | for (int i = 1; i <= sampleCount; i++) 62 | { 63 | float2 sampleCoord = texCoord.xy + sampleOffset * i; 64 | float3 sampleColor = sampColor.SampleLevel(sampColor_s, sampleCoord, 0).rgb; 65 | float sampleDepth = sampZBuffer.SampleLevel(sampZBuffer_s, sampleCoord, 0).x; 66 | 67 | if (sampleDepth - depth < 0.01) 68 | { 69 | color += sampleColor; 70 | count += 1; 71 | } 72 | } 73 | 74 | return float4(color / count, g_BlurRate.x * saturate(dot(abs(velocity), g_ViewportSize.xy) / 8.0) * saturate(count - 1)); 75 | } 76 | -------------------------------------------------------------------------------- /UnleashedRecomp/gpu/shader/gamma_correction_ps.hlsl: -------------------------------------------------------------------------------- 1 | #include "../../../tools/XenosRecomp/XenosRecomp/shader_common.h" 2 | 3 | #ifdef __spirv__ 4 | 5 | #define g_Gamma vk::RawBufferLoad(g_PushConstants.SharedConstants + 0) 6 | #define g_TextureDescriptorIndex vk::RawBufferLoad(g_PushConstants.SharedConstants + 12) 7 | 8 | #define g_ViewportOffset vk::RawBufferLoad(g_PushConstants.SharedConstants + 16) 9 | #define g_ViewportSize vk::RawBufferLoad(g_PushConstants.SharedConstants + 24) 10 | 11 | #else 12 | 13 | cbuffer SharedConstants : register(b2, space4) 14 | { 15 | float3 g_Gamma; 16 | uint g_TextureDescriptorIndex; 17 | int2 g_ViewportOffset; 18 | int2 g_ViewportSize; 19 | }; 20 | 21 | #endif 22 | 23 | float4 main(in float4 position : SV_Position) : SV_Target 24 | { 25 | Texture2D texture = g_Texture2DDescriptorHeap[g_TextureDescriptorIndex]; 26 | 27 | int2 movedPosition = int2(position.xy) - g_ViewportOffset; 28 | bool boxed = any(movedPosition < 0) || any(movedPosition >= g_ViewportSize); 29 | if (boxed) movedPosition = 0; 30 | 31 | float4 color = boxed ? 0.0 : texture.Load(int3(movedPosition, 0)); 32 | color.rgb = pow(color.rgb, g_Gamma); 33 | return color; 34 | } 35 | -------------------------------------------------------------------------------- /UnleashedRecomp/gpu/shader/gaussian_blur.hlsli: -------------------------------------------------------------------------------- 1 | #include "../../../tools/XenosRecomp/XenosRecomp/shader_common.h" 2 | 3 | #ifdef __spirv__ 4 | 5 | #define g_ViewportSize vk::RawBufferLoad(g_PushConstants.PixelShaderConstants + 384, 0x10) 6 | #define g_offsets(INDEX) select((INDEX) < 74, vk::RawBufferLoad(g_PushConstants.PixelShaderConstants + (150 + min(INDEX, 73)) * 16, 0x10), 0.0) 7 | #define g_weights vk::RawBufferLoad(g_PushConstants.PixelShaderConstants + 2656, 0x10) 8 | 9 | #define s0_Texture2DDescriptorIndex vk::RawBufferLoad(g_PushConstants.SharedConstants + 0) 10 | #define s0_SamplerDescriptorIndex vk::RawBufferLoad(g_PushConstants.SharedConstants + 192) 11 | 12 | #else 13 | 14 | cbuffer PixelShaderConstants : register(b1, space4) 15 | { 16 | float4 g_ViewportSize : packoffset(c24); 17 | float4 g_offsets[2] : packoffset(c150); 18 | #define g_offsets(INDEX) select((INDEX) < 74, g_offsets[min(INDEX, 73)], 0.0) 19 | float4 g_weights : packoffset(c166); 20 | }; 21 | 22 | cbuffer SharedConstants : register(b2, space4) 23 | { 24 | uint s0_Texture2DDescriptorIndex : packoffset(c0.x); 25 | uint s0_SamplerDescriptorIndex : packoffset(c12.x); 26 | DEFINE_SHARED_CONSTANTS(); 27 | }; 28 | 29 | #endif 30 | 31 | #ifdef __INTELLISENSE__ 32 | #define KERNEL_SIZE 5 33 | #endif 34 | 35 | #define PI 3.14159265358979323846 36 | 37 | float ComputeWeight(float x) 38 | { 39 | float std = 0.952; 40 | return exp(-(x * x) / (2.0 * std * std)) / (std * sqrt(2.0 * PI)); 41 | } 42 | 43 | float4 main(in float4 iPosition : SV_Position, in float4 iTexCoord0 : TEXCOORD0) : SV_Target 44 | { 45 | Texture2D texture = g_Texture2DDescriptorHeap[s0_Texture2DDescriptorIndex]; 46 | SamplerState samplerState = g_SamplerDescriptorHeap[s0_SamplerDescriptorIndex]; 47 | 48 | float scale; 49 | if ((g_ViewportSize.x * g_ViewportSize.w) >= (16.0 / 9.0)) 50 | scale = g_ViewportSize.y / 360.0; 51 | else 52 | scale = g_ViewportSize.x / 640.0; 53 | 54 | float2 offsets[3]; 55 | offsets[0] = g_offsets(0).xy * scale; 56 | offsets[1] = g_offsets(0).zw * scale; 57 | offsets[2] = g_offsets(1).xy * scale; 58 | 59 | float4 color = 0.0; 60 | float weightSum = 0.0; 61 | 62 | [unroll] 63 | for (int i = 0; i < KERNEL_SIZE; i++) 64 | { 65 | float step = i / float(KERNEL_SIZE - 1); 66 | float scaled = step * 2; 67 | float2 offset = lerp(offsets[int(scaled)], offsets[min(int(scaled) + 1, 2)], frac(scaled)); 68 | float offsetScale = 1.0 / 0.75; 69 | float weight = ComputeWeight(lerp(-offsetScale, offsetScale, step)); 70 | color += texture.SampleLevel(samplerState, iTexCoord0.xy + offset, 0) * weight; 71 | weightSum += weight; 72 | } 73 | 74 | return color / weightSum; 75 | } 76 | -------------------------------------------------------------------------------- /UnleashedRecomp/gpu/shader/gaussian_blur_3x3.hlsl: -------------------------------------------------------------------------------- 1 | #define KERNEL_SIZE 3 2 | #include "gaussian_blur.hlsli" 3 | -------------------------------------------------------------------------------- /UnleashedRecomp/gpu/shader/gaussian_blur_5x5.hlsl: -------------------------------------------------------------------------------- 1 | #define KERNEL_SIZE 5 2 | #include "gaussian_blur.hlsli" 3 | -------------------------------------------------------------------------------- /UnleashedRecomp/gpu/shader/gaussian_blur_7x7.hlsl: -------------------------------------------------------------------------------- 1 | #define KERNEL_SIZE 7 2 | #include "gaussian_blur.hlsli" 3 | -------------------------------------------------------------------------------- /UnleashedRecomp/gpu/shader/gaussian_blur_9x9.hlsl: -------------------------------------------------------------------------------- 1 | #define KERNEL_SIZE 9 2 | #include "gaussian_blur.hlsli" 3 | -------------------------------------------------------------------------------- /UnleashedRecomp/gpu/shader/imgui_common.hlsli: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../imgui/imgui_common.h" 4 | 5 | struct PushConstants 6 | { 7 | float2 BoundsMin; 8 | float2 BoundsMax; 9 | uint GradientTopLeft; 10 | uint GradientTopRight; 11 | uint GradientBottomRight; 12 | uint GradientBottomLeft; 13 | uint ShaderModifier; 14 | uint Texture2DDescriptorIndex; 15 | float2 DisplaySize; 16 | float2 InverseDisplaySize; 17 | float2 Origin; 18 | float2 Scale; 19 | float2 ProceduralOrigin; 20 | float Outline; 21 | }; 22 | 23 | Texture2D g_Texture2DDescriptorHeap[] : register(t0, space0); 24 | SamplerState g_SamplerDescriptorHeap[] : register(s0, space1); 25 | [[vk::push_constant]] ConstantBuffer g_PushConstants : register(b0, space2); 26 | 27 | struct Interpolators 28 | { 29 | float4 Position : SV_Position; 30 | float2 UV : TEXCOORD; 31 | float4 Color : COLOR; 32 | }; 33 | -------------------------------------------------------------------------------- /UnleashedRecomp/gpu/shader/imgui_vs.hlsl: -------------------------------------------------------------------------------- 1 | #include "imgui_common.hlsli" 2 | 3 | void main(in float2 position : POSITION, in float2 uv : TEXCOORD, in float4 color : COLOR, out Interpolators interpolators) 4 | { 5 | if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_TEXT_SKEW) 6 | { 7 | if (position.y < g_PushConstants.Origin.y) 8 | position.x += g_PushConstants.Scale.x; 9 | } 10 | else if (g_PushConstants.ShaderModifier != IMGUI_SHADER_MODIFIER_HORIZONTAL_MARQUEE_FADE && 11 | g_PushConstants.ShaderModifier != IMGUI_SHADER_MODIFIER_VERTICAL_MARQUEE_FADE) 12 | { 13 | position.xy = g_PushConstants.Origin + (position.xy - g_PushConstants.Origin) * g_PushConstants.Scale; 14 | } 15 | 16 | interpolators.Position = float4(position.xy * g_PushConstants.InverseDisplaySize * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0); 17 | interpolators.UV = uv; 18 | interpolators.Color = color; 19 | } 20 | -------------------------------------------------------------------------------- /UnleashedRecomp/gpu/shader/movie_common.hlsli: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../../../tools/XenosRecomp/XenosRecomp/shader_common.h" 4 | 5 | #ifdef __spirv__ 6 | 7 | #define fZmin vk::RawBufferLoad(g_PushConstants.PixelShaderConstants + 0) 8 | #define fZmax vk::RawBufferLoad(g_PushConstants.PixelShaderConstants + 16) 9 | 10 | #define Tex0_ResourceDescriptorIndex vk::RawBufferLoad(g_PushConstants.SharedConstants + 0) 11 | #define Tex1_ResourceDescriptorIndex vk::RawBufferLoad(g_PushConstants.SharedConstants + 4) 12 | #define Tex2_ResourceDescriptorIndex vk::RawBufferLoad(g_PushConstants.SharedConstants + 8) 13 | #define Tex3_ResourceDescriptorIndex vk::RawBufferLoad(g_PushConstants.SharedConstants + 12) 14 | #define Tex4_ResourceDescriptorIndex vk::RawBufferLoad(g_PushConstants.SharedConstants + 16) 15 | 16 | #define Tex0_SamplerDescriptorIndex vk::RawBufferLoad(g_PushConstants.SharedConstants + 64) 17 | #define Tex1_SamplerDescriptorIndex vk::RawBufferLoad(g_PushConstants.SharedConstants + 68) 18 | #define Tex2_SamplerDescriptorIndex vk::RawBufferLoad(g_PushConstants.SharedConstants + 72) 19 | #define Tex3_SamplerDescriptorIndex vk::RawBufferLoad(g_PushConstants.SharedConstants + 76) 20 | #define Tex4_SamplerDescriptorIndex vk::RawBufferLoad(g_PushConstants.SharedConstants + 80) 21 | 22 | #else 23 | 24 | cbuffer PixelShaderConstants : register(b1, space4) 25 | { 26 | float fZmin : packoffset(c0); 27 | float fZmax : packoffset(c1); 28 | }; 29 | 30 | cbuffer SharedConstants : register(b2, space4) 31 | { 32 | uint Tex0_ResourceDescriptorIndex : packoffset(c0.x); 33 | uint Tex1_ResourceDescriptorIndex : packoffset(c0.y); 34 | uint Tex2_ResourceDescriptorIndex : packoffset(c0.z); 35 | uint Tex3_ResourceDescriptorIndex : packoffset(c0.w); 36 | uint Tex4_ResourceDescriptorIndex : packoffset(c1.x); 37 | 38 | uint Tex0_SamplerDescriptorIndex : packoffset(c4.x); 39 | uint Tex1_SamplerDescriptorIndex : packoffset(c4.y); 40 | uint Tex2_SamplerDescriptorIndex : packoffset(c4.z); 41 | uint Tex3_SamplerDescriptorIndex : packoffset(c4.w); 42 | uint Tex4_SamplerDescriptorIndex : packoffset(c5.x); 43 | 44 | DEFINE_SHARED_CONSTANTS(); 45 | }; 46 | 47 | #endif 48 | 49 | #define bCsc (g_Booleans & (1 << (16 + 0))) 50 | #define bAmv (g_Booleans & (1 << (16 + 1))) 51 | #define bZmv (g_Booleans & (1 << (16 + 2))) 52 | 53 | struct VertexShaderInput 54 | { 55 | [[vk::location(0)]] float4 ObjPos : POSITION; 56 | [[vk::location(4)]] float2 UV : TEXCOORD; 57 | }; 58 | 59 | struct Interpolators 60 | { 61 | float4 ProjPos : SV_Position; 62 | float2 UV : TEXCOORD0; 63 | }; 64 | 65 | struct PixelShaderOutput 66 | { 67 | float4 Color : SV_Target0; 68 | float Depth : SV_Depth; 69 | }; 70 | -------------------------------------------------------------------------------- /UnleashedRecomp/gpu/shader/movie_ps.hlsl: -------------------------------------------------------------------------------- 1 | #include "movie_common.hlsli" 2 | 3 | PixelShaderOutput main(in Interpolators In) 4 | { 5 | Texture2D Tex0 = g_Texture2DDescriptorHeap[Tex0_ResourceDescriptorIndex]; 6 | Texture2D Tex1 = g_Texture2DDescriptorHeap[Tex1_ResourceDescriptorIndex]; 7 | Texture2D Tex2 = g_Texture2DDescriptorHeap[Tex2_ResourceDescriptorIndex]; 8 | Texture2D Tex3 = g_Texture2DDescriptorHeap[Tex3_ResourceDescriptorIndex]; 9 | Texture2D Tex4 = g_Texture2DDescriptorHeap[Tex4_ResourceDescriptorIndex]; 10 | 11 | SamplerState Tex0_s = g_SamplerDescriptorHeap[Tex0_SamplerDescriptorIndex]; 12 | SamplerState Tex1_s = g_SamplerDescriptorHeap[Tex1_SamplerDescriptorIndex]; 13 | SamplerState Tex2_s = g_SamplerDescriptorHeap[Tex2_SamplerDescriptorIndex]; 14 | SamplerState Tex3_s = g_SamplerDescriptorHeap[Tex3_SamplerDescriptorIndex]; 15 | SamplerState Tex4_s = g_SamplerDescriptorHeap[Tex4_SamplerDescriptorIndex]; 16 | 17 | PixelShaderOutput Out; 18 | float ValY = Tex0.Sample(Tex0_s, In.UV).r; 19 | float ValU = Tex1.Sample(Tex1_s, In.UV).r - 0.5; 20 | float ValV = Tex2.Sample(Tex2_s, In.UV).r - 0.5; 21 | float ValA = 1.0; 22 | float ValD = 0.0; 23 | if (bAmv) 24 | ValA = (Tex3.Sample(Tex3_s, In.UV).r - 0.0625) * 1.164; 25 | if (bZmv) 26 | { 27 | ValD = (Tex4.Sample(Tex4_s, In.UV).r - 0.0625) * 1.164; 28 | if (ValD < 9.0 / 255.0) 29 | { 30 | ValD = 0.0; 31 | } 32 | else if (ValD < 17.0 / 255.0) 33 | { 34 | ValD = fZmin; 35 | } 36 | else if (ValD < 224.0 / 255.0) 37 | { 38 | ValD = (ValD - 17.0 / 255.0) / (223.0 / 255.0 - 17.0 / 255.0) * (fZmax - fZmin) + fZmin; 39 | } 40 | else if (ValD < 240.0 / 255.0) 41 | { 42 | ValD = fZmax; 43 | } 44 | else 45 | { 46 | ValD = 1.0; 47 | } 48 | } 49 | if (bCsc) 50 | { 51 | if (ValY < 16.0 / 255.0) 52 | { 53 | ValY = ValY * 3.0 / 2.0; 54 | } 55 | else if (ValY < 176.0 / 255.0) 56 | { 57 | ValY = 24.0 / 255.0 + (ValY - 16.0 / 255.0) / 2.0; 58 | } 59 | else if (ValY < 192.0 / 255.0) 60 | { 61 | ValY = 104.0 / 255.0 + (ValY - 176.0 / 255.0) / 1.0; 62 | } 63 | else 64 | { 65 | ValY = 120.0 / 255.0 + (ValY - 192.0 / 255.0) * 2.0; 66 | } 67 | if (abs(ValU) < 24.0 / 255.0) 68 | { 69 | ValU /= 3.0; 70 | } 71 | else 72 | { 73 | ValU = (8.0 / 255.0 + (abs(ValU) - 24.0 / 255.0) * (120.0 / 104.0)) * sign(ValU); 74 | } 75 | if (abs(ValV) < 24.0 / 255.0) 76 | { 77 | ValV /= 3.0; 78 | } 79 | else 80 | { 81 | ValV = (8.0 / 255.0 + (abs(ValV) - 24.0 / 255.0) * (120.0 / 104.0)) * sign(ValV); 82 | } 83 | Out.Color.r = ValY + ValV * 1.402; 84 | Out.Color.g = ValY - ValU * 0.344 - ValV * 0.714; 85 | Out.Color.b = ValY + ValU * 1.772; 86 | } 87 | else 88 | { 89 | ValY = (ValY - 0.0625) * 1.164; 90 | Out.Color.r = ValY + ValV * 1.596; 91 | Out.Color.g = ValY - ValU * 0.392 - ValV * 0.813; 92 | Out.Color.b = ValY + ValU * 2.017; 93 | } 94 | Out.Color.a = ValA; 95 | 96 | if (any(In.UV < 0.0) || any(In.UV > 1.0)) 97 | Out.Color.rgb = 0.0; 98 | 99 | Out.Depth = ValD; 100 | return Out; 101 | } 102 | -------------------------------------------------------------------------------- /UnleashedRecomp/gpu/shader/movie_vs.hlsl: -------------------------------------------------------------------------------- 1 | #include "movie_common.hlsli" 2 | 3 | Interpolators main(in VertexShaderInput In) 4 | { 5 | Interpolators Out; 6 | Out.ProjPos = In.ObjPos; 7 | Out.ProjPos.xy += g_HalfPixelOffset * Out.ProjPos.w; 8 | Out.UV = In.UV; 9 | return Out; 10 | } 11 | -------------------------------------------------------------------------------- /UnleashedRecomp/gpu/shader/resolve_msaa_color.hlsli: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "copy_common.hlsli" 4 | 5 | Texture2DMS g_Texture2DMSDescriptorHeap[] : register(t0, space0); 6 | 7 | float4 main(in float4 position : SV_Position) : SV_Target 8 | { 9 | float4 result = g_Texture2DMSDescriptorHeap[g_PushConstants.ResourceDescriptorIndex].Load(int2(position.xy), 0); 10 | 11 | [unroll] for (int i = 1; i < SAMPLE_COUNT; i++) 12 | result += g_Texture2DMSDescriptorHeap[g_PushConstants.ResourceDescriptorIndex].Load(int2(position.xy), i); 13 | 14 | return result / SAMPLE_COUNT; 15 | } 16 | -------------------------------------------------------------------------------- /UnleashedRecomp/gpu/shader/resolve_msaa_color_2x.hlsl: -------------------------------------------------------------------------------- 1 | #define SAMPLE_COUNT 2 2 | #include "resolve_msaa_color.hlsli" 3 | -------------------------------------------------------------------------------- /UnleashedRecomp/gpu/shader/resolve_msaa_color_4x.hlsl: -------------------------------------------------------------------------------- 1 | #define SAMPLE_COUNT 4 2 | #include "resolve_msaa_color.hlsli" 3 | -------------------------------------------------------------------------------- /UnleashedRecomp/gpu/shader/resolve_msaa_color_8x.hlsl: -------------------------------------------------------------------------------- 1 | #define SAMPLE_COUNT 8 2 | #include "resolve_msaa_color.hlsli" 3 | -------------------------------------------------------------------------------- /UnleashedRecomp/gpu/shader/resolve_msaa_depth.hlsli: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "copy_common.hlsli" 4 | 5 | Texture2DMS g_Texture2DMSDescriptorHeap[] : register(t0, space0); 6 | 7 | float main(in float4 position : SV_Position) : SV_Depth 8 | { 9 | float result = g_Texture2DMSDescriptorHeap[g_PushConstants.ResourceDescriptorIndex].Load(int2(position.xy), 0); 10 | 11 | [unroll] for (int i = 1; i < SAMPLE_COUNT; i++) 12 | result = min(result, g_Texture2DMSDescriptorHeap[g_PushConstants.ResourceDescriptorIndex].Load(int2(position.xy), i)); 13 | 14 | return result; 15 | } 16 | -------------------------------------------------------------------------------- /UnleashedRecomp/gpu/shader/resolve_msaa_depth_2x.hlsl: -------------------------------------------------------------------------------- 1 | #define SAMPLE_COUNT 2 2 | #include "resolve_msaa_depth.hlsli" 3 | -------------------------------------------------------------------------------- /UnleashedRecomp/gpu/shader/resolve_msaa_depth_4x.hlsl: -------------------------------------------------------------------------------- 1 | #define SAMPLE_COUNT 4 2 | #include "resolve_msaa_depth.hlsli" 3 | -------------------------------------------------------------------------------- /UnleashedRecomp/gpu/shader/resolve_msaa_depth_8x.hlsl: -------------------------------------------------------------------------------- 1 | #define SAMPLE_COUNT 8 2 | #include "resolve_msaa_depth.hlsli" 3 | -------------------------------------------------------------------------------- /UnleashedRecomp/hid/hid.cpp: -------------------------------------------------------------------------------- 1 | #include "hid.h" 2 | #include 3 | #include 4 | 5 | hid::EInputDevice hid::g_inputDevice; 6 | hid::EInputDevice hid::g_inputDeviceController; 7 | hid::EInputDeviceExplicit hid::g_inputDeviceExplicit; 8 | 9 | uint16_t hid::g_prohibitedButtons; 10 | bool hid::g_isLeftStickProhibited; 11 | bool hid::g_isRightStickProhibited; 12 | 13 | void hid::SetProhibitedInputs(uint16_t wButtons, bool leftStick, bool rightStick) 14 | { 15 | hid::g_prohibitedButtons = wButtons; 16 | hid::g_isLeftStickProhibited = leftStick; 17 | hid::g_isRightStickProhibited = rightStick; 18 | } 19 | 20 | bool hid::IsInputAllowed() 21 | { 22 | return GameWindow::s_isFocused || Config::AllowBackgroundInput; 23 | } 24 | 25 | bool hid::IsInputDeviceController() 26 | { 27 | return hid::g_inputDevice != hid::EInputDevice::Keyboard && 28 | hid::g_inputDevice != hid::EInputDevice::Mouse; 29 | } 30 | -------------------------------------------------------------------------------- /UnleashedRecomp/hid/hid.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace hid 4 | { 5 | enum class EInputDevice 6 | { 7 | Unknown, 8 | Keyboard, 9 | Mouse, 10 | Xbox, 11 | PlayStation 12 | }; 13 | 14 | enum class EInputDeviceExplicit 15 | { 16 | Unknown, 17 | Xbox360, 18 | XboxOne, 19 | DualShock3, 20 | DualShock4, 21 | SwitchPro, 22 | Virtual, 23 | DualSense, 24 | Luna, 25 | Stadia, 26 | NvShield, 27 | SwitchJCLeft, 28 | SwitchJCRight, 29 | SwitchJCPair 30 | }; 31 | 32 | extern EInputDevice g_inputDevice; 33 | extern EInputDevice g_inputDeviceController; 34 | extern EInputDeviceExplicit g_inputDeviceExplicit; 35 | 36 | extern uint16_t g_prohibitedButtons; 37 | extern bool g_isLeftStickProhibited; 38 | extern bool g_isRightStickProhibited; 39 | 40 | void Init(); 41 | 42 | uint32_t GetState(uint32_t dwUserIndex, XAMINPUT_STATE* pState); 43 | uint32_t SetState(uint32_t dwUserIndex, XAMINPUT_VIBRATION* pVibration); 44 | uint32_t GetCapabilities(uint32_t dwUserIndex, XAMINPUT_CAPABILITIES* pCaps); 45 | 46 | void SetProhibitedInputs(uint16_t wButtons = 0, bool leftStick = false, bool rightStick = false); 47 | bool IsInputAllowed(); 48 | bool IsInputDeviceController(); 49 | } 50 | -------------------------------------------------------------------------------- /UnleashedRecomp/install/directory_file_system.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "virtual_file_system.h" 6 | 7 | struct DirectoryFileSystem : VirtualFileSystem 8 | { 9 | std::filesystem::path directoryPath; 10 | std::string name; 11 | 12 | DirectoryFileSystem(const std::filesystem::path &directoryPath) 13 | { 14 | this->directoryPath = directoryPath; 15 | name = (const char *)(directoryPath.filename().u8string().data()); 16 | } 17 | 18 | bool load(const std::string &path, uint8_t *fileData, size_t fileDataMaxByteCount) const override 19 | { 20 | std::ifstream fileStream(directoryPath / std::filesystem::path(std::u8string_view((const char8_t *)(path.c_str()))), std::ios::binary); 21 | if (fileStream.is_open()) 22 | { 23 | fileStream.read((char *)(fileData), fileDataMaxByteCount); 24 | return !fileStream.bad(); 25 | } 26 | else 27 | { 28 | return false; 29 | } 30 | } 31 | 32 | size_t getSize(const std::string &path) const override 33 | { 34 | std::error_code ec; 35 | size_t fileSize = std::filesystem::file_size(directoryPath / std::filesystem::path(std::u8string_view((const char8_t *)(path.c_str()))), ec); 36 | if (!ec) 37 | { 38 | return fileSize; 39 | } 40 | else 41 | { 42 | return 0; 43 | } 44 | } 45 | 46 | bool exists(const std::string &path) const override 47 | { 48 | if (path.empty()) 49 | { 50 | return false; 51 | } 52 | 53 | return std::filesystem::exists(directoryPath / std::filesystem::path(std::u8string_view((const char8_t *)(path.c_str())))); 54 | } 55 | 56 | const std::string &getName() const override 57 | { 58 | return name; 59 | } 60 | 61 | static std::unique_ptr create(const std::filesystem::path &directoryPath) 62 | { 63 | if (std::filesystem::exists(directoryPath)) 64 | { 65 | return std::make_unique(directoryPath); 66 | } 67 | else 68 | { 69 | return nullptr; 70 | } 71 | } 72 | }; 73 | -------------------------------------------------------------------------------- /UnleashedRecomp/install/hashes/apotos_shamar.h: -------------------------------------------------------------------------------- 1 | // File automatically generated by fshasher 2 | 3 | #pragma once 4 | 5 | #include 6 | 7 | extern const uint64_t ApotosShamarHashes[]; 8 | extern const std::pair ApotosShamarFiles[]; 9 | extern const size_t ApotosShamarFilesSize; 10 | 11 | -------------------------------------------------------------------------------- /UnleashedRecomp/install/hashes/chunnan.h: -------------------------------------------------------------------------------- 1 | // File automatically generated by fshasher 2 | 3 | #pragma once 4 | 5 | #include 6 | 7 | extern const uint64_t ChunnanHashes[]; 8 | extern const std::pair ChunnanFiles[]; 9 | extern const size_t ChunnanFilesSize; 10 | 11 | -------------------------------------------------------------------------------- /UnleashedRecomp/install/hashes/empire_city_adabat.h: -------------------------------------------------------------------------------- 1 | // File automatically generated by fshasher 2 | 3 | #pragma once 4 | 5 | #include 6 | 7 | extern const uint64_t EmpireCityAdabatHashes[]; 8 | extern const std::pair EmpireCityAdabatFiles[]; 9 | extern const size_t EmpireCityAdabatFilesSize; 10 | 11 | -------------------------------------------------------------------------------- /UnleashedRecomp/install/hashes/game.h: -------------------------------------------------------------------------------- 1 | // File automatically generated by fshasher 2 | 3 | #pragma once 4 | 5 | #include 6 | 7 | extern const uint64_t GameHashes[]; 8 | extern const std::pair GameFiles[]; 9 | extern const size_t GameFilesSize; 10 | 11 | -------------------------------------------------------------------------------- /UnleashedRecomp/install/hashes/holoska.h: -------------------------------------------------------------------------------- 1 | // File automatically generated by fshasher 2 | 3 | #pragma once 4 | 5 | #include 6 | 7 | extern const uint64_t HoloskaHashes[]; 8 | extern const std::pair HoloskaFiles[]; 9 | extern const size_t HoloskaFilesSize; 10 | 11 | -------------------------------------------------------------------------------- /UnleashedRecomp/install/hashes/mazuri.h: -------------------------------------------------------------------------------- 1 | // File automatically generated by fshasher 2 | 3 | #pragma once 4 | 5 | #include 6 | 7 | extern const uint64_t MazuriHashes[]; 8 | extern const std::pair MazuriFiles[]; 9 | extern const size_t MazuriFilesSize; 10 | 11 | -------------------------------------------------------------------------------- /UnleashedRecomp/install/hashes/spagonia.h: -------------------------------------------------------------------------------- 1 | // File automatically generated by fshasher 2 | 3 | #pragma once 4 | 5 | #include 6 | 7 | extern const uint64_t SpagoniaHashes[]; 8 | extern const std::pair SpagoniaFiles[]; 9 | extern const size_t SpagoniaFilesSize; 10 | 11 | -------------------------------------------------------------------------------- /UnleashedRecomp/install/hashes/update.cpp: -------------------------------------------------------------------------------- 1 | // File automatically generated by fshasher 2 | 3 | #include 4 | 5 | extern const uint64_t UpdateHashes[]; 6 | extern const std::pair UpdateFiles[]; 7 | extern const size_t UpdateFilesSize; 8 | 9 | const uint64_t UpdateHashes[] = { 10 | 6914273463875662709ULL, 11 | 15542186142639918255ULL, 12 | 17773094197787397017ULL, 13 | 4145621795357905881ULL, 14 | 5694064368761413534ULL, 15 | 10660633045276223515ULL, 16 | 6259845327508088719ULL, 17 | 12140399948272279979ULL, 18 | 7418638713822288133ULL, 19 | 14117267789244251538ULL, 20 | 15188168166630393460ULL, 21 | 3161308809281010162ULL, 22 | 5322614488283725236ULL, 23 | 4606314145682200491ULL, 24 | 11955725616840921688ULL, 25 | 11797277641169528627ULL, 26 | 16412181293791116322ULL, 27 | 11038823378333165548ULL, 28 | 5479982606338303627ULL, 29 | 868771203788675788ULL, 30 | 17001824000099573976ULL, 31 | 9159616851251538481ULL, 32 | 15568568481517853886ULL, 33 | 10521442355407577670ULL, 34 | 9513615512117450320ULL, 35 | 4920031480332452542ULL, 36 | 17152633532642839320ULL, 37 | 1430448682009298272ULL, 38 | 2819842435698853770ULL, 39 | 1430448682009298272ULL, 40 | 2819842435698853770ULL, 41 | 13038055622504309783ULL, 42 | 1430448682009298272ULL, 43 | 2819842435698853770ULL, 44 | 3623875508130760747ULL, 45 | 1400141374194792546ULL, 46 | 3560111161029067800ULL, 47 | 2610060740632518377ULL, 48 | 2610060740632518377ULL, 49 | }; 50 | 51 | const std::pair UpdateFiles[] = { 52 | { "default.xexp", 4 }, 53 | { "work/ActD_MykonosAct1/Base.set.xml", 1 }, 54 | { "work/ActD_NY/Mission_NYCity_S20_10.set.xml", 1 }, 55 | { "work/ActD_SubEU_01/Set2.set.xml", 1 }, 56 | { "work/ActD_SubNY_01/Set.set.xml", 1 }, 57 | { "work/ActD_SubNY_01/Stage.stg.xml", 1 }, 58 | { "work/ActD_SubNY_01/ny_sub_path.path.xml", 1 }, 59 | { "work/ActD_SubSnow_01/Sub_Layer1_Set.set.xml", 1 }, 60 | { "work/ActD_SubSnow_01/Sub_Layer2_Set.set.xml", 1 }, 61 | { "work/ActD_SubSnow_01/Sub_Layer3_Set.set.xml", 1 }, 62 | { "work/ActN_BeachEvil/evl_sea_obj_st_waterCircle.model", 1 }, 63 | { "work/ActN_BeachEvil/sea_water_circle.material", 1 }, 64 | { "work/ActN_ChinaEvil/area06_enemyset.set.xml", 1 }, 65 | { "work/ActN_ChinaEvil/area07_enemyset.set.xml", 1 }, 66 | { "work/ActN_ChinaEvil/system.set.xml", 1 }, 67 | { "work/ActN_EUEvil/area16_gimmickset.set.xml", 1 }, 68 | { "work/ActN_EUEvil/area18_gimmickset.set.xml", 1 }, 69 | { "work/ActN_EUEvil/system.set.xml", 1 }, 70 | { "work/ActN_PetraEvil/system.set.xml", 1 }, 71 | { "work/ActN_SubChina_01/area01_enemyset.set.xml", 1 }, 72 | { "work/ActN_SubChina_01/area01_gimmickset.set.xml", 1 }, 73 | { "work/Act_EggmanLand/BaseEvil.set.xml", 1 }, 74 | { "work/Act_EggmanLand/system.set.xml", 1 }, 75 | { "work/Application/SR_AdjustTownState.seq.xml", 1 }, 76 | { "work/CmnTown_Mykonos/myk_obj_soc_paperboxABC.dds", 1 }, 77 | { "work/CmnTown_Snow/snw_obj_snowman04_dif.dds", 1 }, 78 | { "work/EvilActionCommon_Mykonos/myk_obj_soc_paperboxABC.dds", 1 }, 79 | { "work/EvilActionCommon_Snow/snw_obj_snowman04_dif.dds", 1 }, 80 | { "work/Inspire/scene/evrt_m5_02/evrt_m5_02.inspire_resource.xml", 1 }, 81 | { "work/SonicActionCommon_Mykonos/myk_obj_soc_paperboxABC.dds", 1 }, 82 | { "work/SonicActionCommon_Snow/snw_obj_snowman04_dif.dds", 1 }, 83 | { "work/Title/mat_mainmenu_common_001.dds", 1 }, 84 | { "work/Town_EuropeanCity_Dispel/Mission_EuropeanCity_S30_10.set.xml", 1 }, 85 | { "work/Town_NYCity_Dispel/Mission_MoveMission_S20_10.set.xml", 1 }, 86 | { "work/Town_SouthEastAsiaETF/CommonOBJ.set.xml", 1 }, 87 | { "work/Town_SouthEastAsiaETF_Night/CommonOBJ.set.xml", 1 }, 88 | }; 89 | 90 | const size_t UpdateFilesSize = std::size(UpdateFiles); 91 | -------------------------------------------------------------------------------- /UnleashedRecomp/install/hashes/update.h: -------------------------------------------------------------------------------- 1 | // File automatically generated by fshasher 2 | 3 | #pragma once 4 | 5 | #include 6 | 7 | extern const uint64_t UpdateHashes[]; 8 | extern const std::pair UpdateFiles[]; 9 | extern const size_t UpdateFilesSize; 10 | 11 | -------------------------------------------------------------------------------- /UnleashedRecomp/install/installer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "virtual_file_system.h" 7 | #include 8 | 9 | enum class DLC { 10 | Unknown, 11 | Spagonia, 12 | Chunnan, 13 | Mazuri, 14 | Holoska, 15 | ApotosShamar, 16 | EmpireCityAdabat, 17 | Count = EmpireCityAdabat 18 | }; 19 | 20 | struct Journal 21 | { 22 | enum class Result 23 | { 24 | Success, 25 | Cancelled, 26 | VirtualFileSystemFailed, 27 | DirectoryCreationFailed, 28 | FileMissing, 29 | FileReadFailed, 30 | FileHashFailed, 31 | FileCreationFailed, 32 | FileWriteFailed, 33 | ValidationFileMissing, 34 | DLCParsingFailed, 35 | PatchProcessFailed, 36 | PatchReplacementFailed, 37 | UnknownDLCType 38 | }; 39 | 40 | uint64_t progressCounter = 0; 41 | uint64_t progressTotal = 0; 42 | std::list createdFiles; 43 | std::set createdDirectories; 44 | Result lastResult = Result::Success; 45 | XexPatcher::Result lastPatcherResult = XexPatcher::Result::Success; 46 | std::string lastErrorMessage; 47 | }; 48 | 49 | using FilePair = std::pair; 50 | 51 | struct Installer 52 | { 53 | struct Input 54 | { 55 | std::filesystem::path gameSource; 56 | std::filesystem::path updateSource; 57 | std::list dlcSources; 58 | }; 59 | 60 | struct DLCSource { 61 | std::unique_ptr sourceVfs; 62 | std::span filePairs; 63 | const uint64_t *fileHashes = nullptr; 64 | std::string targetSubDirectory; 65 | }; 66 | 67 | struct Sources 68 | { 69 | std::unique_ptr game; 70 | std::unique_ptr update; 71 | std::vector dlc; 72 | uint64_t totalSize = 0; 73 | }; 74 | 75 | static bool checkGameInstall(const std::filesystem::path &baseDirectory, std::filesystem::path &modulePath); 76 | static bool checkDLCInstall(const std::filesystem::path &baseDirectory, DLC dlc); 77 | static bool checkAllDLC(const std::filesystem::path &baseDirectory); 78 | static bool checkInstallIntegrity(const std::filesystem::path &baseDirectory, Journal &journal, const std::function &progressCallback); 79 | static bool computeTotalSize(std::span filePairs, const uint64_t *fileHashes, VirtualFileSystem &sourceVfs, Journal &journal, uint64_t &totalSize); 80 | static bool checkFiles(std::span filePairs, const uint64_t *fileHashes, const std::filesystem::path &targetDirectory, Journal &journal, const std::function &progressCallback, bool checkSizeOnly); 81 | static bool copyFiles(std::span filePairs, const uint64_t *fileHashes, VirtualFileSystem &sourceVfs, const std::filesystem::path &targetDirectory, const std::string &validationFile, bool skipHashChecks, Journal &journal, const std::function &progressCallback); 82 | static bool parseContent(const std::filesystem::path &sourcePath, std::unique_ptr &targetVfs, Journal &journal); 83 | static bool parseSources(const Input &input, Journal &journal, Sources &sources); 84 | static bool install(const Sources &sources, const std::filesystem::path &targetDirectory, bool skipHashChecks, Journal &journal, std::chrono::seconds endWaitTime, const std::function &progressCallback); 85 | static void rollback(Journal &journal); 86 | 87 | // Convenience method for checking if the specified file contains the game. This should be used when the user selects the file. 88 | static bool parseGame(const std::filesystem::path &sourcePath); 89 | 90 | // Convenience method for checking if the specified file contains the update. This should be used when the user selects the file. 91 | static bool parseUpdate(const std::filesystem::path &sourcePath); 92 | 93 | // Convenience method for the installer to check which DLC the file that was specified corresponds to. This should be used when the user selects the file. 94 | static DLC parseDLC(const std::filesystem::path &sourcePath); 95 | 96 | // Convenience method for checking if a game and an update are compatible. This should be used when the user presses next during installation. 97 | static XexPatcher::Result checkGameUpdateCompatibility(const std::filesystem::path &gameSourcePath, const std::filesystem::path &updateSourcePath); 98 | }; 99 | -------------------------------------------------------------------------------- /UnleashedRecomp/install/iso_file_system.h: -------------------------------------------------------------------------------- 1 | // Referenced from: https://github.com/xenia-canary/xenia-canary/blob/canary_experimental/src/xenia/vfs/devices/disc_image_device.cc 2 | 3 | /** 4 | ****************************************************************************** 5 | * Xenia : Xbox 360 Emulator Research Project * 6 | ****************************************************************************** 7 | * Copyright 2023 Ben Vanik. All rights reserved. * 8 | * Released under the BSD license - see LICENSE in the root for more details. * 9 | ****************************************************************************** 10 | */ 11 | 12 | #pragma once 13 | 14 | #include 15 | #include 16 | 17 | #include "virtual_file_system.h" 18 | 19 | #include 20 | 21 | struct ISOFileSystem : VirtualFileSystem 22 | { 23 | MemoryMappedFile mappedFile; 24 | std::map> fileMap; 25 | std::string name; 26 | 27 | ISOFileSystem(const std::filesystem::path &isoPath); 28 | bool load(const std::string &path, uint8_t *fileData, size_t fileDataMaxByteCount) const override; 29 | size_t getSize(const std::string &path) const override; 30 | bool exists(const std::string &path) const override; 31 | const std::string &getName() const override; 32 | bool empty() const; 33 | 34 | static std::unique_ptr create(const std::filesystem::path &isoPath); 35 | }; 36 | -------------------------------------------------------------------------------- /UnleashedRecomp/install/update_checker.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct UpdateChecker 4 | { 5 | enum class Result 6 | { 7 | NotStarted, 8 | InProgress, 9 | AlreadyUpToDate, 10 | UpdateAvailable, 11 | Failed 12 | }; 13 | 14 | static void initialize(); 15 | static bool start(); 16 | static Result check(); 17 | static void visitWebsite(); 18 | }; 19 | -------------------------------------------------------------------------------- /UnleashedRecomp/install/virtual_file_system.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct VirtualFileSystem { 7 | virtual ~VirtualFileSystem() { }; 8 | virtual bool load(const std::string &path, uint8_t *fileData, size_t fileDataMaxByteCount) const = 0; 9 | virtual size_t getSize(const std::string &path) const = 0; 10 | virtual bool exists(const std::string &path) const = 0; 11 | virtual const std::string &getName() const = 0; 12 | 13 | // Concrete implementation shortcut. 14 | bool load(const std::string &path, std::vector &fileData) 15 | { 16 | size_t fileDataSize = getSize(path); 17 | if (fileDataSize == 0) 18 | { 19 | return false; 20 | } 21 | 22 | fileData.resize(fileDataSize); 23 | return load(path, fileData.data(), fileDataSize); 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /UnleashedRecomp/install/xcontent_file_system.h: -------------------------------------------------------------------------------- 1 | // Referenced from: https://github.com/xenia-canary/xenia-canary/blob/canary_experimental/src/xenia/vfs/devices/xcontent_container_device.cc 2 | 3 | /** 4 | ****************************************************************************** 5 | * Xenia : Xbox 360 Emulator Research Project * 6 | ****************************************************************************** 7 | * Copyright 2023 Ben Vanik. All rights reserved. * 8 | * Released under the BSD license - see LICENSE in the root for more details. * 9 | ****************************************************************************** 10 | */ 11 | 12 | #pragma once 13 | 14 | #include 15 | #include 16 | 17 | #include "virtual_file_system.h" 18 | 19 | #include 20 | 21 | enum class XContentVolumeType 22 | { 23 | STFS = 0, 24 | SVOD = 1, 25 | }; 26 | 27 | enum class SvodLayoutType 28 | { 29 | Unknown = 0x0, 30 | EnhancedGDF = 0x1, 31 | XSF = 0x2, 32 | SingleFile = 0x4, 33 | }; 34 | 35 | struct XContentFileSystem : VirtualFileSystem 36 | { 37 | struct File 38 | { 39 | size_t size = 0; 40 | uint32_t blockIndex = 0; 41 | uint32_t blockCount = 0; 42 | }; 43 | 44 | XContentVolumeType volumeType = XContentVolumeType::STFS; 45 | SvodLayoutType svodLayoutType = SvodLayoutType::Unknown; 46 | size_t svodStartDataBlock = 0; 47 | size_t svodBaseOffset = 0; 48 | size_t svodMagicOffset = 0; 49 | std::vector mappedFiles; 50 | uint64_t baseOffset = 0; 51 | std::map fileMap; 52 | std::string name; 53 | 54 | XContentFileSystem(const std::filesystem::path &contentPath); 55 | bool load(const std::string &path, uint8_t *fileData, size_t fileDataMaxByteCount) const override; 56 | size_t getSize(const std::string &path) const override; 57 | bool exists(const std::string &path) const override; 58 | const std::string &getName() const override; 59 | bool empty() const; 60 | 61 | static std::unique_ptr create(const std::filesystem::path &contentPath); 62 | static bool check(const std::filesystem::path &contentPath); 63 | }; 64 | -------------------------------------------------------------------------------- /UnleashedRecomp/kernel/freelist.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | template 4 | struct FreeList 5 | { 6 | std::vector items; 7 | std::vector freed{}; 8 | 9 | void Free(T& item) 10 | { 11 | std::destroy_at(&item); 12 | freed.push_back(&item - items.data()); 13 | } 14 | 15 | void Free(size_t index) 16 | { 17 | std::destroy_at(&items[index]); 18 | freed.push_back(index); 19 | } 20 | 21 | size_t Alloc() 22 | { 23 | if (freed.size()) 24 | { 25 | auto idx = freed[freed.size() - 1]; 26 | freed.pop_back(); 27 | 28 | std::construct_at(&items[idx]); 29 | return idx; 30 | } 31 | 32 | items.emplace_back(); 33 | return items.size() - 1; 34 | } 35 | 36 | T& operator[](size_t idx) 37 | { 38 | return items[idx]; 39 | } 40 | }; -------------------------------------------------------------------------------- /UnleashedRecomp/kernel/heap.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "heap.h" 3 | #include "memory.h" 4 | #include "function.h" 5 | 6 | constexpr size_t RESERVED_BEGIN = 0x7FEA0000; 7 | constexpr size_t RESERVED_END = 0xA0000000; 8 | 9 | void Heap::Init() 10 | { 11 | heap = o1heapInit(g_memory.Translate(0x20000), RESERVED_BEGIN - 0x20000); 12 | physicalHeap = o1heapInit(g_memory.Translate(RESERVED_END), 0x100000000 - RESERVED_END); 13 | } 14 | 15 | void* Heap::Alloc(size_t size) 16 | { 17 | std::lock_guard lock(mutex); 18 | 19 | return o1heapAllocate(heap, std::max(1, size)); 20 | } 21 | 22 | void* Heap::AllocPhysical(size_t size, size_t alignment) 23 | { 24 | size = std::max(1, size); 25 | alignment = alignment == 0 ? 0x1000 : std::max(16, alignment); 26 | 27 | std::lock_guard lock(physicalMutex); 28 | 29 | void* ptr = o1heapAllocate(physicalHeap, size + alignment); 30 | size_t aligned = ((size_t)ptr + alignment) & ~(alignment - 1); 31 | 32 | *((void**)aligned - 1) = ptr; 33 | *((size_t*)aligned - 2) = size + O1HEAP_ALIGNMENT; 34 | 35 | return (void*)aligned; 36 | } 37 | 38 | void Heap::Free(void* ptr) 39 | { 40 | if (ptr >= physicalHeap) 41 | { 42 | std::lock_guard lock(physicalMutex); 43 | o1heapFree(physicalHeap, *((void**)ptr - 1)); 44 | } 45 | else 46 | { 47 | std::lock_guard lock(mutex); 48 | o1heapFree(heap, ptr); 49 | } 50 | } 51 | 52 | size_t Heap::Size(void* ptr) 53 | { 54 | if (ptr) 55 | return *((size_t*)ptr - 2) - O1HEAP_ALIGNMENT; // relies on fragment header in o1heap.c 56 | 57 | return 0; 58 | } 59 | 60 | uint32_t RtlAllocateHeap(uint32_t heapHandle, uint32_t flags, uint32_t size) 61 | { 62 | void* ptr = g_userHeap.Alloc(size); 63 | if ((flags & 0x8) != 0) 64 | memset(ptr, 0, size); 65 | 66 | assert(ptr); 67 | return g_memory.MapVirtual(ptr); 68 | } 69 | 70 | uint32_t RtlReAllocateHeap(uint32_t heapHandle, uint32_t flags, uint32_t memoryPointer, uint32_t size) 71 | { 72 | void* ptr = g_userHeap.Alloc(size); 73 | if ((flags & 0x8) != 0) 74 | memset(ptr, 0, size); 75 | 76 | if (memoryPointer != 0) 77 | { 78 | void* oldPtr = g_memory.Translate(memoryPointer); 79 | memcpy(ptr, oldPtr, std::min(size, g_userHeap.Size(oldPtr))); 80 | g_userHeap.Free(oldPtr); 81 | } 82 | 83 | assert(ptr); 84 | return g_memory.MapVirtual(ptr); 85 | } 86 | 87 | uint32_t RtlFreeHeap(uint32_t heapHandle, uint32_t flags, uint32_t memoryPointer) 88 | { 89 | if (memoryPointer != NULL) 90 | g_userHeap.Free(g_memory.Translate(memoryPointer)); 91 | 92 | return true; 93 | } 94 | 95 | uint32_t RtlSizeHeap(uint32_t heapHandle, uint32_t flags, uint32_t memoryPointer) 96 | { 97 | if (memoryPointer != NULL) 98 | return (uint32_t)g_userHeap.Size(g_memory.Translate(memoryPointer)); 99 | 100 | return 0; 101 | } 102 | 103 | uint32_t XAllocMem(uint32_t size, uint32_t flags) 104 | { 105 | void* ptr = (flags & 0x80000000) != 0 ? 106 | g_userHeap.AllocPhysical(size, (1ull << ((flags >> 24) & 0xF))) : 107 | g_userHeap.Alloc(size); 108 | 109 | if ((flags & 0x40000000) != 0) 110 | memset(ptr, 0, size); 111 | 112 | assert(ptr); 113 | return g_memory.MapVirtual(ptr); 114 | } 115 | 116 | void XFreeMem(uint32_t baseAddress, uint32_t flags) 117 | { 118 | if (baseAddress != NULL) 119 | g_userHeap.Free(g_memory.Translate(baseAddress)); 120 | } 121 | 122 | GUEST_FUNCTION_STUB(sub_82BD7788); // HeapCreate 123 | GUEST_FUNCTION_STUB(sub_82BD9250); // HeapDestroy 124 | 125 | GUEST_FUNCTION_HOOK(sub_82BD7D30, RtlAllocateHeap); 126 | GUEST_FUNCTION_HOOK(sub_82BD8600, RtlFreeHeap); 127 | GUEST_FUNCTION_HOOK(sub_82BD88F0, RtlReAllocateHeap); 128 | GUEST_FUNCTION_HOOK(sub_82BD6FD0, RtlSizeHeap); 129 | 130 | GUEST_FUNCTION_HOOK(sub_831CC9C8, XAllocMem); 131 | GUEST_FUNCTION_HOOK(sub_831CCA60, XFreeMem); 132 | -------------------------------------------------------------------------------- /UnleashedRecomp/kernel/heap.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "mutex.h" 4 | 5 | struct Heap 6 | { 7 | Mutex mutex; 8 | O1HeapInstance* heap; 9 | 10 | Mutex physicalMutex; 11 | O1HeapInstance* physicalHeap; 12 | 13 | void Init(); 14 | 15 | void* Alloc(size_t size); 16 | void* AllocPhysical(size_t size, size_t alignment); 17 | void Free(void* ptr); 18 | 19 | size_t Size(void* ptr); 20 | 21 | template 22 | T* Alloc(Args&&... args) 23 | { 24 | T* obj = (T*)Alloc(sizeof(T)); 25 | new (obj) T(std::forward(args)...); 26 | return obj; 27 | } 28 | 29 | template 30 | T* AllocPhysical(Args&&... args) 31 | { 32 | T* obj = (T*)AllocPhysical(sizeof(T), alignof(T)); 33 | new (obj) T(std::forward(args)...); 34 | return obj; 35 | } 36 | }; 37 | 38 | extern Heap g_userHeap; 39 | -------------------------------------------------------------------------------- /UnleashedRecomp/kernel/io/file_system.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct FileSystem 4 | { 5 | static std::filesystem::path ResolvePath(const std::string_view& path, bool checkForMods); 6 | }; 7 | -------------------------------------------------------------------------------- /UnleashedRecomp/kernel/memory.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "memory.h" 3 | 4 | Memory::Memory() 5 | { 6 | #ifdef _WIN32 7 | base = (uint8_t*)VirtualAlloc((void*)0x100000000ull, PPC_MEMORY_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); 8 | 9 | if (base == nullptr) 10 | base = (uint8_t*)VirtualAlloc(nullptr, PPC_MEMORY_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); 11 | 12 | if (base == nullptr) 13 | return; 14 | 15 | DWORD oldProtect; 16 | VirtualProtect(base, 4096, PAGE_NOACCESS, &oldProtect); 17 | #else 18 | base = (uint8_t*)mmap((void*)0x100000000ull, PPC_MEMORY_SIZE, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); 19 | 20 | if (base == (uint8_t*)MAP_FAILED) 21 | base = (uint8_t*)mmap(NULL, PPC_MEMORY_SIZE, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); 22 | 23 | if (base == nullptr) 24 | return; 25 | 26 | mprotect(base, 4096, PROT_NONE); 27 | #endif 28 | 29 | for (size_t i = 0; PPCFuncMappings[i].guest != 0; i++) 30 | { 31 | if (PPCFuncMappings[i].host != nullptr) 32 | InsertFunction(PPCFuncMappings[i].guest, PPCFuncMappings[i].host); 33 | } 34 | } 35 | 36 | void* MmGetHostAddress(uint32_t ptr) 37 | { 38 | return g_memory.Translate(ptr); 39 | } 40 | -------------------------------------------------------------------------------- /UnleashedRecomp/kernel/memory.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef _WIN32 4 | #define MEM_COMMIT 0x00001000 5 | #define MEM_RESERVE 0x00002000 6 | #endif 7 | 8 | struct Memory 9 | { 10 | uint8_t* base{}; 11 | 12 | Memory(); 13 | 14 | bool IsInMemoryRange(const void* host) const noexcept 15 | { 16 | return host >= base && host < (base + PPC_MEMORY_SIZE); 17 | } 18 | 19 | void* Translate(size_t offset) const noexcept 20 | { 21 | if (offset) 22 | assert(offset < PPC_MEMORY_SIZE); 23 | 24 | return base + offset; 25 | } 26 | 27 | uint32_t MapVirtual(const void* host) const noexcept 28 | { 29 | if (host) 30 | assert(IsInMemoryRange(host)); 31 | 32 | return static_cast(static_cast(host) - base); 33 | } 34 | 35 | PPCFunc* FindFunction(uint32_t guest) const noexcept 36 | { 37 | return PPC_LOOKUP_FUNC(base, guest); 38 | } 39 | 40 | void InsertFunction(uint32_t guest, PPCFunc* host) 41 | { 42 | PPC_LOOKUP_FUNC(base, guest) = host; 43 | } 44 | }; 45 | 46 | extern "C" void* MmGetHostAddress(uint32_t ptr); 47 | extern Memory g_memory; 48 | -------------------------------------------------------------------------------- /UnleashedRecomp/kernel/xam.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #define MSGID(Area, Number) (uint32_t)((uint16_t)(Area) << 16 | (uint16_t)(Number)) 5 | #define MSG_AREA(msgid) (((msgid) >> 16) & 0xFFFF) 6 | #define MSG_NUMBER(msgid) ((msgid) & 0xFFFF) 7 | 8 | XCONTENT_DATA XamMakeContent(uint32_t type, const std::string_view& name); 9 | void XamRegisterContent(const XCONTENT_DATA& data, const std::string_view& root); 10 | 11 | std::string_view XamGetRootPath(const std::string_view& root); 12 | void XamRootCreate(const std::string_view& root, const std::string_view& path); 13 | 14 | uint32_t XamNotifyCreateListener(uint64_t qwAreas); 15 | void XamNotifyEnqueueEvent(uint32_t dwId, uint32_t dwParam); // i made it the fuck up 16 | bool XNotifyGetNext(uint32_t hNotification, uint32_t dwMsgFilter, be* pdwId, be* pParam); 17 | 18 | uint32_t XamShowMessageBoxUI(uint32_t dwUserIndex, be* wszTitle, be* wszText, uint32_t cButtons, 19 | xpointer>* pwszButtons, uint32_t dwFocusButton, uint32_t dwFlags, be* pResult, XXOVERLAPPED* pOverlapped); 20 | 21 | uint32_t XamContentCreateEnumerator(uint32_t dwUserIndex, uint32_t DeviceID, uint32_t dwContentType, 22 | uint32_t dwContentFlags, uint32_t cItem, be* pcbBuffer, be* phEnum); 23 | uint32_t XamEnumerate(uint32_t hEnum, uint32_t dwFlags, void* pvBuffer, uint32_t cbBuffer, be* pcItemsReturned, XXOVERLAPPED* pOverlapped); 24 | 25 | uint32_t XamContentCreateEx(uint32_t dwUserIndex, const char* szRootName, const XCONTENT_DATA* pContentData, 26 | uint32_t dwContentFlags, be* pdwDisposition, be* pdwLicenseMask, 27 | uint32_t dwFileCacheSize, uint64_t uliContentSize, PXXOVERLAPPED pOverlapped); 28 | uint32_t XamContentGetDeviceData(uint32_t DeviceID, XDEVICE_DATA* pDeviceData); 29 | uint32_t XamContentClose(const char* szRootName, XXOVERLAPPED* pOverlapped); 30 | 31 | uint32_t XamInputGetCapabilities(uint32_t unk, uint32_t userIndex, uint32_t flags, XAMINPUT_CAPABILITIES* caps); 32 | uint32_t XamInputGetState(uint32_t userIndex, uint32_t flags, XAMINPUT_STATE* state); 33 | uint32_t XamInputSetState(uint32_t userIndex, uint32_t flags, XAMINPUT_VIBRATION* vibration); 34 | -------------------------------------------------------------------------------- /UnleashedRecomp/kernel/xdbf.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | extern XDBFWrapper g_xdbfWrapper; 7 | extern std::unordered_map g_xdbfTextureCache; 8 | 9 | namespace xdbf 10 | { 11 | inline std::string& FixInvalidSequences(std::string& str) 12 | { 13 | static std::array invalidSequences = 14 | { 15 | "\xE2\x80\x99" 16 | }; 17 | 18 | static std::array replaceSequences = 19 | { 20 | "'" 21 | }; 22 | 23 | for (int i = 0; i < invalidSequences.size(); i++) 24 | { 25 | size_t pos = 0; 26 | 27 | auto& invalidSeq = invalidSequences[i]; 28 | auto& replaceSeq = replaceSequences[i]; 29 | 30 | while ((pos = str.find(invalidSequences[i], pos)) != std::string::npos) 31 | { 32 | str = str.replace(pos, invalidSeq.length(), replaceSeq); 33 | 34 | pos += replaceSeq.length(); 35 | } 36 | } 37 | 38 | return str; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /UnleashedRecomp/kernel/xdm.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "xdm.h" 3 | #include "freelist.h" 4 | 5 | Mutex g_kernelLock; 6 | 7 | void DestroyKernelObject(KernelObject* obj) 8 | { 9 | obj->~KernelObject(); 10 | g_userHeap.Free(obj); 11 | } 12 | 13 | uint32_t GetKernelHandle(KernelObject* obj) 14 | { 15 | assert(obj != GetInvalidKernelObject()); 16 | return g_memory.MapVirtual(obj); 17 | } 18 | 19 | void DestroyKernelObject(uint32_t handle) 20 | { 21 | DestroyKernelObject(GetKernelObject(handle)); 22 | } 23 | 24 | bool IsKernelObject(uint32_t handle) 25 | { 26 | return (handle & 0x80000000) != 0; 27 | } 28 | 29 | bool IsKernelObject(void* obj) 30 | { 31 | return IsKernelObject(g_memory.MapVirtual(obj)); 32 | } 33 | 34 | bool IsInvalidKernelObject(void* obj) 35 | { 36 | return obj == GetInvalidKernelObject(); 37 | } 38 | -------------------------------------------------------------------------------- /UnleashedRecomp/kernel/xdm.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "heap.h" 4 | #include "memory.h" 5 | 6 | #define OBJECT_SIGNATURE (uint32_t)'XBOX' 7 | #define GUEST_INVALID_HANDLE_VALUE 0xFFFFFFFF 8 | 9 | #ifndef _WIN32 10 | 11 | #define S_OK 0x00000000 12 | #define FALSE 0x00000000 13 | #define TRUE 0x00000001 14 | #define STATUS_SUCCESS 0x00000000 15 | #define STATUS_WAIT_0 0x00000000 16 | #define STATUS_USER_APC 0x000000C0 17 | #define STATUS_TIMEOUT 0x00000102 18 | #define STATUS_FAIL_CHECK 0xC0000229 19 | #define INFINITE 0xFFFFFFFF 20 | #define FILE_ATTRIBUTE_DIRECTORY 0x00000010 21 | #define FILE_ATTRIBUTE_NORMAL 0x00000080 22 | #define GENERIC_READ 0x80000000 23 | #define GENERIC_WRITE 0x40000000 24 | #define FILE_READ_DATA 0x0001 25 | #define FILE_SHARE_READ 0x00000001 26 | #define FILE_SHARE_WRITE 0x00000002 27 | #define CREATE_NEW 1 28 | #define CREATE_ALWAYS 2 29 | #define OPEN_EXISTING 3 30 | #define INVALID_FILE_SIZE 0xFFFFFFFF 31 | #define INVALID_SET_FILE_POINTER 0xFFFFFFFF 32 | #define INVALID_FILE_ATTRIBUTES 0xFFFFFFFF 33 | #define FILE_BEGIN 0 34 | #define FILE_CURRENT 1 35 | #define FILE_END 2 36 | #define ERROR_NO_MORE_FILES 0x12 37 | #define ERROR_NO_SUCH_USER 0x525 38 | #define ERROR_SUCCESS 0x0 39 | #define ERROR_PATH_NOT_FOUND 0x3 40 | #define ERROR_BAD_ARGUMENTS 0xA0 41 | #define ERROR_DEVICE_NOT_CONNECTED 0x48F 42 | #define PAGE_READWRITE 0x04 43 | 44 | typedef union _LARGE_INTEGER { 45 | struct { 46 | uint32_t LowPart; 47 | int32_t HighPart; 48 | }; 49 | struct { 50 | uint32_t LowPart; 51 | int32_t HighPart; 52 | } u; 53 | int64_t QuadPart; 54 | } LARGE_INTEGER; 55 | 56 | static_assert(sizeof(LARGE_INTEGER) == 8); 57 | 58 | typedef struct _FILETIME 59 | { 60 | uint32_t dwLowDateTime; 61 | uint32_t dwHighDateTime; 62 | } FILETIME; 63 | 64 | static_assert(sizeof(FILETIME) == 8); 65 | 66 | typedef struct _WIN32_FIND_DATAA 67 | { 68 | uint32_t dwFileAttributes; 69 | FILETIME ftCreationTime; 70 | FILETIME ftLastAccessTime; 71 | FILETIME ftLastWriteTime; 72 | uint32_t nFileSizeHigh; 73 | uint32_t nFileSizeLow; 74 | uint32_t dwReserved0; 75 | uint32_t dwReserved1; 76 | char cFileName[260]; 77 | char cAlternateFileName[14]; 78 | } WIN32_FIND_DATAA; 79 | 80 | static_assert(sizeof(WIN32_FIND_DATAA) == 320); 81 | 82 | #endif 83 | 84 | struct KernelObject 85 | { 86 | virtual ~KernelObject() 87 | { 88 | } 89 | 90 | virtual uint32_t Wait(uint32_t timeout) 91 | { 92 | assert(false && "Wait not implemented for this kernel object."); 93 | return STATUS_TIMEOUT; 94 | } 95 | }; 96 | 97 | template 98 | inline T* CreateKernelObject(Args&&... args) 99 | { 100 | static_assert(std::is_base_of_v); 101 | return g_userHeap.AllocPhysical(std::forward(args)...); 102 | } 103 | 104 | template 105 | inline T* GetKernelObject(uint32_t handle) 106 | { 107 | assert(handle != GUEST_INVALID_HANDLE_VALUE); 108 | return reinterpret_cast(g_memory.Translate(handle)); 109 | } 110 | 111 | uint32_t GetKernelHandle(KernelObject* obj); 112 | 113 | void DestroyKernelObject(KernelObject* obj); 114 | void DestroyKernelObject(uint32_t handle); 115 | 116 | bool IsKernelObject(uint32_t handle); 117 | bool IsKernelObject(void* obj); 118 | 119 | bool IsInvalidKernelObject(void* obj); 120 | 121 | template 122 | inline T* GetInvalidKernelObject() 123 | { 124 | return reinterpret_cast(g_memory.Translate(GUEST_INVALID_HANDLE_VALUE)); 125 | } 126 | 127 | extern Mutex g_kernelLock; 128 | 129 | template 130 | inline T* QueryKernelObject(XDISPATCHER_HEADER& header) 131 | { 132 | std::lock_guard guard{ g_kernelLock }; 133 | if (header.WaitListHead.Flink != OBJECT_SIGNATURE) 134 | { 135 | header.WaitListHead.Flink = OBJECT_SIGNATURE; 136 | auto* obj = CreateKernelObject(reinterpret_cast(&header)); 137 | header.WaitListHead.Blink = g_memory.MapVirtual(obj); 138 | 139 | return obj; 140 | } 141 | 142 | return static_cast(g_memory.Translate(header.WaitListHead.Blink.get())); 143 | } 144 | 145 | // Get object without initialisation 146 | template 147 | inline T* TryQueryKernelObject(XDISPATCHER_HEADER& header) 148 | { 149 | if (header.WaitListHead.Flink != OBJECT_SIGNATURE) 150 | return nullptr; 151 | 152 | return static_cast(g_memory.Translate(header.WaitListHead.Blink.get())); 153 | } 154 | -------------------------------------------------------------------------------- /UnleashedRecomp/locale/locale.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | enum class ELanguage : uint32_t 4 | { 5 | English = 1, 6 | Japanese, 7 | German, 8 | French, 9 | Spanish, 10 | Italian 11 | }; 12 | 13 | inline std::string g_localeMissing = ""; 14 | 15 | extern std::unordered_map> g_locale; 16 | 17 | std::string& Localise(const std::string_view& key); 18 | -------------------------------------------------------------------------------- /UnleashedRecomp/misc_impl.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include 3 | #include 4 | 5 | uint32_t QueryPerformanceCounterImpl(LARGE_INTEGER* lpPerformanceCount) 6 | { 7 | lpPerformanceCount->QuadPart = ByteSwap(std::chrono::steady_clock::now().time_since_epoch().count()); 8 | return TRUE; 9 | } 10 | 11 | uint32_t QueryPerformanceFrequencyImpl(LARGE_INTEGER* lpFrequency) 12 | { 13 | constexpr auto Frequency = std::chrono::steady_clock::period::den / std::chrono::steady_clock::period::num; 14 | lpFrequency->QuadPart = ByteSwap(Frequency); 15 | return TRUE; 16 | } 17 | 18 | uint32_t GetTickCountImpl() 19 | { 20 | return uint32_t(std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count()); 21 | } 22 | 23 | void GlobalMemoryStatusImpl(XLPMEMORYSTATUS lpMemoryStatus) 24 | { 25 | lpMemoryStatus->dwLength = sizeof(XMEMORYSTATUS); 26 | lpMemoryStatus->dwMemoryLoad = 0; 27 | lpMemoryStatus->dwTotalPhys = 0x20000000; 28 | lpMemoryStatus->dwAvailPhys = 0x20000000; 29 | lpMemoryStatus->dwTotalPageFile = 0x20000000; 30 | lpMemoryStatus->dwAvailPageFile = 0x20000000; 31 | lpMemoryStatus->dwTotalVirtual = 0x20000000; 32 | lpMemoryStatus->dwAvailVirtual = 0x20000000; 33 | } 34 | 35 | GUEST_FUNCTION_HOOK(sub_831B0ED0, memcpy); 36 | GUEST_FUNCTION_HOOK(sub_831CCB98, memcpy); 37 | GUEST_FUNCTION_HOOK(sub_831CEAE0, memcpy); 38 | GUEST_FUNCTION_HOOK(sub_831CEE04, memcpy); 39 | GUEST_FUNCTION_HOOK(sub_831CF2D0, memcpy); 40 | GUEST_FUNCTION_HOOK(sub_831CF660, memcpy); 41 | GUEST_FUNCTION_HOOK(sub_831B1358, memcpy); 42 | GUEST_FUNCTION_HOOK(sub_831B5E00, memmove); 43 | GUEST_FUNCTION_HOOK(sub_831B0BA0, memset); 44 | GUEST_FUNCTION_HOOK(sub_831CCAA0, memset); 45 | 46 | #ifdef _WIN32 47 | GUEST_FUNCTION_HOOK(sub_82BD4CA8, OutputDebugStringA); 48 | #else 49 | GUEST_FUNCTION_STUB(sub_82BD4CA8); 50 | #endif 51 | 52 | GUEST_FUNCTION_HOOK(sub_82BD4AC8, QueryPerformanceCounterImpl); 53 | GUEST_FUNCTION_HOOK(sub_831CD040, QueryPerformanceFrequencyImpl); 54 | GUEST_FUNCTION_HOOK(sub_831CDAD0, GetTickCountImpl); 55 | 56 | GUEST_FUNCTION_HOOK(sub_82BD4BC0, GlobalMemoryStatusImpl); 57 | 58 | // sprintf 59 | PPC_FUNC(sub_82BD4AE8) 60 | { 61 | sub_831B1630(ctx, base); 62 | } 63 | -------------------------------------------------------------------------------- /UnleashedRecomp/mod/ini_file.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class IniFile 6 | { 7 | protected: 8 | struct Property 9 | { 10 | std::string name; 11 | std::string value; 12 | }; 13 | 14 | struct Section 15 | { 16 | std::string name; 17 | xxHashMap properties; 18 | }; 19 | 20 | xxHashMap
m_sections; 21 | 22 | static size_t hashStr(const std::string_view& str); 23 | 24 | static bool isWhitespace(char value); 25 | static bool isNewLine(char value); 26 | 27 | public: 28 | bool read(const std::filesystem::path& filePath); 29 | 30 | std::string getString(const std::string_view& sectionName, const std::string_view& propertyName, std::string defaultValue) const; 31 | 32 | bool getBool(const std::string_view& sectionName, const std::string_view& propertyName, bool defaultValue) const; 33 | 34 | template 35 | T get(const std::string_view& sectionName, const std::string_view& propertyName, T defaultValue) const; 36 | 37 | template 38 | void enumerate(const T& function) const; 39 | 40 | template 41 | void enumerate(const std::string_view& sectionName, const T& function) const; 42 | 43 | bool contains(const std::string_view& sectionName) const; 44 | }; 45 | 46 | #include "ini_file.inl" 47 | -------------------------------------------------------------------------------- /UnleashedRecomp/mod/mod_loader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct ModLoader 4 | { 5 | static inline bool s_isLogTypeConsole; 6 | 7 | static inline std::filesystem::path s_saveFilePath; 8 | 9 | static std::filesystem::path ResolvePath(std::string_view path); 10 | 11 | static std::vector* GetIncludeDirectories(size_t modIndex); 12 | 13 | static void Init(); 14 | }; 15 | -------------------------------------------------------------------------------- /UnleashedRecomp/mutex.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef _WIN32 4 | 5 | struct Mutex : CRITICAL_SECTION 6 | { 7 | Mutex() 8 | { 9 | InitializeCriticalSection(this); 10 | } 11 | ~Mutex() 12 | { 13 | DeleteCriticalSection(this); 14 | } 15 | 16 | void lock() 17 | { 18 | EnterCriticalSection(this); 19 | } 20 | 21 | void unlock() 22 | { 23 | LeaveCriticalSection(this); 24 | } 25 | }; 26 | 27 | #else 28 | 29 | using Mutex = std::mutex; 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /UnleashedRecomp/natvis.natvis: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {get()} 5 | 6 | get() 7 | 8 | 9 | 10 | {get()} 11 | 12 | get() 13 | 14 | 15 | 16 | {get()} 17 | 18 | get() 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /UnleashedRecomp/os/.gitignore: -------------------------------------------------------------------------------- 1 | ![Ww][Ii][Nn]32/ -------------------------------------------------------------------------------- /UnleashedRecomp/os/linux/logger_linux.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void os::logger::Init() 4 | { 5 | } 6 | 7 | void os::logger::Log(const std::string_view str, ELogType type, const char* func) 8 | { 9 | if (func) 10 | { 11 | fmt::println("[{}] {}", func, str); 12 | } 13 | else 14 | { 15 | fmt::println("{}", str); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /UnleashedRecomp/os/linux/media_linux.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | bool os::media::IsExternalMediaPlaying() 4 | { 5 | // This functionality is not supported in Linux. 6 | return false; 7 | } 8 | -------------------------------------------------------------------------------- /UnleashedRecomp/os/linux/process_linux.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | std::filesystem::path os::process::GetExecutablePath() 6 | { 7 | char exePath[PATH_MAX] = {}; 8 | if (readlink("/proc/self/exe", exePath, PATH_MAX) > 0) 9 | { 10 | return std::filesystem::path(std::u8string_view((const char8_t*)(exePath))); 11 | } 12 | else 13 | { 14 | return std::filesystem::path(); 15 | } 16 | } 17 | 18 | std::filesystem::path os::process::GetWorkingDirectory() 19 | { 20 | char cwd[PATH_MAX] = {}; 21 | char *res = getcwd(cwd, sizeof(cwd)); 22 | if (res != nullptr) 23 | { 24 | return std::filesystem::path(std::u8string_view((const char8_t*)(cwd))); 25 | } 26 | else 27 | { 28 | return std::filesystem::path(); 29 | } 30 | } 31 | 32 | bool os::process::SetWorkingDirectory(const std::filesystem::path& path) 33 | { 34 | return chdir(path.c_str()) == 0; 35 | } 36 | 37 | bool os::process::StartProcess(const std::filesystem::path& path, const std::vector& args, std::filesystem::path work) 38 | { 39 | pid_t pid = fork(); 40 | if (pid < 0) 41 | return false; 42 | 43 | if (pid == 0) 44 | { 45 | setsid(); 46 | 47 | std::u8string workU8 = work.u8string(); 48 | chdir((const char*)(workU8.c_str())); 49 | 50 | std::u8string pathU8 = path.u8string(); 51 | std::vector argStrs; 52 | argStrs.push_back((char*)(pathU8.c_str())); 53 | for (const std::string& arg : args) 54 | argStrs.push_back((char *)(arg.c_str())); 55 | 56 | argStrs.push_back(nullptr); 57 | execvp((const char*)(pathU8.c_str()), argStrs.data()); 58 | raise(SIGKILL); 59 | } 60 | 61 | return true; 62 | } 63 | 64 | void os::process::CheckConsole() 65 | { 66 | // Always visible on Linux. 67 | g_consoleVisible = true; 68 | } 69 | 70 | void os::process::ShowConsole() 71 | { 72 | // Unnecessary on Linux. 73 | } 74 | -------------------------------------------------------------------------------- /UnleashedRecomp/os/linux/registry_linux.inl: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // TODO: Implement 4 | inline bool os::registry::Init() 5 | { 6 | return false; 7 | } 8 | 9 | // TODO: read from file? 10 | template 11 | bool os::registry::ReadValue(const std::string_view& name, T& data) 12 | { 13 | return false; 14 | } 15 | 16 | // TODO: write to file? 17 | template 18 | bool os::registry::WriteValue(const std::string_view& name, const T& data) 19 | { 20 | return false; 21 | } 22 | -------------------------------------------------------------------------------- /UnleashedRecomp/os/linux/user_linux.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | bool os::user::IsDarkTheme() 4 | { 5 | return false; 6 | } 7 | -------------------------------------------------------------------------------- /UnleashedRecomp/os/linux/version_linux.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | os::version::OSVersion os::version::GetOSVersion() 4 | { 5 | assert(false && "Unimplemented."); 6 | return os::version::OSVersion(); 7 | } 8 | -------------------------------------------------------------------------------- /UnleashedRecomp/os/logger.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define LOG_IMPL(type, func, str) os::logger::Log(str, os::logger::ELogType::type, func) 6 | #define LOGF_IMPL(type, func, str, ...) os::logger::Log(fmt::format(str, __VA_ARGS__), os::logger::ELogType::type, func) 7 | 8 | // Function-specific logging. 9 | 10 | #define LOG(str) LOG_IMPL(None, __func__, str) 11 | #define LOG_WARNING(str) LOG_IMPL(Warning, __func__, str) 12 | #define LOG_ERROR(str) LOG_IMPL(Error, __func__, str) 13 | 14 | #if _DEBUG 15 | #define LOG_UTILITY(str) LOG_IMPL(Utility, __func__, str) 16 | #else 17 | #define LOG_UTILITY(str) 18 | #endif 19 | 20 | #define LOGF(str, ...) LOGF_IMPL(None, __func__, str, __VA_ARGS__) 21 | #define LOGF_WARNING(str, ...) LOGF_IMPL(Warning, __func__, str, __VA_ARGS__) 22 | #define LOGF_ERROR(str, ...) LOGF_IMPL(Error, __func__, str, __VA_ARGS__) 23 | 24 | #if _DEBUG 25 | #define LOGF_UTILITY(str, ...) LOGF_IMPL(Utility, __func__, str, __VA_ARGS__) 26 | #else 27 | #define LOGF_UTILITY(str, ...) 28 | #endif 29 | 30 | // Non-function-specific logging. 31 | 32 | #define LOGN(str) LOG_IMPL(None, "*", str) 33 | #define LOGN_WARNING(str) LOG_IMPL(Warning, "*", str) 34 | #define LOGN_ERROR(str) LOG_IMPL(Error, "*", str) 35 | 36 | #if _DEBUG 37 | #define LOGN_UTILITY(str) LOG_IMPL(Utility, "*", str) 38 | #else 39 | #define LOGN_UTILITY(str) 40 | #endif 41 | 42 | #define LOGFN(str, ...) LOGF_IMPL(None, "*", str, __VA_ARGS__) 43 | #define LOGFN_WARNING(str, ...) LOGF_IMPL(Warning, "*", str, __VA_ARGS__) 44 | #define LOGFN_ERROR(str, ...) LOGF_IMPL(Error, "*", str, __VA_ARGS__) 45 | 46 | #if _DEBUG 47 | #define LOGFN_UTILITY(str, ...) LOGF_IMPL(Utility, "*", str, __VA_ARGS__) 48 | #else 49 | #define LOGFN_UTILITY(str, ...) 50 | #endif 51 | 52 | namespace os::logger 53 | { 54 | enum class ELogType 55 | { 56 | None, 57 | Utility, 58 | Warning, 59 | Error 60 | }; 61 | 62 | void Init(); 63 | void Log(const std::string_view str, ELogType type = ELogType::None, const char* func = nullptr); 64 | } 65 | -------------------------------------------------------------------------------- /UnleashedRecomp/os/media.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace os::media 4 | { 5 | bool IsExternalMediaPlaying(); 6 | } 7 | -------------------------------------------------------------------------------- /UnleashedRecomp/os/process.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace os::process 4 | { 5 | inline bool g_consoleVisible; 6 | 7 | std::filesystem::path GetExecutablePath(); 8 | std::filesystem::path GetWorkingDirectory(); 9 | bool SetWorkingDirectory(const std::filesystem::path& path); 10 | bool StartProcess(const std::filesystem::path& path, const std::vector& args, std::filesystem::path work = {}); 11 | void CheckConsole(); 12 | void ShowConsole(); 13 | } 14 | -------------------------------------------------------------------------------- /UnleashedRecomp/os/registry.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace os::registry 4 | { 5 | bool Init(); 6 | 7 | template 8 | bool ReadValue(const std::string_view& name, T& data); 9 | 10 | template 11 | bool WriteValue(const std::string_view& name, const T& data); 12 | } 13 | 14 | #if _WIN32 15 | #include 16 | #elif defined(__linux__) 17 | #include 18 | #endif 19 | -------------------------------------------------------------------------------- /UnleashedRecomp/os/user.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace os::user 4 | { 5 | bool IsDarkTheme(); 6 | } 7 | -------------------------------------------------------------------------------- /UnleashedRecomp/os/version.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace os::version 4 | { 5 | struct OSVersion 6 | { 7 | uint32_t Major{}; 8 | uint32_t Minor{}; 9 | uint32_t Build{}; 10 | }; 11 | 12 | OSVersion GetOSVersion(); 13 | } 14 | -------------------------------------------------------------------------------- /UnleashedRecomp/os/win32/logger_win32.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define FOREGROUND_WHITE (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE) 5 | #define FOREGROUND_YELLOW (FOREGROUND_RED | FOREGROUND_GREEN) 6 | 7 | static HANDLE g_hStandardOutput; 8 | 9 | void os::logger::Init() 10 | { 11 | g_hStandardOutput = GetStdHandle(STD_OUTPUT_HANDLE); 12 | } 13 | 14 | void os::logger::Log(const std::string_view str, ELogType type, const char* func) 15 | { 16 | if (!os::process::g_consoleVisible) 17 | return; 18 | 19 | switch (type) 20 | { 21 | case ELogType::Utility: 22 | SetConsoleTextAttribute(g_hStandardOutput, FOREGROUND_GREEN | FOREGROUND_INTENSITY); 23 | break; 24 | 25 | case ELogType::Warning: 26 | SetConsoleTextAttribute(g_hStandardOutput, FOREGROUND_YELLOW | FOREGROUND_INTENSITY); 27 | break; 28 | 29 | case ELogType::Error: 30 | SetConsoleTextAttribute(g_hStandardOutput, FOREGROUND_RED | FOREGROUND_INTENSITY); 31 | break; 32 | 33 | default: 34 | SetConsoleTextAttribute(g_hStandardOutput, FOREGROUND_WHITE); 35 | break; 36 | } 37 | 38 | if (func) 39 | { 40 | fmt::println("[{}] {}", func, str); 41 | } 42 | else 43 | { 44 | fmt::println("{}", str); 45 | } 46 | 47 | SetConsoleTextAttribute(g_hStandardOutput, FOREGROUND_WHITE); 48 | } 49 | -------------------------------------------------------------------------------- /UnleashedRecomp/os/win32/media_win32.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace winrt; 7 | using namespace winrt::Windows::Foundation; 8 | using namespace winrt::Windows::Media::Control; 9 | 10 | static GlobalSystemMediaTransportControlsSessionManager g_sessionManager = nullptr; 11 | 12 | static GlobalSystemMediaTransportControlsSessionManager GetSessionManager() 13 | { 14 | if (g_sessionManager) 15 | return g_sessionManager; 16 | 17 | try 18 | { 19 | init_apartment(); 20 | return g_sessionManager = GlobalSystemMediaTransportControlsSessionManager::RequestAsync().get(); 21 | } 22 | catch (...) 23 | { 24 | LOGF_ERROR("Failed to retrieve GSMTC session manager: 0x{:X}", to_hresult().value); 25 | return nullptr; 26 | } 27 | } 28 | 29 | static GlobalSystemMediaTransportControlsSession GetCurrentSession() 30 | { 31 | auto sessionManager = GetSessionManager(); 32 | 33 | if (!sessionManager) 34 | return nullptr; 35 | 36 | try 37 | { 38 | return sessionManager.GetCurrentSession(); 39 | } 40 | catch (...) 41 | { 42 | LOGF_ERROR("Failed to retrieve current GSMTC session: 0x{:X}", to_hresult().value); 43 | return nullptr; 44 | } 45 | } 46 | 47 | static GlobalSystemMediaTransportControlsSessionPlaybackInfo GetPlaybackInfo() 48 | { 49 | auto session = GetCurrentSession(); 50 | 51 | if (!session) 52 | return nullptr; 53 | 54 | try 55 | { 56 | return session.GetPlaybackInfo(); 57 | } 58 | catch (...) 59 | { 60 | LOGF_ERROR("Failed to retrieve GSMTC playback info: 0x{:X}", to_hresult().value); 61 | return nullptr; 62 | } 63 | } 64 | 65 | bool os::media::IsExternalMediaPlaying() 66 | { 67 | auto playbackInfo = GetPlaybackInfo(); 68 | 69 | if (!playbackInfo) 70 | return false; 71 | 72 | try 73 | { 74 | return playbackInfo.PlaybackStatus() == GlobalSystemMediaTransportControlsSessionPlaybackStatus::Playing; 75 | } 76 | catch (...) 77 | { 78 | LOGF_ERROR("Failed to retrieve GSMTC playback status: 0x{:X}", to_hresult().value); 79 | return false; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /UnleashedRecomp/os/win32/process_win32.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | std::filesystem::path os::process::GetExecutablePath() 4 | { 5 | WCHAR exePath[MAX_PATH]; 6 | 7 | if (!GetModuleFileNameW(nullptr, exePath, MAX_PATH)) 8 | return std::filesystem::path(); 9 | 10 | return std::filesystem::path(exePath); 11 | } 12 | 13 | std::filesystem::path os::process::GetWorkingDirectory() 14 | { 15 | WCHAR workPath[MAX_PATH]; 16 | 17 | if (!GetCurrentDirectoryW(MAX_PATH, workPath)) 18 | return std::filesystem::path(); 19 | 20 | return std::filesystem::path(workPath); 21 | } 22 | 23 | bool os::process::SetWorkingDirectory(const std::filesystem::path& path) 24 | { 25 | return SetCurrentDirectoryW(path.c_str()); 26 | } 27 | 28 | bool os::process::StartProcess(const std::filesystem::path& path, const std::vector& args, std::filesystem::path work) 29 | { 30 | if (path.empty()) 31 | return false; 32 | 33 | if (work.empty()) 34 | work = path.parent_path(); 35 | 36 | auto cli = path.wstring(); 37 | 38 | // NOTE: We assume the CLI arguments only contain ASCII characters. 39 | for (auto& arg : args) 40 | cli += L" " + std::wstring(arg.begin(), arg.end()); 41 | 42 | STARTUPINFOW startInfo{ sizeof(STARTUPINFOW) }; 43 | PROCESS_INFORMATION procInfo{}; 44 | std::wstring pathW = path.wstring(); 45 | std::wstring workW = work.wstring(); 46 | if (!CreateProcessW(pathW.c_str(), cli.data(), nullptr, nullptr, false, 0, nullptr, workW.c_str(), &startInfo, &procInfo)) 47 | return false; 48 | 49 | CloseHandle(procInfo.hProcess); 50 | CloseHandle(procInfo.hThread); 51 | 52 | return true; 53 | } 54 | 55 | void os::process::CheckConsole() 56 | { 57 | g_consoleVisible = (GetConsoleWindow() != nullptr); 58 | } 59 | 60 | void os::process::ShowConsole() 61 | { 62 | if (GetConsoleWindow() == nullptr) 63 | { 64 | AllocConsole(); 65 | freopen("CONIN$", "r", stdin); 66 | freopen("CONOUT$", "w", stderr); 67 | freopen("CONOUT$", "w", stdout); 68 | 69 | g_consoleVisible = true; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /UnleashedRecomp/os/win32/registry_win32.inl: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | inline const wchar_t* g_registryRoot = L"Software\\UnleashedRecomp"; 4 | 5 | inline bool os::registry::Init() 6 | { 7 | return true; 8 | } 9 | 10 | template 11 | bool os::registry::ReadValue(const std::string_view& name, T& data) 12 | { 13 | HKEY hKey; 14 | 15 | if (RegOpenKeyExW(HKEY_CURRENT_USER, g_registryRoot, 0, KEY_READ, &hKey) != ERROR_SUCCESS) 16 | return false; 17 | 18 | wchar_t wideName[128]; 19 | int wideNameSize = MultiByteToWideChar(CP_UTF8, 0, name.data(), name.size(), wideName, sizeof(wideName)); 20 | if (wideNameSize == 0) 21 | { 22 | return false; 23 | } 24 | 25 | wideName[wideNameSize] = 0; 26 | DWORD bufferSize = 0; 27 | DWORD dataType = 0; 28 | 29 | auto result = RegGetValueW(hKey, nullptr, wideName, RRF_RT_ANY, &dataType, nullptr, &bufferSize); 30 | 31 | if (result != ERROR_SUCCESS) 32 | { 33 | RegCloseKey(hKey); 34 | return false; 35 | } 36 | 37 | result = ERROR_INVALID_FUNCTION; 38 | if constexpr (std::is_same_v) 39 | { 40 | if (dataType == REG_SZ) 41 | { 42 | std::vector buffer{}; 43 | buffer.reserve(bufferSize); 44 | result = RegGetValueW(hKey, nullptr, wideName, RRF_RT_REG_SZ, nullptr, buffer.data(), &bufferSize); 45 | 46 | if (result == ERROR_SUCCESS) 47 | { 48 | int valueSize = WideCharToMultiByte(CP_UTF8, 0, (wchar_t*)buffer.data(), (bufferSize / sizeof(wchar_t)) - 1, nullptr, 0, nullptr, nullptr); 49 | data.resize(valueSize); 50 | WideCharToMultiByte(CP_UTF8, 0, (wchar_t*)buffer.data(), (bufferSize / sizeof(wchar_t)) - 1, data.data(), valueSize, nullptr, nullptr); 51 | } 52 | } 53 | } 54 | else if constexpr (std::is_same_v) 55 | { 56 | if (dataType == REG_SZ) 57 | { 58 | std::vector buffer{}; 59 | buffer.reserve(bufferSize); 60 | result = RegGetValueW(hKey, nullptr, wideName, RRF_RT_REG_SZ, nullptr, buffer.data(), &bufferSize); 61 | 62 | if (result == ERROR_SUCCESS) 63 | { 64 | data = reinterpret_cast(buffer.data()); 65 | } 66 | } 67 | } 68 | else if constexpr (std::is_same_v) 69 | { 70 | result = RegGetValueW(hKey, nullptr, wideName, RRF_RT_DWORD, nullptr, (BYTE*)&data, &bufferSize); 71 | } 72 | else if constexpr (std::is_same_v) 73 | { 74 | result = RegGetValueW(hKey, nullptr, wideName, RRF_RT_QWORD, nullptr, (BYTE*)&data, &bufferSize); 75 | } 76 | else 77 | { 78 | static_assert(false, "Unsupported data type."); 79 | } 80 | 81 | RegCloseKey(hKey); 82 | return result == ERROR_SUCCESS; 83 | } 84 | 85 | template 86 | bool os::registry::WriteValue(const std::string_view& name, const T& data) 87 | { 88 | HKEY hKey; 89 | 90 | if (RegCreateKeyExW(HKEY_CURRENT_USER, g_registryRoot, 0, nullptr, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL) != ERROR_SUCCESS) 91 | return false; 92 | 93 | BYTE* pData = nullptr; 94 | DWORD dataSize = 0; 95 | DWORD dataType = 0; 96 | bool wideString = false; 97 | 98 | if constexpr (std::is_same_v) 99 | { 100 | pData = (BYTE*)data.c_str(); 101 | dataSize = data.size() + 1; 102 | dataType = REG_SZ; 103 | } 104 | else if constexpr (std::is_same_v) 105 | { 106 | pData = &data; 107 | dataSize = sizeof(T); 108 | dataType = REG_DWORD; 109 | } 110 | else if constexpr (std::is_same_v) 111 | { 112 | pData = &data; 113 | dataSize = sizeof(T); 114 | dataType = REG_QWORD; 115 | } 116 | else if constexpr (std::is_same_v) 117 | { 118 | pData = (BYTE*)data.c_str(); 119 | dataSize = (wcslen((const wchar_t*)pData) + 1) * sizeof(wchar_t); 120 | dataType = REG_SZ; 121 | wideString = true; 122 | } 123 | else 124 | { 125 | static_assert(false, "Unsupported data type."); 126 | } 127 | 128 | LSTATUS result = ERROR_INVALID_FUNCTION; 129 | if (wideString) 130 | { 131 | wchar_t wideName[128]; 132 | int wideNameSize = MultiByteToWideChar(CP_UTF8, 0, name.data(), name.size(), wideName, sizeof(wideName)); 133 | if (wideNameSize == 0) 134 | { 135 | return false; 136 | } 137 | 138 | wideName[wideNameSize] = 0; 139 | result = RegSetValueExW(hKey, wideName, 0, dataType, pData, dataSize); 140 | } 141 | else 142 | { 143 | result = RegSetValueExA(hKey, name.data(), 0, dataType, pData, dataSize); 144 | } 145 | 146 | RegCloseKey(hKey); 147 | 148 | if (result != ERROR_SUCCESS) 149 | return false; 150 | 151 | return true; 152 | } 153 | -------------------------------------------------------------------------------- /UnleashedRecomp/os/win32/user_win32.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | bool os::user::IsDarkTheme() 4 | { 5 | HKEY hKey; 6 | 7 | if (RegOpenKeyExA(HKEY_CURRENT_USER, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", 0, KEY_READ, &hKey) == ERROR_SUCCESS) 8 | { 9 | DWORD value = 0; 10 | DWORD valueSize = sizeof(value); 11 | 12 | if (RegQueryValueExA(hKey, "AppsUseLightTheme", nullptr, nullptr, (LPBYTE)&value, &valueSize) == ERROR_SUCCESS) 13 | { 14 | RegCloseKey(hKey); 15 | 16 | return value == 0; 17 | } 18 | 19 | RegCloseKey(hKey); 20 | } 21 | 22 | return false; 23 | } 24 | -------------------------------------------------------------------------------- /UnleashedRecomp/os/win32/version_win32.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | LIB_FUNCTION(LONG, "ntdll.dll", RtlGetVersion, PRTL_OSVERSIONINFOW); 4 | 5 | os::version::OSVersion os::version::GetOSVersion() 6 | { 7 | auto result = os::version::OSVersion{}; 8 | 9 | OSVERSIONINFOEXW osvi = { 0 }; 10 | osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW); 11 | 12 | if (RtlGetVersion((PRTL_OSVERSIONINFOW)&osvi) != 0) 13 | return result; 14 | 15 | result.Major = osvi.dwMajorVersion; 16 | result.Minor = osvi.dwMinorVersion; 17 | result.Build = osvi.dwBuildNumber; 18 | 19 | return result; 20 | } 21 | -------------------------------------------------------------------------------- /UnleashedRecomp/patches/CGameModeStageTitle_patches.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // SWA::CGameModeStageTitle::Update 5 | PPC_FUNC_IMPL(__imp__sub_825518B8); 6 | PPC_FUNC(sub_825518B8) 7 | { 8 | auto pGameModeStageTitle = (SWA::CGameModeStageTitle*)g_memory.Translate(ctx.r3.u32); 9 | 10 | __imp__sub_825518B8(ctx, base); 11 | 12 | if (g_quitMessageOpen) 13 | pGameModeStageTitle->m_AdvertiseMovieWaitTime = 0; 14 | } 15 | -------------------------------------------------------------------------------- /UnleashedRecomp/patches/CTitleStateIntro_patches.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | extern bool g_quitMessageOpen; 4 | -------------------------------------------------------------------------------- /UnleashedRecomp/patches/CTitleStateMenu_patches.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | static bool g_installMessageOpen = false; 15 | static bool g_installMessageFaderBegun = false; 16 | static int g_installMessageResult = -1; 17 | 18 | static bool ProcessInstallMessage() 19 | { 20 | if (!g_installMessageOpen) 21 | return false; 22 | 23 | if (g_installMessageFaderBegun) 24 | return true; 25 | 26 | auto& str = App::s_isMissingDLC 27 | ? Localise("Installer_Message_TitleMissingDLC") 28 | : Localise("Installer_Message_Title"); 29 | 30 | std::array options = { Localise("Common_Yes"), Localise("Common_No") }; 31 | 32 | if (MessageWindow::Open(str, &g_installMessageResult, options, 1) == MSG_CLOSED) 33 | { 34 | switch (g_installMessageResult) 35 | { 36 | case 0: 37 | Fader::FadeOut(1, []() { App::Restart({ "--install-dlc" }); }); 38 | g_installMessageFaderBegun = true; 39 | break; 40 | 41 | case 1: 42 | g_installMessageOpen = false; 43 | g_installMessageResult = -1; 44 | break; 45 | } 46 | } 47 | 48 | return true; 49 | } 50 | 51 | // SWA::CTitleStateMenu::Update 52 | PPC_FUNC_IMPL(__imp__sub_825882B8); 53 | PPC_FUNC(sub_825882B8) 54 | { 55 | auto pTitleStateMenu = (SWA::CTitleStateMenu*)g_memory.Translate(ctx.r3.u32); 56 | auto pGameDocument = SWA::CGameDocument::GetInstance(); 57 | 58 | auto pInputState = SWA::CInputState::GetInstance(); 59 | auto& pPadState = pInputState->GetPadState(); 60 | auto isAccepted = pPadState.IsTapped(SWA::eKeyState_A) || pPadState.IsTapped(SWA::eKeyState_Start); 61 | 62 | auto pContext = pTitleStateMenu->GetContextBase(); 63 | auto isNewGameIndex = pContext->m_pTitleMenu->m_CursorIndex == 0; 64 | auto isOptionsIndex = pContext->m_pTitleMenu->m_CursorIndex == 2; 65 | auto isInstallIndex = pContext->m_pTitleMenu->m_CursorIndex == 3; 66 | 67 | // Always default to New Game with corrupted save data. 68 | if (App::s_isSaveDataCorrupt && pContext->m_pTitleMenu->m_CursorIndex == 1) 69 | pContext->m_pTitleMenu->m_CursorIndex = 0; 70 | 71 | if (isNewGameIndex && isAccepted) 72 | { 73 | if (pContext->m_pTitleMenu->m_IsDeleteCheckMessageOpen && 74 | pGameDocument->m_pMember->m_pGeneralWindow->m_SelectedIndex == 1) 75 | { 76 | LOGN("Resetting achievements..."); 77 | 78 | AchievementManager::Reset(); 79 | } 80 | } 81 | else if (!OptionsMenu::s_isVisible && isOptionsIndex) 82 | { 83 | if (OptionsMenu::s_isRestartRequired) 84 | { 85 | static int result = -1; 86 | 87 | if (MessageWindow::Open(Localise("Options_Message_Restart"), &result) == MSG_CLOSED) 88 | Fader::FadeOut(1, []() { App::Restart(); }); 89 | } 90 | else if (isAccepted) 91 | { 92 | Game_PlaySound("sys_worldmap_window"); 93 | Game_PlaySound("sys_worldmap_decide"); 94 | OptionsMenu::Open(); 95 | } 96 | } 97 | else if (isInstallIndex && isAccepted) 98 | { 99 | g_installMessageOpen = true; 100 | } 101 | 102 | if (!OptionsMenu::s_isVisible && !OptionsMenu::s_isRestartRequired && !ProcessInstallMessage()) 103 | __imp__sub_825882B8(ctx, base); 104 | 105 | if (isOptionsIndex) 106 | { 107 | if (OptionsMenu::CanClose() && pPadState.IsTapped(SWA::eKeyState_B)) 108 | { 109 | Game_PlaySound("sys_worldmap_cansel"); 110 | OptionsMenu::Close(); 111 | } 112 | } 113 | } 114 | 115 | void TitleMenuRemoveContinueOnCorruptSaveMidAsmHook(PPCRegister& r3) 116 | { 117 | if (!App::s_isSaveDataCorrupt) 118 | return; 119 | 120 | r3.u64 = 0; 121 | } 122 | 123 | void TitleMenuRemoveStorageDeviceOptionMidAsmHook(PPCRegister& r11) 124 | { 125 | r11.u32 = 0; 126 | } 127 | 128 | void TitleMenuAddInstallOptionMidAsmHook(PPCRegister& r3) 129 | { 130 | r3.u32 = 1; 131 | } 132 | -------------------------------------------------------------------------------- /UnleashedRecomp/patches/aspect_ratio_patches.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | inline constexpr float NARROW_ASPECT_RATIO = 4.0f / 3.0f; 4 | inline constexpr float WIDE_ASPECT_RATIO = 16.0f / 9.0f; 5 | inline constexpr float STEAM_DECK_ASPECT_RATIO = 16.0f / 10.0f; 6 | 7 | inline float g_aspectRatio; 8 | inline float g_aspectRatioOffsetX; 9 | inline float g_aspectRatioOffsetY; 10 | inline float g_aspectRatioScale; 11 | inline float g_aspectRatioGameplayScale; 12 | inline float g_aspectRatioNarrowScale; 13 | 14 | struct AspectRatioPatches 15 | { 16 | static void ComputeOffsets(); 17 | }; 18 | -------------------------------------------------------------------------------- /UnleashedRecomp/patches/audio_patches.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int AudioPatches::m_isAttenuationSupported = -1; 9 | 10 | static be* GetVolume(bool isMusic = true) 11 | { 12 | auto ppUnkClass = (be*)g_memory.Translate(0x83362FFC); 13 | 14 | if (!ppUnkClass->get()) 15 | return nullptr; 16 | 17 | // NOTE (Hyper): This is fine, trust me. See 0x82E58728. 18 | return (be*)g_memory.Translate(4 * ((int)isMusic + 0x1C) + ((be*)g_memory.Translate(ppUnkClass->get() + 4))->get()); 19 | } 20 | 21 | bool AudioPatches::CanAttenuate() 22 | { 23 | #if _WIN32 24 | if (m_isAttenuationSupported >= 0) 25 | return m_isAttenuationSupported; 26 | 27 | auto version = os::version::GetOSVersion(); 28 | 29 | m_isAttenuationSupported = version.Major >= 10 && version.Build >= 17763; 30 | 31 | return m_isAttenuationSupported; 32 | #else 33 | return false; 34 | #endif 35 | } 36 | 37 | void AudioPatches::Update(float deltaTime) 38 | { 39 | auto pMusicVolume = GetVolume(); 40 | auto pEffectsVolume = GetVolume(false); 41 | 42 | if (!pMusicVolume || !pEffectsVolume) 43 | return; 44 | 45 | if (Config::MusicAttenuation && CanAttenuate()) 46 | { 47 | auto time = 1.0f - expf(2.5f * -deltaTime); 48 | 49 | if (os::media::IsExternalMediaPlaying()) 50 | { 51 | *pMusicVolume = std::lerp(*pMusicVolume, 0.0f, time); 52 | } 53 | else 54 | { 55 | *pMusicVolume = std::lerp(*pMusicVolume, Config::MusicVolume, time); 56 | } 57 | } 58 | else 59 | { 60 | *pMusicVolume = Config::MusicVolume; 61 | } 62 | 63 | *pEffectsVolume = Config::EffectsVolume; 64 | } 65 | 66 | // Stub volume setter. 67 | GUEST_FUNCTION_STUB(sub_82E58728); 68 | 69 | // HORRIBLE HACK ZONE 70 | // The options menu uses se_system_worldmap.csb, which is stored in Title.ar.00. 71 | // This archive gets unloaded in stages, which causes sounds to not play in the options menu. 72 | // To solve this, once the CSB gets loaded at title, we'll keep it PERMANENTLY loaded. 73 | // This'll make the SFX not work if Title never gets loaded, but that'll only happen when quick booting to stages. 74 | static bool g_loadedWorldMapCsb; 75 | 76 | bool MakeCueSheetDataMidAsmHook(PPCRegister& r31) 77 | { 78 | uint8_t* base = g_memory.base; 79 | uint32_t str = PPC_LOAD_U32(r31.u32); 80 | 81 | if (str != NULL && strcmp(reinterpret_cast(base + str), "se_system_worldmap") == 0) 82 | { 83 | if (!g_loadedWorldMapCsb) 84 | { 85 | g_loadedWorldMapCsb = true; 86 | return false; // Allow load for the first and only time. 87 | } 88 | 89 | // Already loaded before, skip all the loading and name assignment code. 90 | // Not assigning the name prevents it from unloading the CSB file. 91 | return true; 92 | } 93 | 94 | return false; 95 | } 96 | -------------------------------------------------------------------------------- /UnleashedRecomp/patches/audio_patches.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class AudioPatches 4 | { 5 | protected: 6 | static int m_isAttenuationSupported; 7 | 8 | public: 9 | static bool CanAttenuate(); 10 | static void Update(float deltaTime); 11 | }; 12 | -------------------------------------------------------------------------------- /UnleashedRecomp/patches/camera_patches.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "camera_patches.h" 6 | #include "aspect_ratio_patches.h" 7 | 8 | void CameraAspectRatioMidAsmHook(PPCRegister& r30, PPCRegister& r31) 9 | { 10 | r30.u32 = 0; 11 | 12 | auto camera = (SWA::CCamera*)g_memory.Translate(r31.u32); 13 | 14 | // Dynamically adjust horizontal aspect ratio to window dimensions. 15 | camera->m_HorzAspectRatio = g_aspectRatio; 16 | } 17 | 18 | float AdjustFieldOfView(float fieldOfView, float aspectRatio) 19 | { 20 | if (Config::AspectRatio == EAspectRatio::OriginalNarrow) 21 | { 22 | // Replicate the original incorrect field of view formula if requested. 23 | fieldOfView *= NARROW_ASPECT_RATIO; 24 | } 25 | else if (aspectRatio < WIDE_ASPECT_RATIO) 26 | { 27 | // Use proper VERT+ otherwise for narrow aspect ratios. 28 | fieldOfView = 2.0 * atan(tan(0.5 * fieldOfView) / aspectRatio * WIDE_ASPECT_RATIO); 29 | } 30 | 31 | return fieldOfView; 32 | } 33 | 34 | void CameraFieldOfViewMidAsmHook(PPCRegister& r31, PPCRegister& f31) 35 | { 36 | auto camera = (SWA::CCamera*)g_memory.Translate(r31.u32); 37 | 38 | f31.f64 = AdjustFieldOfView(f31.f64, camera->m_HorzAspectRatio); 39 | } 40 | 41 | PPC_FUNC_IMPL(__imp__sub_824697B0); 42 | PPC_FUNC(sub_824697B0) 43 | { 44 | auto pCamera = (SWA::CCamera*)g_memory.Translate(ctx.r3.u32); 45 | 46 | pCamera->m_InvertX = Config::HorizontalCamera == ECameraRotationMode::Reverse; 47 | pCamera->m_InvertY = Config::VerticalCamera == ECameraRotationMode::Reverse; 48 | 49 | __imp__sub_824697B0(ctx, base); 50 | } 51 | 52 | // SWA::CCamera::UpdateSerial 53 | PPC_FUNC_IMPL(__imp__sub_82467890); 54 | PPC_FUNC(sub_82467890) 55 | { 56 | if (g_needsResize) 57 | { 58 | // Recompute the projection matrix for one frame to fix stretching on pause menu. 59 | auto r3 = ctx.r3; 60 | auto r4 = ctx.r4; 61 | sub_82468E38(ctx, base); 62 | ctx.r3 = r3; 63 | ctx.r4 = r4; 64 | } 65 | 66 | __imp__sub_82467890(ctx, base); 67 | } 68 | -------------------------------------------------------------------------------- /UnleashedRecomp/patches/camera_patches.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | extern float AdjustFieldOfView(float fieldOfView, float aspectRatio); 4 | -------------------------------------------------------------------------------- /UnleashedRecomp/patches/frontend_listener.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | static class FrontendListener : public SDLEventListener 8 | { 9 | bool m_isF8KeyDown = false; 10 | 11 | public: 12 | bool OnSDLEvent(SDL_Event* event) override 13 | { 14 | if (!Config::HUDToggleKey || OptionsMenu::s_isVisible) 15 | return false; 16 | 17 | switch (event->type) 18 | { 19 | case SDL_KEYDOWN: 20 | { 21 | if (event->key.keysym.sym != SDLK_F8 || m_isF8KeyDown) 22 | break; 23 | 24 | *SWA::SGlobals::ms_IsRenderHud = !*SWA::SGlobals::ms_IsRenderHud; 25 | 26 | LOGFN("HUD {}", *SWA::SGlobals::ms_IsRenderHud ? "ON" : "OFF"); 27 | 28 | m_isF8KeyDown = true; 29 | 30 | break; 31 | } 32 | 33 | case SDL_KEYUP: 34 | m_isF8KeyDown = event->key.keysym.sym != SDLK_F8; 35 | break; 36 | } 37 | 38 | return false; 39 | } 40 | } 41 | g_frontendListener; 42 | -------------------------------------------------------------------------------- /UnleashedRecomp/patches/inspire_patches.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class InspirePatches 4 | { 5 | public: 6 | static std::string s_sceneName; 7 | 8 | static void DrawDebug(); 9 | static void Update(); 10 | }; 11 | -------------------------------------------------------------------------------- /UnleashedRecomp/patches/object_patches.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | // SWA::CObjFlame::CObjFlame 7 | // A field is not zero initialized, 8 | // causing collisions to constantly get created 9 | // and slow down the game. 10 | PPC_FUNC_IMPL(__imp__sub_82608E60); 11 | PPC_FUNC(sub_82608E60) 12 | { 13 | memset(base + ctx.r3.u32, 0, 0x154); 14 | __imp__sub_82608E60(ctx, base); 15 | } 16 | 17 | // The barrel gets stuck at a slope at high frame rates and softlocks the player. 18 | // We can update the character proxy at 30 FPS, and interpolate the visuals to work around this issue. 19 | static constexpr size_t OBJ_BIG_BARREL_SIZE = 0x1A0; 20 | 21 | struct ObjBigBarrelEx 22 | { 23 | float elapsedTime{}; 24 | bool interpolate{}; 25 | }; 26 | 27 | void ObjBigBarrelAllocMidAsmHook(PPCRegister& r3) 28 | { 29 | r3.u32 += sizeof(ObjBigBarrelEx); 30 | } 31 | 32 | // SWA::CObjBigBarrel::CObjBigBarrel 33 | PPC_FUNC_IMPL(__imp__sub_8271AC08); 34 | PPC_FUNC(sub_8271AC08) 35 | { 36 | new (base + ctx.r3.u32 + OBJ_BIG_BARREL_SIZE) ObjBigBarrelEx(); 37 | __imp__sub_8271AC08(ctx, base); 38 | } 39 | 40 | // CObjBigBarrel::Integrate 41 | PPC_FUNC_IMPL(__imp__sub_8271AA30); 42 | PPC_FUNC(sub_8271AA30) 43 | { 44 | auto objBigBarrelEx = reinterpret_cast(base + ctx.r3.u32 + OBJ_BIG_BARREL_SIZE); 45 | objBigBarrelEx->interpolate = ctx.f1.f64 < (1.0 / 30.0); 46 | objBigBarrelEx->elapsedTime += ctx.f1.f64; 47 | 48 | if (!objBigBarrelEx->interpolate || objBigBarrelEx->elapsedTime >= (1.0f / 30.0f)) 49 | { 50 | ctx.f1.f64 = objBigBarrelEx->elapsedTime; 51 | __imp__sub_8271AA30(ctx, base); 52 | objBigBarrelEx->elapsedTime = 0.0f; 53 | } 54 | } 55 | 56 | void ObjBigBarrelSetPositionMidAsmHook(PPCRegister& r3, PPCRegister& r4) 57 | { 58 | uint8_t* base = g_memory.base; 59 | auto objBigBarrelEx = reinterpret_cast(base + r3.u32 + OBJ_BIG_BARREL_SIZE); 60 | 61 | if (objBigBarrelEx->interpolate) 62 | { 63 | auto characterProxy = reinterpret_cast(base + PPC_LOAD_U32(r3.u32 + 0x100)); 64 | auto position = reinterpret_cast(base + r4.u32); 65 | 66 | float factor = (1.0f / 30.0f) - objBigBarrelEx->elapsedTime; 67 | position->X = position->X - characterProxy->m_Velocity.X * factor; 68 | position->Y = position->Y - characterProxy->m_Velocity.Y * factor; 69 | position->Z = position->Z - characterProxy->m_Velocity.Z * factor; 70 | } 71 | } 72 | 73 | // SWA::CExBullet::AddCallback 74 | // Tornado Defense bullet particles are colored by the button prompt, which differs on PlayStation 3. 75 | // Luckily, the PS3 particles are left in the files, and they get spawned by name when a bullet gets created. 76 | PPC_FUNC_IMPL(__imp__sub_82B14CC0); 77 | PPC_FUNC(sub_82B14CC0) 78 | { 79 | auto isPlayStation = Config::ControllerIcons == EControllerIcons::PlayStation; 80 | 81 | if (Config::ControllerIcons == EControllerIcons::Auto) 82 | isPlayStation = hid::g_inputDeviceController == hid::EInputDevice::PlayStation; 83 | 84 | if (isPlayStation) 85 | { 86 | PPC_STORE_U8(0x820C2A0B, 'b'); // Cross 87 | PPC_STORE_U8(0x820C29C3, 'r'); // Circle 88 | PPC_STORE_U8(0x820C29DB, 'p'); // Square 89 | PPC_STORE_U8(0x820C29F3, 'g'); // Triangle 90 | } 91 | else 92 | { 93 | PPC_STORE_U8(0x820C2A0B, 'g'); // A 94 | PPC_STORE_U8(0x820C29C3, 'r'); // B 95 | PPC_STORE_U8(0x820C29DB, 'b'); // X 96 | PPC_STORE_U8(0x820C29F3, 'o'); // Y 97 | } 98 | 99 | __imp__sub_82B14CC0(ctx, base); 100 | } 101 | 102 | // CObjGrindDashPanel is particularly egregious when it comes to overlapping sounds at HFR 103 | // due to the character proxy sending the hit message multiple times in a frame. This is a 104 | // quick workaround to limit the message process function to occur at a 30 FPS time step. 105 | static constexpr size_t OBJ_GRIND_DASH_PANEL_SIZE = 0x160; 106 | 107 | void ObjGrindDashPanelAllocMidAsmHook(PPCRegister& r3) 108 | { 109 | r3.u32 += sizeof(double); 110 | } 111 | 112 | // SWA::CObjGrindDashPanel::CObjGrindDashPanel 113 | PPC_FUNC_IMPL(__imp__sub_82614228); 114 | PPC_FUNC(sub_82614228) 115 | { 116 | *reinterpret_cast(base + ctx.r3.u32 + OBJ_GRIND_DASH_PANEL_SIZE) = 0.0; 117 | __imp__sub_82614228(ctx, base); 118 | } 119 | 120 | // SWA::CObjGrindDashPanel::MsgHitEventCollision::Impl 121 | PPC_FUNC_IMPL(__imp__sub_826145D8); 122 | PPC_FUNC(sub_826145D8) 123 | { 124 | constexpr double REFERENCE_DELTA_TIME = 1.0 / 30.0; 125 | constexpr double DELTA_TIME_TOLERANCE = 0.0001; 126 | 127 | auto lastHitTime = reinterpret_cast(base + ctx.r3.u32 + OBJ_GRIND_DASH_PANEL_SIZE); 128 | auto deltaTime = App::s_time - *lastHitTime; 129 | 130 | if ((deltaTime + DELTA_TIME_TOLERANCE) > REFERENCE_DELTA_TIME) 131 | { 132 | __imp__sub_826145D8(ctx, base); 133 | *lastHitTime = App::s_time; 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /UnleashedRecomp/patches/player_patches.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | static uint32_t g_lastEnemyScore; 9 | static uint32_t g_lastTrickScore; 10 | static float g_lastDarkGaiaEnergy; 11 | static bool g_isUnleashCancelled; 12 | 13 | /* Hook function for when checkpoints are activated 14 | to preserve the current checkpoint score. */ 15 | PPC_FUNC_IMPL(__imp__sub_82624308); 16 | PPC_FUNC(sub_82624308) 17 | { 18 | __imp__sub_82624308(ctx, base); 19 | 20 | if (!Config::SaveScoreAtCheckpoints) 21 | return; 22 | 23 | if (auto pGameDocument = SWA::CGameDocument::GetInstance()) 24 | { 25 | g_lastEnemyScore = pGameDocument->m_pMember->m_ScoreInfo.EnemyScore; 26 | g_lastTrickScore = pGameDocument->m_pMember->m_ScoreInfo.TrickScore; 27 | 28 | LOGFN("Score: {}", g_lastEnemyScore + g_lastTrickScore); 29 | } 30 | } 31 | 32 | /* Hook function that resets the score 33 | and restore the last checkpoint score. */ 34 | PPC_FUNC_IMPL(__imp__sub_8245F048); 35 | PPC_FUNC(sub_8245F048) 36 | { 37 | __imp__sub_8245F048(ctx, base); 38 | 39 | if (!Config::SaveScoreAtCheckpoints) 40 | return; 41 | 42 | if (auto pGameDocument = SWA::CGameDocument::GetInstance()) 43 | { 44 | LOGFN("Score: {}", g_lastEnemyScore + g_lastTrickScore); 45 | 46 | pGameDocument->m_pMember->m_ScoreInfo.EnemyScore = g_lastEnemyScore; 47 | pGameDocument->m_pMember->m_ScoreInfo.TrickScore = g_lastTrickScore; 48 | } 49 | } 50 | 51 | void ResetScoreOnRestartMidAsmHook() 52 | { 53 | g_lastEnemyScore = 0; 54 | g_lastTrickScore = 0; 55 | } 56 | 57 | // Dark Gaia energy change hook. 58 | PPC_FUNC_IMPL(__imp__sub_823AF7A8); 59 | PPC_FUNC(sub_823AF7A8) 60 | { 61 | auto pEvilSonicContext = (SWA::Player::CEvilSonicContext*)g_memory.Translate(ctx.r3.u32); 62 | 63 | g_lastDarkGaiaEnergy = pEvilSonicContext->m_DarkGaiaEnergy; 64 | 65 | // Don't drain energy if out of control. 66 | if (Config::FixUnleashOutOfControlDrain && pEvilSonicContext->m_OutOfControlCount && ctx.f1.f64 < 0.0) 67 | return; 68 | 69 | __imp__sub_823AF7A8(ctx, base); 70 | 71 | if (!Config::AllowCancellingUnleash) 72 | return; 73 | 74 | auto pInputState = SWA::CInputState::GetInstance(); 75 | 76 | // Don't allow cancelling Unleash if the intro anim is still playing. 77 | if (!pInputState || pEvilSonicContext->m_AnimationID == 39) 78 | return; 79 | 80 | if (pInputState->GetPadState().IsTapped(SWA::eKeyState_RightBumper)) 81 | { 82 | pEvilSonicContext->m_DarkGaiaEnergy = 0.0f; 83 | g_isUnleashCancelled = true; 84 | } 85 | } 86 | 87 | void PostUnleashMidAsmHook(PPCRegister& r30) 88 | { 89 | if (!g_isUnleashCancelled) 90 | return; 91 | 92 | if (auto pEvilSonicContext = (SWA::Player::CEvilSonicContext*)g_memory.Translate(r30.u32)) 93 | pEvilSonicContext->m_DarkGaiaEnergy = std::max(0.0f, g_lastDarkGaiaEnergy - 35.0f); 94 | 95 | g_isUnleashCancelled = false; 96 | } 97 | 98 | // SWA::Player::CEvilSonicContext 99 | PPC_FUNC_IMPL(__imp__sub_823B49D8); 100 | PPC_FUNC(sub_823B49D8) 101 | { 102 | __imp__sub_823B49D8(ctx, base); 103 | 104 | App::s_isWerehog = true; 105 | 106 | SDL_User_EvilSonic(true); 107 | } 108 | 109 | // ~SWA::Player::CEvilSonicContext 110 | PPC_FUNC_IMPL(__imp__sub_823B4590); 111 | PPC_FUNC(sub_823B4590) 112 | { 113 | __imp__sub_823B4590(ctx, base); 114 | 115 | App::s_isWerehog = false; 116 | 117 | SDL_User_EvilSonic(false); 118 | } 119 | -------------------------------------------------------------------------------- /UnleashedRecomp/preload_executable.cpp: -------------------------------------------------------------------------------- 1 | #include "preload_executable.h" 2 | #include 3 | 4 | // Code from Zelda 64: Recompiled 5 | // https://github.com/Zelda64Recomp/Zelda64Recomp/blob/91db87632c2bfb6995ef1554ec71b11977c621f8/src/main/main.cpp#L440-L514 6 | 7 | PreloadContext::~PreloadContext() 8 | { 9 | #ifdef _WIN32 10 | if (preloaded) 11 | { 12 | VirtualUnlock(view, size); 13 | CloseHandle(mappingHandle); 14 | CloseHandle(handle); 15 | } 16 | #endif 17 | } 18 | 19 | void PreloadContext::PreloadExecutable() 20 | { 21 | #ifdef _WIN32 22 | wchar_t moduleName[MAX_PATH]; 23 | GetModuleFileNameW(NULL, moduleName, MAX_PATH); 24 | 25 | handle = CreateFileW(moduleName, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); 26 | if (handle == INVALID_HANDLE_VALUE) 27 | { 28 | LOG_ERROR("Failed to load executable into memory!"); 29 | *this = {}; 30 | return; 31 | } 32 | 33 | LARGE_INTEGER moduleSize; 34 | if (!GetFileSizeEx(handle, &moduleSize)) 35 | { 36 | LOG_ERROR("Failed to get size of executable!"); 37 | CloseHandle(handle); 38 | *this = {}; 39 | return; 40 | } 41 | 42 | size = moduleSize.QuadPart; 43 | 44 | mappingHandle = CreateFileMappingW(handle, nullptr, PAGE_READONLY, 0, 0, nullptr); 45 | if (mappingHandle == nullptr) 46 | { 47 | LOG_ERROR("Failed to create file mapping of executable!"); 48 | CloseHandle(handle); 49 | *this = {}; 50 | return; 51 | } 52 | 53 | view = MapViewOfFile(mappingHandle, FILE_MAP_READ, 0, 0, 0); 54 | if (view == nullptr) 55 | { 56 | LOG_ERROR("Failed to map view of of executable!"); 57 | CloseHandle(mappingHandle); 58 | CloseHandle(handle); 59 | *this = {}; 60 | return; 61 | } 62 | 63 | DWORD pid = GetCurrentProcessId(); 64 | HANDLE processHandle = OpenProcess(PROCESS_SET_QUOTA | PROCESS_QUERY_INFORMATION, FALSE, pid); 65 | if (processHandle == nullptr) 66 | { 67 | LOG_ERROR("Failed to open own process!"); 68 | CloseHandle(mappingHandle); 69 | CloseHandle(handle); 70 | *this = {}; 71 | return; 72 | } 73 | 74 | SIZE_T minimumSetSize, maximumSetSize; 75 | if (!GetProcessWorkingSetSize(processHandle, &minimumSetSize, &maximumSetSize)) 76 | { 77 | LOG_ERROR("Failed to get working set size!"); 78 | CloseHandle(mappingHandle); 79 | CloseHandle(handle); 80 | *this = {}; 81 | return; 82 | } 83 | 84 | if (!SetProcessWorkingSetSize(processHandle, minimumSetSize + size, maximumSetSize + size)) 85 | { 86 | LOG_ERROR("Failed to set working set size!"); 87 | CloseHandle(mappingHandle); 88 | CloseHandle(handle); 89 | *this = {}; 90 | return; 91 | } 92 | 93 | if (VirtualLock(view, size) == 0) 94 | { 95 | LOGF_ERROR("Failed to lock view of executable! (Error: 0x{:X})\n", GetLastError()); 96 | CloseHandle(mappingHandle); 97 | CloseHandle(handle); 98 | *this = {}; 99 | return; 100 | } 101 | 102 | preloaded = true; 103 | #endif 104 | } 105 | -------------------------------------------------------------------------------- /UnleashedRecomp/preload_executable.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct PreloadContext 4 | { 5 | #ifdef _WIN32 6 | HANDLE handle{}; 7 | HANDLE mappingHandle{}; 8 | SIZE_T size{}; 9 | PVOID view{}; 10 | bool preloaded{}; 11 | #endif 12 | 13 | ~PreloadContext(); 14 | void PreloadExecutable(); 15 | }; 16 | -------------------------------------------------------------------------------- /UnleashedRecomp/res/.gitignore: -------------------------------------------------------------------------------- 1 | ![Ww][Ii][Nn]32/ 2 | *.c 3 | *.h 4 | !credits.h -------------------------------------------------------------------------------- /UnleashedRecomp/res/credits.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | inline std::array g_credits = 4 | { 5 | "Skyth", 6 | "Sajid", 7 | "Hyper", 8 | "Darío", 9 | "DeaThProj", 10 | "RadiantDerg", 11 | "PTKay", 12 | "SuperSonic16", 13 | "NextinHKRY", 14 | "LadyLunanova", 15 | "LJSTAR", 16 | "saguinee", 17 | "Goalringmod27", 18 | "M&M", 19 | "DaGuAr", 20 | "brianuuuSonic", 21 | "Kitzuku" 22 | }; 23 | -------------------------------------------------------------------------------- /UnleashedRecomp/res/version.cpp.template: -------------------------------------------------------------------------------- 1 | #include "version.h" 2 | 3 | // This file is auto-generated, do not modify! 4 | 5 | const char* g_buildType = "@BUILD_TYPE@"; 6 | 7 | const char* g_branchName = "@BRANCH_NAME@"; 8 | const char* g_commitHash = "@COMMIT_HASH@"; 9 | const char* g_commitHashShort = "@COMMIT_HASH_SHORT@"; 10 | 11 | const char* g_versionMilestone = "@VERSION_MILESTONE@"; 12 | size_t g_versionMajor = @VERSION_MAJOR@; 13 | size_t g_versionMinor = @VERSION_MINOR@; 14 | size_t g_versionRevision = @VERSION_REVISION@; 15 | const char* g_versionString = "@VERSION_STRING@"; 16 | -------------------------------------------------------------------------------- /UnleashedRecomp/res/version.h.template: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // This file is auto-generated, do not modify! 4 | 5 | extern const char* g_buildType; 6 | 7 | extern const char* g_branchName; 8 | extern const char* g_commitHash; 9 | extern const char* g_commitHashShort; 10 | 11 | extern const char* g_versionMilestone; 12 | extern size_t g_versionMajor; 13 | extern size_t g_versionMinor; 14 | extern size_t g_versionRevision; 15 | extern const char* g_versionString; 16 | -------------------------------------------------------------------------------- /UnleashedRecomp/res/version.txt: -------------------------------------------------------------------------------- 1 | VERSION_MILESTONE="" 2 | VERSION_MAJOR=1 3 | VERSION_MINOR=0 4 | VERSION_REVISION=3 5 | -------------------------------------------------------------------------------- /UnleashedRecomp/res/win32/res.rc.template: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define UNLEASHED_RECOMP_VERSION_BIN @WIN32_VERSION_BINARY@ 4 | #define UNLEASHED_RECOMP_VERSION_STR "@WIN32_VERSION_STRING@" 5 | 6 | #ifdef _DEBUG 7 | #define UNLEASHED_RECOMP_FILE_FLAGS VS_FF_DEBUG 8 | #else 9 | #define UNLEASHED_RECOMP_FILE_FLAGS 0 10 | #endif 11 | 12 | IDI_ICON1 ICON "@WIN32_ICON_PATH@" 13 | 14 | VS_VERSION_INFO VERSIONINFO 15 | FILEVERSION UNLEASHED_RECOMP_VERSION_BIN 16 | PRODUCTVERSION UNLEASHED_RECOMP_VERSION_BIN 17 | FILEFLAGSMASK VS_FFI_FILEFLAGSMASK 18 | FILEFLAGS UNLEASHED_RECOMP_FILE_FLAGS 19 | FILEOS VOS_NT_WINDOWS32 20 | FILETYPE VFT_APP 21 | FILESUBTYPE VFT2_UNKNOWN 22 | BEGIN 23 | BLOCK "StringFileInfo" 24 | BEGIN 25 | BLOCK "080904B0" // English (UK), Unicode 26 | BEGIN 27 | VALUE "ProductName", "Unleashed Recompiled" 28 | VALUE "FileDescription", "Unleashed Recompiled" 29 | VALUE "CompanyName", "hedge-dev" 30 | VALUE "FileVersion", UNLEASHED_RECOMP_VERSION_STR 31 | VALUE "ProductVersion", UNLEASHED_RECOMP_VERSION_STR 32 | VALUE "InternalName", "UnleashedRecomp" 33 | VALUE "OriginalFilename", "SWA.exe" 34 | END 35 | END 36 | BLOCK "VarFileInfo" 37 | BEGIN 38 | VALUE "Translation", 0x0809, 0x04B0 39 | END 40 | END 41 | -------------------------------------------------------------------------------- /UnleashedRecomp/sdl_events.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #define SDL_USER_EVILSONIC (SDL_USEREVENT + 1) 7 | 8 | inline void SDL_ResizeEvent(SDL_Window* pWindow, int width, int height) 9 | { 10 | SDL_Event event{}; 11 | event.type = SDL_WINDOWEVENT; 12 | event.window.event = SDL_WINDOWEVENT_RESIZED; 13 | event.window.windowID = SDL_GetWindowID(pWindow); 14 | event.window.data1 = width; 15 | event.window.data2 = height; 16 | 17 | SDL_PushEvent(&event); 18 | } 19 | 20 | inline void SDL_MoveEvent(SDL_Window* pWindow, int x, int y) 21 | { 22 | SDL_Event event{}; 23 | event.type = SDL_WINDOWEVENT; 24 | event.window.event = SDL_WINDOWEVENT_MOVED; 25 | event.window.windowID = SDL_GetWindowID(pWindow); 26 | event.window.data1 = x; 27 | event.window.data2 = y; 28 | 29 | SDL_PushEvent(&event); 30 | } 31 | 32 | inline void SDL_User_EvilSonic(bool isEvil) 33 | { 34 | SDL_Event event{}; 35 | event.type = SDL_USER_EVILSONIC; 36 | event.user.code = isEvil; 37 | 38 | SDL_PushEvent(&event); 39 | } 40 | -------------------------------------------------------------------------------- /UnleashedRecomp/sdl_listener.cpp: -------------------------------------------------------------------------------- 1 | #include "sdl_listener.h" 2 | 3 | std::vector& GetEventListeners() 4 | { 5 | static std::vector g_eventListeners; 6 | return g_eventListeners; 7 | } 8 | -------------------------------------------------------------------------------- /UnleashedRecomp/sdl_listener.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class ISDLEventListener 4 | { 5 | public: 6 | virtual ~ISDLEventListener() = default; 7 | virtual bool OnSDLEvent(SDL_Event* event) = 0; 8 | }; 9 | 10 | extern std::vector& GetEventListeners(); 11 | 12 | class SDLEventListener : public ISDLEventListener 13 | { 14 | public: 15 | SDLEventListener() 16 | { 17 | GetEventListeners().emplace_back(this); 18 | } 19 | 20 | bool OnSDLEvent(SDL_Event* event) override { return false; } 21 | }; 22 | -------------------------------------------------------------------------------- /UnleashedRecomp/stdafx.cpp: -------------------------------------------------------------------------------- 1 | #define STB_IMAGE_IMPLEMENTATION 2 | #include 3 | 4 | #include "stdafx.h" 5 | -------------------------------------------------------------------------------- /UnleashedRecomp/stdafx.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define NOMINMAX 4 | 5 | #if defined(_WIN32) 6 | #include 7 | #include 8 | #include 9 | 10 | using Microsoft::WRL::ComPtr; 11 | #elif defined(__linux__) 12 | #include 13 | #include 14 | #endif 15 | 16 | #ifdef UNLEASHED_RECOMP_D3D12 17 | #include 18 | #endif 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | 54 | #include "framework.h" 55 | #include "mutex.h" 56 | 57 | #ifndef _WIN32 58 | #include 59 | #endif 60 | -------------------------------------------------------------------------------- /UnleashedRecomp/ui/achievement_menu.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class AchievementMenu 4 | { 5 | public: 6 | inline static bool s_isVisible = false; 7 | 8 | static void Init(); 9 | static void Draw(); 10 | static void Open(); 11 | static void Close(); 12 | }; 13 | -------------------------------------------------------------------------------- /UnleashedRecomp/ui/achievement_overlay.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class AchievementOverlay 6 | { 7 | public: 8 | static inline bool s_isVisible = false; 9 | 10 | static inline std::queue s_queue{}; 11 | 12 | static void Init(); 13 | static void Draw(); 14 | static void Open(int id); 15 | static void Close(); 16 | }; 17 | -------------------------------------------------------------------------------- /UnleashedRecomp/ui/black_bar.cpp: -------------------------------------------------------------------------------- 1 | #include "black_bar.h" 2 | #include 3 | 4 | void BlackBar::Draw() 5 | { 6 | if (g_inspirePillarbox) 7 | { 8 | auto drawList = ImGui::GetBackgroundDrawList(); 9 | auto& res = ImGui::GetIO().DisplaySize; 10 | 11 | float width = (res.x - (res.y * 16.0f / 9.0f)) / 2.0f; 12 | 13 | drawList->AddRectFilled( 14 | { 0.0f, 0.0f }, 15 | { width, res.y }, 16 | IM_COL32_BLACK); 17 | 18 | drawList->AddRectFilled( 19 | { res.x - width, 0.0f }, 20 | res, 21 | IM_COL32_BLACK); 22 | 23 | g_inspirePillarbox = false; 24 | } 25 | 26 | if (g_loadingBlackBarAlpha != 0) 27 | { 28 | auto drawList = ImGui::GetBackgroundDrawList(); 29 | auto& res = ImGui::GetIO().DisplaySize; 30 | 31 | if (g_aspectRatio > WIDE_ASPECT_RATIO) 32 | { 33 | drawList->AddRectFilled( 34 | { 0.0f, 0.0f }, 35 | { g_loadingBlackBarMin.x, g_loadingBlackBarMax.y }, 36 | IM_COL32(0, 0, 0, g_loadingBlackBarAlpha)); 37 | 38 | drawList->AddRectFilled( 39 | { g_loadingBlackBarMax.x, g_loadingBlackBarMin.y }, 40 | res, 41 | IM_COL32(0, 0, 0, g_loadingBlackBarAlpha)); 42 | } 43 | else if (g_aspectRatio < NARROW_ASPECT_RATIO) 44 | { 45 | drawList->AddRectFilled( 46 | { 0.0f, 0.0f }, 47 | { g_loadingBlackBarMax.x, g_loadingBlackBarMin.y }, 48 | IM_COL32(0, 0, 0, g_loadingBlackBarAlpha)); 49 | 50 | drawList->AddRectFilled( 51 | { g_loadingBlackBarMin.x, g_loadingBlackBarMax.y }, 52 | res, 53 | IM_COL32(0, 0, 0, g_loadingBlackBarAlpha)); 54 | } 55 | 56 | g_loadingBlackBarAlpha = 0; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /UnleashedRecomp/ui/black_bar.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct BlackBar 4 | { 5 | static inline bool g_inspirePillarbox; 6 | 7 | static inline ImVec2 g_loadingBlackBarMin; 8 | static inline ImVec2 g_loadingBlackBarMax; 9 | static inline uint8_t g_loadingBlackBarAlpha; 10 | 11 | static void Draw(); 12 | }; 13 | -------------------------------------------------------------------------------- /UnleashedRecomp/ui/button_guide.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | enum class EButtonIcon 6 | { 7 | // Controller 8 | A, 9 | B, 10 | X, 11 | Y, 12 | LB, 13 | RB, 14 | LBRB, 15 | LT, 16 | RT, 17 | LTRT, 18 | Start, 19 | Back, 20 | 21 | // Keyboard + Mouse (temporary) 22 | LMB, 23 | Enter, 24 | Escape 25 | }; 26 | 27 | enum class EButtonAlignment 28 | { 29 | Left, 30 | Right 31 | }; 32 | 33 | enum class EFontQuality 34 | { 35 | Low, 36 | High 37 | }; 38 | 39 | class Button 40 | { 41 | public: 42 | std::string Name{}; 43 | float MaxWidth{ FLT_MAX }; 44 | EButtonIcon Icon{}; 45 | EButtonAlignment Alignment{ EButtonAlignment::Right }; 46 | EFontQuality FontQuality{ EFontQuality::High }; 47 | bool* Visibility{ nullptr }; 48 | 49 | Button(std::string name, float maxWidth, EButtonIcon icon, EButtonAlignment alignment, EFontQuality fontQuality = EFontQuality::High, bool* visibility = nullptr) 50 | : Name(name), MaxWidth(maxWidth), Icon(icon), Alignment(alignment), FontQuality(fontQuality), Visibility(visibility) {} 51 | 52 | Button(std::string name, float maxWidth, EButtonIcon icon, EButtonAlignment alignment, bool* visibility) 53 | : Name(name), MaxWidth(maxWidth), Icon(icon), Alignment(alignment), Visibility(visibility) {} 54 | 55 | Button(std::string name, float maxWidth, EButtonIcon icon, bool* visibility) 56 | : Name(name), MaxWidth(maxWidth), Icon(icon), Visibility(visibility) {} 57 | 58 | Button(std::string name, float maxWidth, EButtonIcon icon, EFontQuality fontQuality = EFontQuality::High) 59 | : Name(name), MaxWidth(maxWidth), Icon(icon), FontQuality(fontQuality) {} 60 | }; 61 | 62 | class ButtonGuide 63 | { 64 | public: 65 | static inline bool s_isVisible = false; 66 | 67 | static void Init(); 68 | static void Draw(); 69 | static void Open(Button button); 70 | static void Open(const std::span