Compare commits

..

1 Commits

Author SHA1 Message Date
Hyper 4a33883512 hid: implemented delay input for switching devices
This makes controllers have their first input ignored until after detection, much like other PC ports.

This probably won't end up being merged in though, as other Sonic PC ports don't seem to work this way, but I'll push it anyway just for the sake of having this somewhere.
2025-02-16 19:21:58 +00:00
55 changed files with 2096 additions and 1569 deletions
+6
View File
@@ -7,6 +7,9 @@
[submodule "tools/XenosRecomp"] [submodule "tools/XenosRecomp"]
path = tools/XenosRecomp path = tools/XenosRecomp
url = https://github.com/hedge-dev/XenosRecomp.git url = https://github.com/hedge-dev/XenosRecomp.git
[submodule "thirdparty/libmspack"]
path = thirdparty/libmspack
url = https://github.com/kyz/libmspack
[submodule "UnleashedRecompResources"] [submodule "UnleashedRecompResources"]
path = UnleashedRecompResources path = UnleashedRecompResources
url = https://github.com/hedge-dev/UnleashedRecompResources.git url = https://github.com/hedge-dev/UnleashedRecompResources.git
@@ -37,6 +40,9 @@
[submodule "thirdparty/concurrentqueue"] [submodule "thirdparty/concurrentqueue"]
path = thirdparty/concurrentqueue path = thirdparty/concurrentqueue
url = https://github.com/cameron314/concurrentqueue.git url = https://github.com/cameron314/concurrentqueue.git
[submodule "thirdparty/tiny-AES-c"]
path = thirdparty/tiny-AES-c
url = https://github.com/kokke/tiny-AES-c.git
[submodule "thirdparty/magic_enum"] [submodule "thirdparty/magic_enum"]
path = thirdparty/magic_enum path = thirdparty/magic_enum
url = https://github.com/Neargye/magic_enum.git url = https://github.com/Neargye/magic_enum.git
+1 -1
View File
@@ -4,7 +4,7 @@
``` ```
git clone --recurse-submodules https://github.com/hedge-dev/UnleashedRecomp.git git clone --recurse-submodules https://github.com/hedge-dev/UnleashedRecomp.git
``` ```
2. Place `default.xex` and `default.xexp` in `./UnleashedRecompLib/private/`. 2. Decompress and decrypt `default.xex`, apply the title update patch (`default.xexp`), and place the resulting file in `./UnleashedRecompLib/private/`.
3. Decompress `shader.ar` and place the resulting file in `./UnleashedRecompLib/private/`. 3. Decompress `shader.ar` and place the resulting file in `./UnleashedRecompLib/private/`.
4. Open the repository directory in Visual Studio 2022 and wait for CMake generation to complete. If you don't plan to debug, switch to the `x64-Clang-Release` configuration. 4. Open the repository directory in Visual Studio 2022 and wait for CMake generation to complete. If you don't plan to debug, switch to the `x64-Clang-Release` configuration.
5. Under Solution Explorer, right-click and choose "Switch to CMake Targets View". 5. Under Solution Explorer, right-click and choose "Switch to CMake Targets View".
+15 -6
View File
@@ -165,8 +165,10 @@ set(UNLEASHED_RECOMP_UI_CXX_SOURCES
set(UNLEASHED_RECOMP_INSTALL_CXX_SOURCES set(UNLEASHED_RECOMP_INSTALL_CXX_SOURCES
"install/installer.cpp" "install/installer.cpp"
"install/iso_file_system.cpp" "install/iso_file_system.cpp"
"install/memory_mapped_file.cpp"
"install/update_checker.cpp" "install/update_checker.cpp"
"install/xcontent_file_system.cpp" "install/xcontent_file_system.cpp"
"install/xex_patcher.cpp"
"install/hashes/apotos_shamar.cpp" "install/hashes/apotos_shamar.cpp"
"install/hashes/chunnan.cpp" "install/hashes/chunnan.cpp"
"install/hashes/empire_city_adabat.cpp" "install/hashes/empire_city_adabat.cpp"
@@ -199,6 +201,8 @@ set(UNLEASHED_RECOMP_THIRDPARTY_SOURCES
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/implot/implot.cpp" "${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/implot/implot.cpp"
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/implot/implot_demo.cpp" "${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/implot/implot_demo.cpp"
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/implot/implot_items.cpp" "${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/implot/implot_items.cpp"
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/libmspack/libmspack/mspack/lzxd.c"
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/tiny-AES-c/aes.c"
"${UNLEASHED_RECOMP_TOOLS_ROOT}/XenosRecomp/thirdparty/smol-v/source/smolv.cpp" "${UNLEASHED_RECOMP_TOOLS_ROOT}/XenosRecomp/thirdparty/smol-v/source/smolv.cpp"
) )
@@ -208,8 +212,11 @@ set(UNLEASHED_RECOMP_THIRDPARTY_INCLUDES
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/imgui" "${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/imgui"
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/implot" "${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/implot"
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/json/include" "${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/json/include"
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/libmspack/libmspack/mspack"
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/magic_enum/include" "${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/magic_enum/include"
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/stb" "${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/stb"
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/tiny-AES-c"
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/TinySHA1"
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/unordered_dense/include" "${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/unordered_dense/include"
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/volk" "${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/volk"
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/Vulkan-Headers/include" "${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/Vulkan-Headers/include"
@@ -493,8 +500,8 @@ BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/in
## Options Menu ## ## Options Menu ##
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/achievement_notifications.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/achievement_notifications.dds" ARRAY_NAME "g_achievement_notifications" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/achievement_notifications.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/achievement_notifications.dds" ARRAY_NAME "g_achievement_notifications" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/allow_background_input_ps.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/allow_background_input_ps.dds" ARRAY_NAME "g_allow_background_input_ps" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/allow_background_input.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/allow_background_input.dds" ARRAY_NAME "g_allow_background_input" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/allow_background_input_xb.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/allow_background_input_xb.dds" ARRAY_NAME "g_allow_background_input_xb" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/allow_dpad_movement.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/allow_dpad_movement.dds" ARRAY_NAME "g_allow_dpad_movement" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/antialiasing_none.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/antialiasing_none.dds" ARRAY_NAME "g_antialiasing_none" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/antialiasing_none.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/antialiasing_none.dds" ARRAY_NAME "g_antialiasing_none" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/antialiasing_2x.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/antialiasing_2x.dds" ARRAY_NAME "g_antialiasing_2x" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/antialiasing_2x.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/antialiasing_2x.dds" ARRAY_NAME "g_antialiasing_2x" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/antialiasing_4x.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/antialiasing_4x.dds" ARRAY_NAME "g_antialiasing_4x" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/antialiasing_4x.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/antialiasing_4x.dds" ARRAY_NAME "g_antialiasing_4x" COMPRESSION_TYPE "zstd")
@@ -504,8 +511,8 @@ BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/op
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/brightness.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/brightness.dds" ARRAY_NAME "g_brightness" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/brightness.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/brightness.dds" ARRAY_NAME "g_brightness" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/channel_stereo.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/channel_stereo.dds" ARRAY_NAME "g_channel_stereo" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/channel_stereo.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/channel_stereo.dds" ARRAY_NAME "g_channel_stereo" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/channel_surround.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/channel_surround.dds" ARRAY_NAME "g_channel_surround" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/channel_surround.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/channel_surround.dds" ARRAY_NAME "g_channel_surround" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/control_tutorial_ps.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/control_tutorial_ps.dds" ARRAY_NAME "g_control_tutorial_ps" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/control_tutorial_xb.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/control_tutorial_xb.dds" ARRAY_NAME "g_control_tutorial_xb" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/control_tutorial_xb.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/control_tutorial_xb.dds" ARRAY_NAME "g_control_tutorial_xb" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/control_tutorial_ps.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/control_tutorial_ps.dds" ARRAY_NAME "g_control_tutorial_ps" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/controller_icons.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/controller_icons.dds" ARRAY_NAME "g_controller_icons" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/controller_icons.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/controller_icons.dds" ARRAY_NAME "g_controller_icons" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/default.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/default.dds" ARRAY_NAME "g_default" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/default.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/default.dds" ARRAY_NAME "g_default" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/effects_volume.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/effects_volume.dds" ARRAY_NAME "g_effects_volume" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/effects_volume.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/effects_volume.dds" ARRAY_NAME "g_effects_volume" COMPRESSION_TYPE "zstd")
@@ -525,21 +532,23 @@ BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/op
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/movie_scale_fill.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/movie_scale_fill.dds" ARRAY_NAME "g_movie_scale_fill" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/movie_scale_fill.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/movie_scale_fill.dds" ARRAY_NAME "g_movie_scale_fill" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/music_attenuation.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/music_attenuation.dds" ARRAY_NAME "g_music_attenuation" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/music_attenuation.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/music_attenuation.dds" ARRAY_NAME "g_music_attenuation" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/music_volume.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/music_volume.dds" ARRAY_NAME "g_music_volume" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/music_volume.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/music_volume.dds" ARRAY_NAME "g_music_volume" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/resolution_scale.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/resolution_scale.dds" ARRAY_NAME "g_resolution_scale" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/shadow_resolution_original.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/shadow_resolution_original.dds" ARRAY_NAME "g_shadow_resolution_original" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/shadow_resolution_x512.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/shadow_resolution_x512.dds" ARRAY_NAME "g_shadow_resolution_x512" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/shadow_resolution_x512.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/shadow_resolution_x512.dds" ARRAY_NAME "g_shadow_resolution_x512" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/shadow_resolution_x1024.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/shadow_resolution_x1024.dds" ARRAY_NAME "g_shadow_resolution_x1024" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/shadow_resolution_x1024.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/shadow_resolution_x1024.dds" ARRAY_NAME "g_shadow_resolution_x1024" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/shadow_resolution_x2048.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/shadow_resolution_x2048.dds" ARRAY_NAME "g_shadow_resolution_x2048" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/shadow_resolution_x2048.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/shadow_resolution_x2048.dds" ARRAY_NAME "g_shadow_resolution_x2048" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/shadow_resolution_x4096.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/shadow_resolution_x4096.dds" ARRAY_NAME "g_shadow_resolution_x4096" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/shadow_resolution_x4096.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/shadow_resolution_x4096.dds" ARRAY_NAME "g_shadow_resolution_x4096" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/shadow_resolution_x8192.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/shadow_resolution_x8192.dds" ARRAY_NAME "g_shadow_resolution_x8192" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/shadow_resolution_x8192.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/shadow_resolution_x8192.dds" ARRAY_NAME "g_shadow_resolution_x8192" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/time_transition_ps.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/time_transition_ps.dds" ARRAY_NAME "g_time_of_day_transition_playstation" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/subtitles.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/subtitles.dds" ARRAY_NAME "g_subtitles" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/time_transition_xb.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/time_transition_xb.dds" ARRAY_NAME "g_time_of_day_transition_xbox" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/time_transition_xb.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/time_transition_xb.dds" ARRAY_NAME "g_time_of_day_transition_xbox" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/time_transition_ps.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/time_transition_ps.dds" ARRAY_NAME "g_time_of_day_transition_playstation" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/transparency_antialiasing_false.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/transparency_antialiasing_false.dds" ARRAY_NAME "g_transparency_antialiasing_false" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/transparency_antialiasing_false.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/transparency_antialiasing_false.dds" ARRAY_NAME "g_transparency_antialiasing_false" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/transparency_antialiasing_true.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/transparency_antialiasing_true.dds" ARRAY_NAME "g_transparency_antialiasing_true" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/transparency_antialiasing_true.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/transparency_antialiasing_true.dds" ARRAY_NAME "g_transparency_antialiasing_true" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/ui_alignment_centre.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/ui_alignment_centre.dds" ARRAY_NAME "g_ui_alignment_centre" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/ui_alignment_centre.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/ui_alignment_centre.dds" ARRAY_NAME "g_ui_alignment_centre" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/ui_alignment_edge.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/ui_alignment_edge.dds" ARRAY_NAME "g_ui_alignment_edge" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/ui_alignment_edge.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/ui_alignment_edge.dds" ARRAY_NAME "g_ui_alignment_edge" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/vertical_camera.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/vertical_camera.dds" ARRAY_NAME "g_vertical_camera" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/vertical_camera.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/vertical_camera.dds" ARRAY_NAME "g_vertical_camera" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/voice_language.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/voice_language.dds" ARRAY_NAME "g_voice_language" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/voice_language.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/voice_language.dds" ARRAY_NAME "g_voice_language" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/vibration_ps.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/vibration_ps.dds" ARRAY_NAME "g_vibration_ps" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/vibration.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/vibration.dds" ARRAY_NAME "g_vibration" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/vibration_xb.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/vibration_xb.dds" ARRAY_NAME "g_vibration_xb" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/vsync_on.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/vsync_on.dds" ARRAY_NAME "g_vsync_on" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/vsync_on.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/vsync_on.dds" ARRAY_NAME "g_vsync_on" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/vsync_off.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/vsync_off.dds" ARRAY_NAME "g_vsync_off" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/vsync_off.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/vsync_off.dds" ARRAY_NAME "g_vsync_off" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/window_size.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/window_size.dds" ARRAY_NAME "g_window_size" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/window_size.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/window_size.dds" ARRAY_NAME "g_window_size" COMPRESSION_TYPE "zstd")
-1
View File
@@ -60,7 +60,6 @@ PPC_FUNC(sub_822C1130)
} }
App::s_deltaTime = ctx.f1.f64; App::s_deltaTime = ctx.f1.f64;
App::s_time += App::s_deltaTime;
// This function can also be called by the loading thread, // This function can also be called by the loading thread,
// which SDL does not like. To prevent the OS from thinking // which SDL does not like. To prevent the OS from thinking
-1
View File
@@ -14,7 +14,6 @@ public:
static inline ELanguage s_language; static inline ELanguage s_language;
static inline double s_deltaTime; static inline double s_deltaTime;
static inline double s_time = 0.0; // How much time elapsed since the game started.
static void Restart(std::vector<std::string> restartArgs = {}); static void Restart(std::vector<std::string> restartArgs = {});
static void Exit(); static void Exit();
+1 -11
View File
@@ -1,7 +1,6 @@
#include <apu/audio.h> #include <apu/audio.h>
#include <cpu/guest_thread.h> #include <cpu/guest_thread.h>
#include <kernel/heap.h> #include <kernel/heap.h>
#include <os/logger.h>
#include <user/config.h> #include <user/config.h>
static PPCFunc* g_clientCallback{}; static PPCFunc* g_clientCallback{};
@@ -30,9 +29,6 @@ static void CreateAudioDevice()
g_audioDevice = SDL_OpenAudioDevice(nullptr, 0, &desired, &obtained, 0); g_audioDevice = SDL_OpenAudioDevice(nullptr, 0, &desired, &obtained, 0);
} }
if (!g_audioDevice)
LOGFN_ERROR("Failed to open audio device: {}", SDL_GetError());
g_downMixToStereo = (obtained.channels == 2); g_downMixToStereo = (obtained.channels == 2);
} }
@@ -40,13 +36,7 @@ void XAudioInitializeSystem()
{ {
SDL_SetHint(SDL_HINT_AUDIO_CATEGORY, "playback"); SDL_SetHint(SDL_HINT_AUDIO_CATEGORY, "playback");
SDL_SetHint(SDL_HINT_AUDIO_DEVICE_APP_NAME, "Unleashed Recompiled"); SDL_SetHint(SDL_HINT_AUDIO_DEVICE_APP_NAME, "Unleashed Recompiled");
SDL_InitSubSystem(SDL_INIT_AUDIO);
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
{
LOGFN_ERROR("Failed to init audio subsystem: {}", SDL_GetError());
return;
}
CreateAudioDevice(); CreateAudioDevice();
} }
-1
View File
@@ -11,7 +11,6 @@
#define IMGUI_SHADER_MODIFIER_TITLE_BEVEL 8 #define IMGUI_SHADER_MODIFIER_TITLE_BEVEL 8
#define IMGUI_SHADER_MODIFIER_CATEGORY_BEVEL 9 #define IMGUI_SHADER_MODIFIER_CATEGORY_BEVEL 9
#define IMGUI_SHADER_MODIFIER_RECTANGLE_BEVEL 10 #define IMGUI_SHADER_MODIFIER_RECTANGLE_BEVEL 10
#define IMGUI_SHADER_MODIFIER_LOW_QUALITY_TEXT 11
#ifdef __cplusplus #ifdef __cplusplus
@@ -220,8 +220,6 @@ static bool FontBuilder_Build(ImFontAtlas* atlas)
for (size_t i = 0; i < atlas->ConfigData.size(); i++) for (size_t i = 0; i < atlas->ConfigData.size(); i++)
{ {
double spaceAdvance = 0.0;
auto& config = atlas->ConfigData[i]; auto& config = atlas->ConfigData[i];
bool increaseSpacing = strstr(config.Name, "Seurat") != nullptr; bool increaseSpacing = strstr(config.Name, "Seurat") != nullptr;
@@ -234,15 +232,8 @@ static bool FontBuilder_Build(ImFontAtlas* atlas)
glyph.getQuadAtlasBounds(u0, v0, u1, v1); glyph.getQuadAtlasBounds(u0, v0, u1, v1);
double advance = glyph.getAdvance(); double advance = glyph.getAdvance();
if (glyph.getCodepoint() == ' ') if (increaseSpacing && glyph.getCodepoint() == ' ')
{ advance *= 1.5;
if (increaseSpacing)
{
advance *= 1.5;
}
spaceAdvance = advance;
}
config.DstFont->AddGlyph( config.DstFont->AddGlyph(
&config, &config,
@@ -258,39 +249,6 @@ static bool FontBuilder_Build(ImFontAtlas* atlas)
advance); advance);
} }
// Used as a zero-width helper for automatic line breaks.
// This is useful for languages like Japanese to separate 'words'
// so that they don't get split mid-kana by the automatic splitter.
config.DstFont->AddGlyph(
&config,
0x200B,
0.0f,
0.0f,
0.0f,
0.0f,
0.0f,
0.0f,
0.0f,
0.0f,
0.0f);
// A duplicate of the normal width space character.
// Overrides the unicode Four-Per-Em Space character.
// This can be used to add visual spacers that are ignored
// by the automatic line splitting logic.
config.DstFont->AddGlyph(
&config,
0x2005,
0.0f,
0.0f,
0.0f,
0.0f,
0.0f,
0.0f,
0.0f,
0.0f,
spaceAdvance);
config.DstFont->BuildLookupTable(); config.DstFont->BuildLookupTable();
} }
@@ -12,7 +12,6 @@ struct PushConstants
uint GradientBottomLeft; uint GradientBottomLeft;
uint ShaderModifier; uint ShaderModifier;
uint Texture2DDescriptorIndex; uint Texture2DDescriptorIndex;
float2 DisplaySize;
float2 InverseDisplaySize; float2 InverseDisplaySize;
float2 Origin; float2 Origin;
float2 Scale; float2 Scale;
+38 -81
View File
@@ -61,10 +61,10 @@ float4 SampleLinear(float2 uvTexspace)
float4 PixelAntialiasing(float2 uvTexspace) float4 PixelAntialiasing(float2 uvTexspace)
{ {
if ((g_PushConstants.DisplaySize.x * g_PushConstants.InverseDisplaySize.y) >= (4.0 / 3.0)) if ((g_PushConstants.InverseDisplaySize.y / g_PushConstants.InverseDisplaySize.x) >= (4.0 / 3.0))
uvTexspace *= g_PushConstants.InverseDisplaySize.y * 720.0; uvTexspace *= g_PushConstants.InverseDisplaySize.y * 720.0f;
else else
uvTexspace *= g_PushConstants.InverseDisplaySize.x * 960.0; uvTexspace *= g_PushConstants.InverseDisplaySize.x * 960.0f;
float2 seam = floor(uvTexspace + 0.5); float2 seam = floor(uvTexspace + 0.5);
uvTexspace = (uvTexspace - seam) / fwidth(uvTexspace) + seam; uvTexspace = (uvTexspace - seam) / fwidth(uvTexspace) + seam;
@@ -78,51 +78,6 @@ float median(float r, float g, float b)
return max(min(r, g), min(max(r, g), b)); return max(min(r, g), min(max(r, g), b));
} }
float4 SampleSdfFont(float4 color, Texture2D<float4> texture, float2 uv, float2 screenTexSize)
{
float4 textureColor = texture.Sample(g_SamplerDescriptorHeap[0], uv);
uint width, height;
texture.GetDimensions(width, height);
float pxRange = 8.0;
float2 unitRange = pxRange / float2(width, height);
float screenPxRange = max(0.5 * dot(unitRange, screenTexSize), 1.0);
float sd = median(textureColor.r, textureColor.g, textureColor.b) - 0.5;
float screenPxDistance = screenPxRange * (sd + g_PushConstants.Outline / (pxRange * 1.5));
if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_TITLE_BEVEL)
{
float2 normal = normalize(float3(ddx(sd), ddy(sd), 0.01)).xy;
float3 rimColor = float3(1, 0.8, 0.29);
float3 shadowColor = float3(0.84, 0.57, 0);
float cosTheta = dot(normal, normalize(float2(1, 1)));
float3 gradient = lerp(color.rgb, cosTheta >= 0.0 ? rimColor : shadowColor, abs(cosTheta));
color.rgb = lerp(gradient, color.rgb, pow(saturate(sd + 0.77), 32.0));
}
else if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_CATEGORY_BEVEL)
{
float2 normal = normalize(float3(ddx(sd), ddy(sd), 0.25)).xy;
float cosTheta = dot(normal, normalize(float2(1, 1)));
float gradient = 1.0 + cosTheta * 0.5;
color.rgb = saturate(color.rgb * gradient);
}
else if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_TEXT_SKEW)
{
float2 normal = normalize(float3(ddx(sd), ddy(sd), 0.5)).xy;
float cosTheta = dot(normal, normalize(float2(1, 1)));
float gradient = saturate(1.0 + cosTheta);
color.rgb = lerp(color.rgb * gradient, color.rgb, pow(saturate(sd + 0.77), 32.0));
}
color.a *= saturate(screenPxDistance + 0.5);
color.a *= textureColor.a;
return color;
}
float4 main(in Interpolators interpolators) : SV_Target float4 main(in Interpolators interpolators) : SV_Target
{ {
float4 color = interpolators.Color; float4 color = interpolators.Color;
@@ -131,50 +86,52 @@ float4 main(in Interpolators interpolators) : SV_Target
if (g_PushConstants.Texture2DDescriptorIndex != 0) if (g_PushConstants.Texture2DDescriptorIndex != 0)
{ {
Texture2D<float4> texture = g_Texture2DDescriptorHeap[g_PushConstants.Texture2DDescriptorIndex & 0x7FFFFFFF]; Texture2D<float4> texture = g_Texture2DDescriptorHeap[g_PushConstants.Texture2DDescriptorIndex & 0x7FFFFFFF];
float4 textureColor = texture.Sample(g_SamplerDescriptorHeap[0], interpolators.UV);
if ((g_PushConstants.Texture2DDescriptorIndex & 0x80000000) != 0) if ((g_PushConstants.Texture2DDescriptorIndex & 0x80000000) != 0)
{ {
if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_LOW_QUALITY_TEXT) uint width, height;
texture.GetDimensions(width, height);
float pxRange = 8.0;
float2 unitRange = pxRange / float2(width, height);
float2 screenTexSize = 1.0 / fwidth(interpolators.UV);
float screenPxRange = max(0.5 * dot(unitRange, screenTexSize), 1.0);
float sd = median(textureColor.r, textureColor.g, textureColor.b) - 0.5;
float screenPxDistance = screenPxRange * (sd + g_PushConstants.Outline / (pxRange * 2.0));
if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_TITLE_BEVEL)
{ {
float scale; float2 normal = normalize(float3(ddx(sd), ddy(sd), 0.01)).xy;
float invScale; float3 rimColor = float3(1, 0.8, 0.29);
float3 shadowColor = float3(0.84, 0.57, 0);
if ((g_PushConstants.DisplaySize.x * g_PushConstants.InverseDisplaySize.y) >= (4.0 / 3.0)) float cosTheta = dot(normal, normalize(float2(1, 1)));
{ float3 gradient = lerp(color.rgb, cosTheta >= 0.0 ? rimColor : shadowColor, abs(cosTheta));
scale = g_PushConstants.InverseDisplaySize.y * 720.0; color.rgb = lerp(gradient, color.rgb, pow(saturate(sd + 0.8), 32.0));
invScale = g_PushConstants.DisplaySize.y / 720.0;
}
else
{
scale = g_PushConstants.InverseDisplaySize.x * 960.0;
invScale = g_PushConstants.DisplaySize.x / 960.0;
}
float2 lowQualityPosition = (interpolators.Position.xy - 0.5) * scale;
float2 fracPart = frac(lowQualityPosition);
float2 uvStep = fwidth(interpolators.UV) * invScale;
float2 lowQualityUV = interpolators.UV - fracPart * uvStep;
float2 screenTexSize = 1.0 / uvStep;
float4 topLeft = SampleSdfFont(color, texture, lowQualityUV + float2(0, 0), screenTexSize);
float4 topRight = SampleSdfFont(color, texture, lowQualityUV + float2(uvStep.x, 0), screenTexSize);
float4 bottomLeft = SampleSdfFont(color, texture, lowQualityUV + float2(0, uvStep.y), screenTexSize);
float4 bottomRight = SampleSdfFont(color, texture, lowQualityUV + uvStep.xy, screenTexSize);
float4 top = lerp(topLeft, topRight, fracPart.x);
float4 bottom = lerp(bottomLeft, bottomRight, fracPart.x);
color = lerp(top, bottom, fracPart.y);
} }
else else if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_CATEGORY_BEVEL)
{ {
color = SampleSdfFont(color, texture, interpolators.UV, 1.0 / fwidth(interpolators.UV)); float2 normal = normalize(float3(ddx(sd), ddy(sd), 0.25)).xy;
float cosTheta = dot(normal, normalize(float2(1, 1)));
float gradient = 1.0 + cosTheta * 0.5;
color.rgb = saturate(color.rgb * gradient);
} }
else if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_TEXT_SKEW)
{
float2 normal = normalize(float3(ddx(sd), ddy(sd), 0.5)).xy;
float cosTheta = dot(normal, normalize(float2(1, 1)));
float gradient = saturate(1.0 + cosTheta);
color.rgb = lerp(color.rgb * gradient, color.rgb, pow(saturate(sd + 0.77), 32.0));
}
color.a *= saturate(screenPxDistance + 0.5);
color.a *= textureColor.a;
} }
else else
{ {
color *= texture.Sample(g_SamplerDescriptorHeap[0], interpolators.UV); color *= textureColor;
} }
} }
+1 -195
View File
@@ -285,8 +285,6 @@ static bool g_vulkan = false;
static constexpr bool g_vulkan = true; static constexpr bool g_vulkan = true;
#endif #endif
static bool g_mesaTriangleStripWorkaround = false;
static constexpr bool g_hardwareResolve = true; static constexpr bool g_hardwareResolve = true;
static constexpr bool g_hardwareDepthResolve = true; static constexpr bool g_hardwareDepthResolve = true;
@@ -1340,7 +1338,6 @@ struct ImGuiPushConstants
ImU32 gradientBottomLeft{}; ImU32 gradientBottomLeft{};
uint32_t shaderModifier{}; uint32_t shaderModifier{};
uint32_t texture2DDescriptorIndex{}; uint32_t texture2DDescriptorIndex{};
ImVec2 displaySize{};
ImVec2 inverseDisplaySize{}; ImVec2 inverseDisplaySize{};
ImVec2 origin{ 0.0f, 0.0f }; ImVec2 origin{ 0.0f, 0.0f };
ImVec2 scale{ 1.0f, 1.0f }; ImVec2 scale{ 1.0f, 1.0f };
@@ -1702,14 +1699,6 @@ bool Video::CreateHostDevice(const char *sdlVideoDriver)
g_vulkan = (interfaceFunction == CreateVulkanInterfaceWrapper); g_vulkan = (interfaceFunction == CreateVulkanInterfaceWrapper);
#endif #endif
if (interfaceFunction == CreateVulkanInterfaceWrapper)
{
// Enable triangle strip workaround if we are on the Mesa RADV driver, as it currently has a bug where
// restart indices cause triangles to be culled incorrectly. Converting them to degenerate triangles fixes it.
g_mesaTriangleStripWorkaround = deviceDescription.name.find(" (RADV ") != std::string::npos;
}
break; break;
} }
} }
@@ -2329,7 +2318,6 @@ static void DrawProfiler()
ImGui::Text("Present Wait: %s", g_capabilities.presentWait ? "Supported" : "Unsupported"); ImGui::Text("Present Wait: %s", g_capabilities.presentWait ? "Supported" : "Unsupported");
ImGui::Text("Triangle Fan: %s", g_capabilities.triangleFan ? "Supported" : "Unsupported"); ImGui::Text("Triangle Fan: %s", g_capabilities.triangleFan ? "Supported" : "Unsupported");
ImGui::Text("Dynamic Depth Bias: %s", g_capabilities.dynamicDepthBias ? "Supported" : "Unsupported"); ImGui::Text("Dynamic Depth Bias: %s", g_capabilities.dynamicDepthBias ? "Supported" : "Unsupported");
ImGui::Text("Triangle Strip Workaround: %s", g_mesaTriangleStripWorkaround ? "Enabled" : "Disabled");
ImGui::NewLine(); ImGui::NewLine();
ImGui::Text("API: %s", g_vulkan ? "Vulkan" : "D3D12"); ImGui::Text("API: %s", g_vulkan ? "Vulkan" : "D3D12");
@@ -2497,7 +2485,6 @@ static void ProcDrawImGui(const RenderCommand& cmd)
commandList->setViewports(RenderViewport(drawData.DisplayPos.x, drawData.DisplayPos.y, drawData.DisplaySize.x, drawData.DisplaySize.y)); commandList->setViewports(RenderViewport(drawData.DisplayPos.x, drawData.DisplayPos.y, drawData.DisplaySize.x, drawData.DisplaySize.y));
ImGuiPushConstants pushConstants{}; ImGuiPushConstants pushConstants{};
pushConstants.displaySize = drawData.DisplaySize;
pushConstants.inverseDisplaySize = { 1.0f / drawData.DisplaySize.x, 1.0f / drawData.DisplaySize.y }; pushConstants.inverseDisplaySize = { 1.0f / drawData.DisplaySize.x, 1.0f / drawData.DisplaySize.y };
commandList->setGraphicsPushConstants(0, &pushConstants); commandList->setGraphicsPushConstants(0, &pushConstants);
@@ -3609,7 +3596,7 @@ static void SetTexture(GuestDevice* device, uint32_t index, GuestTexture* textur
auto isPlayStation = Config::ControllerIcons == EControllerIcons::PlayStation; auto isPlayStation = Config::ControllerIcons == EControllerIcons::PlayStation;
if (Config::ControllerIcons == EControllerIcons::Auto) if (Config::ControllerIcons == EControllerIcons::Auto)
isPlayStation = hid::g_inputDeviceController == hid::EInputDevice::PlayStation; isPlayStation = hid::g_inputDevicePad == hid::EInputDevice::PlayStation;
if (isPlayStation && texture != nullptr && texture->patchedTexture != nullptr) if (isPlayStation && texture != nullptr && texture->patchedTexture != nullptr)
texture = texture->patchedTexture.get(); texture = texture->patchedTexture.get();
@@ -7415,187 +7402,6 @@ bool FxShadowMapMidAsmHook(PPCRegister& r4, PPCRegister& r5, PPCRegister& r6, PP
} }
} }
// There is a driver bug on Mesa where restart indices cause incorrect culling and prevent some triangles from being rendered.
// Restart indices can be converted to degenerate triangles as a workaround until this issue gets fixed.
static void ConvertToDegenerateTriangles(uint16_t* indices, uint32_t indexCount, uint16_t*& newIndices, uint32_t& newIndexCount)
{
newIndices = reinterpret_cast<uint16_t*>(g_userHeap.Alloc(indexCount * sizeof(uint16_t) * 3));
newIndexCount = 0;
bool stripStart = true;
uint32_t stripSize = 0;
uint16_t lastIndex = 0;
for (uint32_t i = 0; i < indexCount; i++)
{
uint16_t index = indices[i];
if (index == 0xFFFF)
{
if ((stripSize % 2) != 0)
newIndices[newIndexCount++] = lastIndex;
stripStart = true;
stripSize = 0;
}
else
{
if (stripStart && newIndexCount != 0)
{
newIndices[newIndexCount++] = lastIndex;
newIndices[newIndexCount++] = index;
}
newIndices[newIndexCount++] = index;
stripStart = false;
++stripSize;
lastIndex = index;
}
}
}
struct MeshResource
{
SWA_INSERT_PADDING(0x4);
be<uint32_t> indexCount;
be<uint32_t> indices;
};
static std::vector<uint16_t*> g_newIndicesToFree;
// Hedgehog::Mirage::CMeshData::Make
PPC_FUNC_IMPL(__imp__sub_82E44AF8);
PPC_FUNC(sub_82E44AF8)
{
uint16_t* newIndicesToFree = nullptr;
auto databaseData = reinterpret_cast<Hedgehog::Database::CDatabaseData*>(base + ctx.r3.u32);
if (g_mesaTriangleStripWorkaround && !databaseData->IsMadeOne())
{
auto meshResource = reinterpret_cast<MeshResource*>(base + ctx.r4.u32);
if (meshResource->indexCount != 0)
{
uint16_t* newIndices;
uint32_t newIndexCount;
ConvertToDegenerateTriangles(
reinterpret_cast<uint16_t*>(base + meshResource->indices),
meshResource->indexCount,
newIndices,
newIndexCount);
meshResource->indexCount = newIndexCount;
meshResource->indices = static_cast<uint32_t>(reinterpret_cast<uint8_t*>(newIndices) - base);
if (PPC_LOAD_U32(0x83396E98) != NULL)
{
// If index buffers are getting merged, new indices need to survive until the merge happens.
g_newIndicesToFree.push_back(newIndices);
}
else
{
// Otherwise, we can free it immediately.
newIndicesToFree = newIndices;
}
}
}
__imp__sub_82E44AF8(ctx, base);
if (newIndicesToFree != nullptr)
g_userHeap.Free(newIndicesToFree);
}
// Hedgehog::Mirage::CShareVertexBuffer::Reset
PPC_FUNC_IMPL(__imp__sub_82E250D0);
PPC_FUNC(sub_82E250D0)
{
__imp__sub_82E250D0(ctx, base);
for (auto newIndicesToFree : g_newIndicesToFree)
g_userHeap.Free(newIndicesToFree);
g_newIndicesToFree.clear();
}
struct LightAndIndexBufferResourceV1
{
SWA_INSERT_PADDING(0x4);
be<uint32_t> indexCount;
be<uint32_t> indices;
};
// Hedgehog::Mirage::CLightAndIndexBufferData::MakeV1
PPC_FUNC_IMPL(__imp__sub_82E3AFC8);
PPC_FUNC(sub_82E3AFC8)
{
uint16_t* newIndices = nullptr;
auto databaseData = reinterpret_cast<Hedgehog::Database::CDatabaseData*>(base + ctx.r3.u32);
if (g_mesaTriangleStripWorkaround && !databaseData->IsMadeOne())
{
auto lightAndIndexBufferResource = reinterpret_cast<LightAndIndexBufferResourceV1*>(base + ctx.r4.u32);
if (lightAndIndexBufferResource->indexCount != 0)
{
uint32_t newIndexCount;
ConvertToDegenerateTriangles(
reinterpret_cast<uint16_t*>(base + lightAndIndexBufferResource->indices),
lightAndIndexBufferResource->indexCount,
newIndices,
newIndexCount);
lightAndIndexBufferResource->indexCount = newIndexCount;
lightAndIndexBufferResource->indices = static_cast<uint32_t>(reinterpret_cast<uint8_t*>(newIndices) - base);
}
}
__imp__sub_82E3AFC8(ctx, base);
if (newIndices != nullptr)
g_userHeap.Free(newIndices);
}
struct LightAndIndexBufferResourceV5
{
SWA_INSERT_PADDING(0x8);
be<uint32_t> indexCount;
be<uint32_t> indices;
};
// Hedgehog::Mirage::CLightAndIndexBufferData::MakeV5
PPC_FUNC_IMPL(__imp__sub_82E3B1C0);
PPC_FUNC(sub_82E3B1C0)
{
uint16_t* newIndices = nullptr;
auto databaseData = reinterpret_cast<Hedgehog::Database::CDatabaseData*>(base + ctx.r3.u32);
if (g_mesaTriangleStripWorkaround && !databaseData->IsMadeOne())
{
auto lightAndIndexBufferResource = reinterpret_cast<LightAndIndexBufferResourceV5*>(base + ctx.r4.u32);
if (lightAndIndexBufferResource->indexCount != 0)
{
uint32_t newIndexCount;
ConvertToDegenerateTriangles(
reinterpret_cast<uint16_t*>(base + lightAndIndexBufferResource->indices),
lightAndIndexBufferResource->indexCount,
newIndices,
newIndexCount);
lightAndIndexBufferResource->indexCount = newIndexCount;
lightAndIndexBufferResource->indices = static_cast<uint32_t>(reinterpret_cast<uint8_t*>(newIndices) - base);
}
}
__imp__sub_82E3B1C0(ctx, base);
if (newIndices != nullptr)
g_userHeap.Free(newIndices);
}
GUEST_FUNCTION_HOOK(sub_82BD99B0, CreateDevice); GUEST_FUNCTION_HOOK(sub_82BD99B0, CreateDevice);
GUEST_FUNCTION_HOOK(sub_82BE6230, DestructResource); GUEST_FUNCTION_HOOK(sub_82BE6230, DestructResource);
+28 -12
View File
@@ -174,17 +174,16 @@ static void SetControllerInputDevice(Controller* controller)
if (App::s_isLoading) if (App::s_isLoading)
return; return;
// Signal that we've changed input device to block first input.
if (hid::g_inputDevice == hid::EInputDevice::Keyboard)
hid::g_hasChangedInputDevice = true;
hid::g_inputDevice = controller->GetInputDevice(); hid::g_inputDevice = controller->GetInputDevice();
hid::g_inputDeviceController = hid::g_inputDevice; hid::g_inputDevicePad = hid::g_inputDevice;
hid::g_inputDevicePadExplicit = (hid::EInputDeviceExplicit)controller->GetControllerType();
auto controllerType = (hid::EInputDeviceExplicit)controller->GetControllerType(); if (hid::g_hasChangedInputDevice)
LOGFN("Input Device: {}", hid::GetInputDeviceName());
if (hid::g_inputDeviceExplicit != controllerType)
{
hid::g_inputDeviceExplicit = controllerType;
LOGFN("Detected controller: {}", hid::GetInputDeviceName());
}
} }
static void SetControllerTimeOfDayLED(Controller& controller, bool isNight) static void SetControllerTimeOfDayLED(Controller& controller, bool isNight)
@@ -244,14 +243,16 @@ int HID_OnSDLEvent(void*, SDL_Event* event)
SetControllerInputDevice(controller); SetControllerInputDevice(controller);
} }
controller->PollAxis(); if (!hid::g_hasChangedInputDevice)
controller->PollAxis();
} }
else else
{ {
SDL_ShowCursor(SDL_DISABLE); SDL_ShowCursor(SDL_DISABLE);
SetControllerInputDevice(controller); SetControllerInputDevice(controller);
controller->Poll(); if (!hid::g_hasChangedInputDevice)
controller->Poll();
} }
break; break;
@@ -259,8 +260,17 @@ int HID_OnSDLEvent(void*, SDL_Event* event)
case SDL_KEYDOWN: case SDL_KEYDOWN:
case SDL_KEYUP: case SDL_KEYUP:
hid::g_inputDevice = hid::EInputDevice::Keyboard; {
if (hid::g_inputDevice != hid::EInputDevice::Keyboard)
{
hid::g_inputDevice = hid::EInputDevice::Keyboard;
hid::g_hasChangedInputDevice = true;
LOGN("Input Device: Keyboard");
}
break; break;
}
case SDL_MOUSEMOTION: case SDL_MOUSEMOTION:
case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONDOWN:
@@ -331,6 +341,12 @@ uint32_t hid::GetState(uint32_t dwUserIndex, XAMINPUT_STATE* pState)
if (!g_activeController) if (!g_activeController)
return ERROR_DEVICE_NOT_CONNECTED; return ERROR_DEVICE_NOT_CONNECTED;
if (hid::g_hasChangedInputDevice)
{
hid::g_hasChangedInputDevice = false;
return ERROR_SUCCESS;
}
pState->Gamepad = g_activeController->state; pState->Gamepad = g_activeController->state;
return ERROR_SUCCESS; return ERROR_SUCCESS;
+5 -4
View File
@@ -2,9 +2,10 @@
#include <ui/game_window.h> #include <ui/game_window.h>
#include <user/config.h> #include <user/config.h>
hid::EInputDevice hid::g_inputDevice; hid::EInputDevice hid::g_inputDevice = EInputDevice::None;
hid::EInputDevice hid::g_inputDeviceController; hid::EInputDevice hid::g_inputDevicePad = EInputDevice::None;
hid::EInputDeviceExplicit hid::g_inputDeviceExplicit; hid::EInputDeviceExplicit hid::g_inputDevicePadExplicit = EInputDeviceExplicit::Unknown;
bool hid::g_hasChangedInputDevice;
uint16_t hid::g_prohibitedButtons; uint16_t hid::g_prohibitedButtons;
bool hid::g_isLeftStickProhibited; bool hid::g_isLeftStickProhibited;
@@ -39,7 +40,7 @@ std::string hid::GetInputDeviceName()
return "Mouse"; return "Mouse";
} }
switch (g_inputDeviceExplicit) switch (g_inputDevicePadExplicit)
{ {
case EInputDeviceExplicit::Xbox360: case EInputDeviceExplicit::Xbox360:
return "Xbox 360"; return "Xbox 360";
+4 -2
View File
@@ -4,6 +4,7 @@ namespace hid
{ {
enum class EInputDevice enum class EInputDevice
{ {
None,
Keyboard, Keyboard,
Mouse, Mouse,
Xbox, Xbox,
@@ -29,8 +30,9 @@ namespace hid
}; };
extern EInputDevice g_inputDevice; extern EInputDevice g_inputDevice;
extern EInputDevice g_inputDeviceController; extern EInputDevice g_inputDevicePad;
extern EInputDeviceExplicit g_inputDeviceExplicit; extern EInputDeviceExplicit g_inputDevicePadExplicit;
extern bool g_hasChangedInputDevice;
extern uint16_t g_prohibitedButtons; extern uint16_t g_prohibitedButtons;
extern bool g_isLeftStickProhibited; extern bool g_isLeftStickProhibited;
+1 -1
View File
@@ -598,5 +598,5 @@ XexPatcher::Result Installer::checkGameUpdateCompatibility(const std::filesystem
} }
std::vector<uint8_t> patchedBytes; std::vector<uint8_t> patchedBytes;
return XexPatcher::apply(xexBytes.data(), xexBytes.size(), patchBytes.data(), patchBytes.size(), patchedBytes, true); return XexPatcher::apply(xexBytes, patchBytes, patchedBytes, true);
} }
+1 -1
View File
@@ -4,7 +4,7 @@
#include <set> #include <set>
#include "virtual_file_system.h" #include "virtual_file_system.h"
#include <xex_patcher.h> #include "xex_patcher.h"
enum class DLC { enum class DLC {
Unknown, Unknown,
+1 -1
View File
@@ -16,7 +16,7 @@
#include "virtual_file_system.h" #include "virtual_file_system.h"
#include <memory_mapped_file.h> #include "memory_mapped_file.h"
struct ISOFileSystem : VirtualFileSystem struct ISOFileSystem : VirtualFileSystem
{ {
@@ -0,0 +1,169 @@
#include "memory_mapped_file.h"
#if !defined(_WIN32)
# include <cstring>
# include <cstdio>
# include <fcntl.h>
# include <unistd.h>
#endif
MemoryMappedFile::MemoryMappedFile()
{
// Default constructor.
}
MemoryMappedFile::MemoryMappedFile(const std::filesystem::path &path)
{
open(path);
}
MemoryMappedFile::~MemoryMappedFile()
{
close();
}
MemoryMappedFile::MemoryMappedFile(MemoryMappedFile &&other)
{
#if defined(_WIN32)
fileHandle = other.fileHandle;
fileMappingHandle = other.fileMappingHandle;
fileView = other.fileView;
fileSize = other.fileSize;
other.fileHandle = nullptr;
other.fileMappingHandle = nullptr;
other.fileView = nullptr;
other.fileSize.QuadPart = 0;
#else
fileHandle = other.fileHandle;
fileView = other.fileView;
fileSize = other.fileSize;
other.fileHandle = -1;
other.fileView = MAP_FAILED;
other.fileSize = 0;
#endif
}
bool MemoryMappedFile::open(const std::filesystem::path &path)
{
#if defined(_WIN32)
fileHandle = CreateFileW(path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
if (fileHandle == INVALID_HANDLE_VALUE)
{
fprintf(stderr, "CreateFileW failed with error %lu.\n", GetLastError());
fileHandle = nullptr;
return false;
}
if (!GetFileSizeEx(fileHandle, &fileSize))
{
fprintf(stderr, "GetFileSizeEx failed with error %lu.\n", GetLastError());
CloseHandle(fileHandle);
fileHandle = nullptr;
return false;
}
fileMappingHandle = CreateFileMappingW(fileHandle, nullptr, PAGE_READONLY, 0, 0, nullptr);
if (fileMappingHandle == nullptr)
{
fprintf(stderr, "CreateFileMappingW failed with error %lu.\n", GetLastError());
CloseHandle(fileHandle);
fileHandle = nullptr;
return false;
}
fileView = MapViewOfFile(fileMappingHandle, FILE_MAP_READ, 0, 0, 0);
if (fileView == nullptr)
{
fprintf(stderr, "MapViewOfFile failed with error %lu.\n", GetLastError());
CloseHandle(fileMappingHandle);
CloseHandle(fileHandle);
fileMappingHandle = nullptr;
fileHandle = nullptr;
return false;
}
return true;
#else
fileHandle = ::open(path.c_str(), O_RDONLY);
if (fileHandle == -1)
{
fprintf(stderr, "open for %s failed with error %s.\n", path.c_str(), strerror(errno));
return false;
}
fileSize = lseek(fileHandle, 0, SEEK_END);
if (fileSize == (off_t)(-1))
{
fprintf(stderr, "lseek failed with error %s.\n", strerror(errno));
::close(fileHandle);
fileHandle = -1;
return false;
}
fileView = mmap(nullptr, fileSize, PROT_READ, MAP_PRIVATE, fileHandle, 0);
if (fileView == MAP_FAILED)
{
fprintf(stderr, "mmap failed with error %s.\n", strerror(errno));
::close(fileHandle);
fileHandle = -1;
return false;
}
return true;
#endif
}
void MemoryMappedFile::close()
{
#if defined(_WIN32)
if (fileView != nullptr)
{
UnmapViewOfFile(fileView);
}
if (fileMappingHandle != nullptr)
{
CloseHandle(fileMappingHandle);
}
if (fileHandle != nullptr)
{
CloseHandle(fileHandle);
}
#else
if (fileView != MAP_FAILED)
{
munmap(fileView, fileSize);
}
if (fileHandle != -1)
{
::close(fileHandle);
}
#endif
}
bool MemoryMappedFile::isOpen() const
{
#if defined(_WIN32)
return (fileView != nullptr);
#else
return (fileView != MAP_FAILED);
#endif
}
uint8_t *MemoryMappedFile::data() const
{
return reinterpret_cast<uint8_t *>(fileView);
}
size_t MemoryMappedFile::size() const
{
#if defined(_WIN32)
return fileSize.QuadPart;
#else
return static_cast<size_t>(fileSize);
#endif
}
@@ -0,0 +1,32 @@
#pragma once
#include <filesystem>
#if defined(_WIN32)
# include <Windows.h>
#else
# include <sys/mman.h>
#endif
struct MemoryMappedFile {
#if defined(_WIN32)
HANDLE fileHandle = nullptr;
HANDLE fileMappingHandle = nullptr;
LPVOID fileView = nullptr;
LARGE_INTEGER fileSize = {};
#else
int fileHandle = -1;
void *fileView = MAP_FAILED;
off_t fileSize = 0;
#endif
MemoryMappedFile();
MemoryMappedFile(const std::filesystem::path &path);
MemoryMappedFile(MemoryMappedFile &&other);
~MemoryMappedFile();
bool open(const std::filesystem::path &path);
void close();
bool isOpen() const;
uint8_t *data() const;
size_t size() const;
};
@@ -16,7 +16,7 @@
#include "virtual_file_system.h" #include "virtual_file_system.h"
#include <memory_mapped_file.h> #include "memory_mapped_file.h"
enum class XContentVolumeType enum class XContentVolumeType
{ {
+693
View File
@@ -0,0 +1,693 @@
// Referenced from: https://github.com/xenia-canary/xenia-canary/blob/canary_experimental/src/xenia/cpu/xex_module.cc
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2023 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xex_patcher.h"
#include <bit>
#include <cassert>
#include <aes.hpp>
#include <lzx.h>
#include <mspack.h>
#include <TinySHA1.hpp>
#include "memory_mapped_file.h"
enum Xex2ModuleFlags
{
XEX_MODULE_MODULE_PATCH = 0x10,
XEX_MODULE_PATCH_FULL = 0x20,
XEX_MODULE_PATCH_DELTA = 0x40,
};
enum Xex2HeaderKeys
{
XEX_HEADER_FILE_FORMAT_INFO = 0x3FF,
XEX_HEADER_DELTA_PATCH_DESCRIPTOR = 0x5FF,
};
enum Xex2EncryptionType
{
XEX_ENCRYPTION_NONE = 0,
XEX_ENCRYPTION_NORMAL = 1,
};
enum Xex2CompressionType
{
XEX_COMPRESSION_NONE = 0,
XEX_COMPRESSION_BASIC = 1,
XEX_COMPRESSION_NORMAL = 2,
XEX_COMPRESSION_DELTA = 3,
};
enum Xex2SectionType
{
XEX_SECTION_CODE = 1,
XEX_SECTION_DATA = 2,
XEX_SECTION_READONLY_DATA = 3,
};
struct Xex2OptHeader
{
be<uint32_t> key;
union
{
be<uint32_t> value;
be<uint32_t> offset;
};
};
struct Xex2Header
{
be<uint32_t> magic;
be<uint32_t> moduleFlags;
be<uint32_t> headerSize;
be<uint32_t> reserved;
be<uint32_t> securityOffset;
be<uint32_t> headerCount;
Xex2OptHeader headers[1];
};
struct Xex2PageDescriptor
{
union
{
// Must be endian-swapped before reading the bitfield.
uint32_t beValue;
struct
{
uint32_t info : 4;
uint32_t pageCount : 28;
};
};
char dataDigest[0x14];
};
struct Xex2SecurityInfo
{
be<uint32_t> headerSize;
be<uint32_t> imageSize;
char rsaSignature[0x100];
be<uint32_t> unknown;
be<uint32_t> imageFlags;
be<uint32_t> loadAddress;
char sectionDigest[0x14];
be<uint32_t> importTableCount;
char importTableDigest[0x14];
char xgd2MediaId[0x10];
char aesKey[0x10];
be<uint32_t> exportTable;
char headerDigest[0x14];
be<uint32_t> region;
be<uint32_t> allowedMediaTypes;
be<uint32_t> pageDescriptorCount;
Xex2PageDescriptor pageDescriptors[1];
};
struct Xex2DeltaPatch
{
be<uint32_t> oldAddress;
be<uint32_t> newAddress;
be<uint16_t> uncompressedLength;
be<uint16_t> compressedLength;
char patchData[1];
};
struct Xex2OptDeltaPatchDescriptor
{
be<uint32_t> size;
be<uint32_t> targetVersionValue;
be<uint32_t> sourceVersionValue;
uint8_t digestSource[0x14];
uint8_t imageKeySource[0x10];
be<uint32_t> sizeOfTargetHeaders;
be<uint32_t> deltaHeadersSourceOffset;
be<uint32_t> deltaHeadersSourceSize;
be<uint32_t> deltaHeadersTargetOffset;
be<uint32_t> deltaImageSourceOffset;
be<uint32_t> deltaImageSourceSize;
be<uint32_t> deltaImageTargetOffset;
Xex2DeltaPatch info;
};
struct Xex2FileBasicCompressionBlock
{
be<uint32_t> dataSize;
be<uint32_t> zeroSize;
};
struct Xex2FileBasicCompressionInfo
{
Xex2FileBasicCompressionBlock firstBlock;
};
struct Xex2CompressedBlockInfo
{
be<uint32_t> blockSize;
uint8_t blockHash[20];
};
struct Xex2FileNormalCompressionInfo
{
be<uint32_t> windowSize;
Xex2CompressedBlockInfo firstBlock;
};
struct Xex2OptFileFormatInfo
{
be<uint32_t> infoSize;
be<uint16_t> encryptionType;
be<uint16_t> compressionType;
union
{
Xex2FileBasicCompressionInfo basic;
Xex2FileNormalCompressionInfo normal;
} compressionInfo;
};
static const void *getOptHeaderPtr(std::span<const uint8_t> moduleBytes, uint32_t headerKey)
{
if ((headerKey & 0xFF) == 0)
{
assert(false && "Wrong type of method for this key. Expected return value is a number.");
return nullptr;
}
const Xex2Header *xex2Header = (const Xex2Header *)(moduleBytes.data());
for (uint32_t i = 0; i < xex2Header->headerCount; i++)
{
const Xex2OptHeader &optHeader = xex2Header->headers[i];
if (optHeader.key == headerKey)
{
if ((headerKey & 0xFF) == 1)
{
return &optHeader.value;
}
else
{
return &moduleBytes.data()[optHeader.offset];
}
}
}
return nullptr;
}
struct mspack_memory_file
{
mspack_system sys;
void *buffer;
size_t bufferSize;
size_t offset;
};
static mspack_memory_file *mspack_memory_open(mspack_system *sys, void *buffer, size_t bufferSize)
{
assert(bufferSize < INT_MAX);
if (bufferSize >= INT_MAX)
{
return nullptr;
}
mspack_memory_file *memoryFile = (mspack_memory_file *)(std::calloc(1, sizeof(mspack_memory_file)));
if (memoryFile == nullptr)
{
return memoryFile;
}
memoryFile->buffer = buffer;
memoryFile->bufferSize = bufferSize;
memoryFile->offset = 0;
return memoryFile;
}
static void mspack_memory_close(mspack_memory_file *file)
{
std::free(file);
}
static int mspack_memory_read(mspack_file *file, void *buffer, int chars)
{
mspack_memory_file *memoryFile = (mspack_memory_file *)(file);
const size_t remaining = memoryFile->bufferSize - memoryFile->offset;
const size_t total = std::min(size_t(chars), remaining);
std::memcpy(buffer, (uint8_t *)(memoryFile->buffer) + memoryFile->offset, total);
memoryFile->offset += total;
return int(total);
}
static int mspack_memory_write(mspack_file *file, void *buffer, int chars)
{
mspack_memory_file *memoryFile = (mspack_memory_file *)(file);
const size_t remaining = memoryFile->bufferSize - memoryFile->offset;
const size_t total = std::min(size_t(chars), remaining);
std::memcpy((uint8_t *)(memoryFile->buffer) + memoryFile->offset, buffer, total);
memoryFile->offset += total;
return int(total);
}
static void *mspack_memory_alloc(mspack_system *sys, size_t chars)
{
return std::calloc(chars, 1);
}
static void mspack_memory_free(void *ptr)
{
std::free(ptr);
}
static void mspack_memory_copy(void *src, void *dest, size_t chars)
{
std::memcpy(dest, src, chars);
}
static mspack_system *mspack_memory_sys_create()
{
auto sys = (mspack_system *)(std::calloc(1, sizeof(mspack_system)));
if (!sys)
{
return nullptr;
}
sys->read = mspack_memory_read;
sys->write = mspack_memory_write;
sys->alloc = mspack_memory_alloc;
sys->free = mspack_memory_free;
sys->copy = mspack_memory_copy;
return sys;
}
static void mspack_memory_sys_destroy(struct mspack_system *sys)
{
free(sys);
}
#if defined(_WIN32)
inline bool bitScanForward(uint32_t v, uint32_t *outFirstSetIndex)
{
return _BitScanForward((unsigned long *)(outFirstSetIndex), v) != 0;
}
inline bool bitScanForward(uint64_t v, uint32_t *outFirstSetIndex)
{
return _BitScanForward64((unsigned long *)(outFirstSetIndex), v) != 0;
}
#else
inline bool bitScanForward(uint32_t v, uint32_t *outFirstSetIndex)
{
int i = ffs(v);
*outFirstSetIndex = i - 1;
return i != 0;
}
inline bool bitScanForward(uint64_t v, uint32_t *outFirstSetIndex)
{
int i = __builtin_ffsll(v);
*outFirstSetIndex = i - 1;
return i != 0;
}
#endif
static int lzxDecompress(const void *lzxData, size_t lzxLength, void *dst, size_t dstLength, uint32_t windowSize, void *windowData, size_t windowDataLength)
{
int resultCode = 1;
uint32_t windowBits;
if (!bitScanForward(windowSize, &windowBits)) {
return resultCode;
}
mspack_system *sys = mspack_memory_sys_create();
mspack_memory_file *lzxSrc = mspack_memory_open(sys, (void *)(lzxData), lzxLength);
mspack_memory_file *lzxDst = mspack_memory_open(sys, dst, dstLength);
lzxd_stream *lzxd = lzxd_init(sys, (mspack_file *)(lzxSrc), (mspack_file *)(lzxDst), windowBits, 0, 0x8000, dstLength, 0);
if (lzxd != nullptr) {
if (windowData != nullptr) {
size_t paddingLength = windowSize - windowDataLength;
std::memset(&lzxd->window[0], 0, paddingLength);
std::memcpy(&lzxd->window[paddingLength], windowData, windowDataLength);
lzxd->ref_data_size = windowSize;
}
resultCode = lzxd_decompress(lzxd, dstLength);
lzxd_free(lzxd);
}
if (lzxSrc) {
mspack_memory_close(lzxSrc);
}
if (lzxDst) {
mspack_memory_close(lzxDst);
}
if (sys) {
mspack_memory_sys_destroy(sys);
}
return resultCode;
}
static int lzxDeltaApplyPatch(const Xex2DeltaPatch *deltaPatch, uint32_t patchLength, uint32_t windowSize, uint8_t *dstData)
{
const void *patchEnd = (const uint8_t *)(deltaPatch) + patchLength;
const Xex2DeltaPatch *curPatch = deltaPatch;
while (patchEnd > curPatch)
{
int patchSize = -4;
if (curPatch->compressedLength == 0 && curPatch->uncompressedLength == 0 && curPatch->newAddress == 0 && curPatch->oldAddress == 0)
{
// End of patch.
break;
}
switch (curPatch->compressedLength)
{
case 0:
// Set the data to zeroes.
std::memset(&dstData[curPatch->newAddress], 0, curPatch->uncompressedLength);
break;
case 1:
// Move the data.
std::memcpy(&dstData[curPatch->newAddress], &dstData[curPatch->oldAddress], curPatch->uncompressedLength);
break;
default:
// Decompress the data into the destination.
patchSize = curPatch->compressedLength - 4;
int result = lzxDecompress(curPatch->patchData, curPatch->compressedLength, &dstData[curPatch->newAddress], curPatch->uncompressedLength, windowSize, &dstData[curPatch->oldAddress], curPatch->uncompressedLength);
if (result != 0)
{
return result;
}
break;
}
curPatch++;
curPatch = (const Xex2DeltaPatch *)((const uint8_t *)(curPatch) + patchSize);
}
return 0;
}
XexPatcher::Result XexPatcher::apply(std::span<const uint8_t> xexBytes, std::span<const uint8_t> patchBytes, std::vector<uint8_t> &outBytes, bool skipData)
{
// Validate headers.
static const char Xex2Magic[] = "XEX2";
const Xex2Header *xexHeader = (const Xex2Header *)(xexBytes.data());
if (memcmp(xexBytes.data(), Xex2Magic, 4) != 0)
{
return Result::XexFileInvalid;
}
const Xex2Header *patchHeader = (const Xex2Header *)(patchBytes.data());
if (memcmp(patchBytes.data(), Xex2Magic, 4) != 0)
{
return Result::PatchFileInvalid;
}
if ((patchHeader->moduleFlags & (XEX_MODULE_MODULE_PATCH | XEX_MODULE_PATCH_DELTA | XEX_MODULE_PATCH_FULL)) == 0)
{
return Result::PatchFileInvalid;
}
// Validate patch.
const Xex2OptDeltaPatchDescriptor *patchDescriptor = (const Xex2OptDeltaPatchDescriptor *)(getOptHeaderPtr(patchBytes, XEX_HEADER_DELTA_PATCH_DESCRIPTOR));
if (patchDescriptor == nullptr)
{
return Result::PatchFileInvalid;
}
const Xex2OptFileFormatInfo *patchFileFormatInfo = (const Xex2OptFileFormatInfo *)(getOptHeaderPtr(patchBytes, XEX_HEADER_FILE_FORMAT_INFO));
if (patchFileFormatInfo == nullptr)
{
return Result::PatchFileInvalid;
}
if (patchFileFormatInfo->compressionType != XEX_COMPRESSION_DELTA)
{
return Result::PatchFileInvalid;
}
if (patchDescriptor->deltaHeadersSourceOffset > xexHeader->headerSize)
{
return Result::PatchIncompatible;
}
if (patchDescriptor->deltaHeadersSourceSize > (xexHeader->headerSize - patchDescriptor->deltaHeadersSourceOffset))
{
return Result::PatchIncompatible;
}
if (patchDescriptor->deltaHeadersTargetOffset > patchDescriptor->sizeOfTargetHeaders)
{
return Result::PatchIncompatible;
}
uint32_t deltaTargetSize = patchDescriptor->sizeOfTargetHeaders - patchDescriptor->deltaHeadersTargetOffset;
if (patchDescriptor->deltaHeadersSourceSize > deltaTargetSize)
{
return Result::PatchIncompatible;
}
// Apply patch.
uint32_t headerTargetSize = patchDescriptor->sizeOfTargetHeaders;
if (headerTargetSize == 0)
{
headerTargetSize = patchDescriptor->deltaHeadersTargetOffset + patchDescriptor->deltaHeadersSourceSize;
}
// Create the bytes for the new XEX header. Copy over the existing data.
uint32_t newXexHeaderSize = std::max(headerTargetSize, xexHeader->headerSize.get());
outBytes.resize(newXexHeaderSize);
memset(outBytes.data(), 0, newXexHeaderSize);
memcpy(outBytes.data(), xexBytes.data(), headerTargetSize);
Xex2Header *newXexHeader = (Xex2Header *)(outBytes.data());
if (patchDescriptor->deltaHeadersSourceOffset > 0)
{
memcpy(&outBytes[patchDescriptor->deltaHeadersTargetOffset], &outBytes[patchDescriptor->deltaHeadersSourceOffset], patchDescriptor->deltaHeadersSourceSize);
}
int resultCode = lzxDeltaApplyPatch(&patchDescriptor->info, patchDescriptor->size, patchFileFormatInfo->compressionInfo.normal.windowSize, outBytes.data());
if (resultCode != 0)
{
return Result::PatchFailed;
}
// Make the header the specified size by the patch.
outBytes.resize(headerTargetSize);
newXexHeader = (Xex2Header *)(outBytes.data());
// Copy the rest of the data.
const Xex2SecurityInfo *newSecurityInfo = (const Xex2SecurityInfo *)(&outBytes[newXexHeader->securityOffset]);
outBytes.resize(outBytes.size() + newSecurityInfo->imageSize);
memset(&outBytes[headerTargetSize], 0, outBytes.size() - headerTargetSize);
memcpy(&outBytes[headerTargetSize], &xexBytes[xexHeader->headerSize], xexBytes.size() - xexHeader->headerSize);
newXexHeader = (Xex2Header *)(outBytes.data());
newSecurityInfo = (const Xex2SecurityInfo *)(&outBytes[newXexHeader->securityOffset]);
// Decrypt the keys and validate that the patch is compatible with the base file.
static const uint32_t KeySize = 16;
static const uint8_t Xex2RetailKey[16] = { 0x20, 0xB1, 0x85, 0xA5, 0x9D, 0x28, 0xFD, 0xC3, 0x40, 0x58, 0x3F, 0xBB, 0x08, 0x96, 0xBF, 0x91 };
static const uint8_t AESBlankIV[AES_BLOCKLEN] = {};
const Xex2SecurityInfo *originalSecurityInfo = (const Xex2SecurityInfo *)(&xexBytes[xexHeader->securityOffset]);
const Xex2SecurityInfo *patchSecurityInfo = (const Xex2SecurityInfo *)(&patchBytes[patchHeader->securityOffset]);
uint8_t decryptedOriginalKey[KeySize];
uint8_t decryptedNewKey[KeySize];
uint8_t decryptedPatchKey[KeySize];
uint8_t decrpytedImageKeySource[KeySize];
memcpy(decryptedOriginalKey, originalSecurityInfo->aesKey, KeySize);
memcpy(decryptedNewKey, newSecurityInfo->aesKey, KeySize);
memcpy(decryptedPatchKey, patchSecurityInfo->aesKey, KeySize);
memcpy(decrpytedImageKeySource, patchDescriptor->imageKeySource, KeySize);
AES_ctx aesContext;
AES_init_ctx_iv(&aesContext, Xex2RetailKey, AESBlankIV);
AES_CBC_decrypt_buffer(&aesContext, decryptedOriginalKey, KeySize);
AES_ctx_set_iv(&aesContext, AESBlankIV);
AES_CBC_decrypt_buffer(&aesContext, decryptedNewKey, KeySize);
AES_init_ctx_iv(&aesContext, decryptedNewKey, AESBlankIV);
AES_CBC_decrypt_buffer(&aesContext, decryptedPatchKey, KeySize);
AES_ctx_set_iv(&aesContext, AESBlankIV);
AES_CBC_decrypt_buffer(&aesContext, decrpytedImageKeySource, KeySize);
// Validate the patch's key matches the one from the original XEX.
if (memcmp(decrpytedImageKeySource, decryptedOriginalKey, KeySize) != 0)
{
return Result::PatchIncompatible;
}
// Don't process the rest of the patch.
if (skipData)
{
return Result::Success;
}
// Decrypt base XEX if necessary.
const Xex2OptFileFormatInfo *fileFormatInfo = (const Xex2OptFileFormatInfo *)(getOptHeaderPtr(xexBytes, XEX_HEADER_FILE_FORMAT_INFO));
if (fileFormatInfo == nullptr)
{
return Result::XexFileInvalid;
}
if (fileFormatInfo->encryptionType == XEX_ENCRYPTION_NORMAL)
{
AES_init_ctx_iv(&aesContext, decryptedOriginalKey, AESBlankIV);
AES_CBC_decrypt_buffer(&aesContext, &outBytes[headerTargetSize], xexBytes.size() - xexHeader->headerSize);
}
else if (fileFormatInfo->encryptionType != XEX_ENCRYPTION_NONE)
{
return Result::XexFileInvalid;
}
// Decompress base XEX if necessary.
if (fileFormatInfo->compressionType == XEX_COMPRESSION_BASIC)
{
const Xex2FileBasicCompressionBlock *blocks = &fileFormatInfo->compressionInfo.basic.firstBlock;
int32_t numBlocks = (fileFormatInfo->infoSize / sizeof(Xex2FileBasicCompressionBlock)) - 1;
int32_t baseCompressedSize = 0;
int32_t baseImageSize = 0;
for (int32_t i = 0; i < numBlocks; i++) {
baseCompressedSize += blocks[i].dataSize;
baseImageSize += blocks[i].dataSize + blocks[i].zeroSize;
}
if (outBytes.size() < (headerTargetSize + baseImageSize))
{
return Result::XexFileInvalid;
}
// Reverse iteration allows to perform this decompression in place.
uint8_t *srcDataCursor = outBytes.data() + headerTargetSize + baseCompressedSize;
uint8_t *outDataCursor = outBytes.data() + headerTargetSize + baseImageSize;
for (int32_t i = numBlocks - 1; i >= 0; i--)
{
outDataCursor -= blocks[i].zeroSize;
memset(outDataCursor, 0, blocks[i].zeroSize);
outDataCursor -= blocks[i].dataSize;
srcDataCursor -= blocks[i].dataSize;
memmove(outDataCursor, srcDataCursor, blocks[i].dataSize);
}
}
else if (fileFormatInfo->compressionType == XEX_COMPRESSION_NORMAL || fileFormatInfo->compressionType == XEX_COMPRESSION_DELTA)
{
return Result::XexFileUnsupported;
}
else if (fileFormatInfo->compressionType != XEX_COMPRESSION_NONE)
{
return Result::XexFileInvalid;
}
Xex2OptFileFormatInfo *newFileFormatInfo = (Xex2OptFileFormatInfo *)(getOptHeaderPtr(outBytes, XEX_HEADER_FILE_FORMAT_INFO));
if (newFileFormatInfo == nullptr)
{
return Result::PatchFailed;
}
// Update the header to indicate no encryption or compression is used.
newFileFormatInfo->encryptionType = XEX_ENCRYPTION_NONE;
newFileFormatInfo->compressionType = XEX_COMPRESSION_NONE;
// Copy and decrypt patch data if necessary.
std::vector<uint8_t> patchData;
patchData.resize(patchBytes.size() - patchHeader->headerSize);
memcpy(patchData.data(), &patchBytes[patchHeader->headerSize], patchData.size());
if (patchFileFormatInfo->encryptionType == XEX_ENCRYPTION_NORMAL)
{
AES_init_ctx_iv(&aesContext, decryptedPatchKey, AESBlankIV);
AES_CBC_decrypt_buffer(&aesContext, patchData.data(), patchData.size());
}
else if (patchFileFormatInfo->encryptionType != XEX_ENCRYPTION_NONE)
{
return Result::PatchFileInvalid;
}
const Xex2CompressedBlockInfo *currentBlock = &patchFileFormatInfo->compressionInfo.normal.firstBlock;
uint8_t *outExe = &outBytes[newXexHeader->headerSize];
if (patchDescriptor->deltaImageSourceOffset > 0)
{
memcpy(&outExe[patchDescriptor->deltaImageTargetOffset], &outExe[patchDescriptor->deltaImageSourceOffset], patchDescriptor->deltaImageSourceSize);
}
static const uint32_t DigestSize = 20;
uint8_t sha1Digest[DigestSize];
sha1::SHA1 sha1Context;
uint8_t *patchDataCursor = patchData.data();
while (currentBlock->blockSize > 0)
{
const Xex2CompressedBlockInfo *nextBlock = (const Xex2CompressedBlockInfo *)(patchDataCursor);
// Hash and validate the block.
sha1Context.reset();
sha1Context.processBytes(patchDataCursor, currentBlock->blockSize);
sha1Context.finalize(sha1Digest);
if (memcmp(sha1Digest, currentBlock->blockHash, DigestSize) != 0)
{
return Result::PatchFailed;
}
patchDataCursor += 24;
// Apply the block's patch data.
uint32_t blockDataSize = currentBlock->blockSize - 24;
if (lzxDeltaApplyPatch((const Xex2DeltaPatch *)(patchDataCursor), blockDataSize, patchFileFormatInfo->compressionInfo.normal.windowSize, outExe) != 0)
{
return Result::PatchFailed;
}
patchDataCursor += blockDataSize;
currentBlock = nextBlock;
}
return Result::Success;
}
XexPatcher::Result XexPatcher::apply(const std::filesystem::path &baseXexPath, const std::filesystem::path &patchXexPath, const std::filesystem::path &newXexPath)
{
MemoryMappedFile baseXexFile(baseXexPath);
MemoryMappedFile patchFile(patchXexPath);
if (!baseXexFile.isOpen() || !patchFile.isOpen())
{
return Result::FileOpenFailed;
}
std::vector<uint8_t> newXexBytes;
Result result = apply({ baseXexFile.data(), baseXexFile.size() }, { patchFile.data(), patchFile.size() }, newXexBytes, false);
if (result != Result::Success)
{
return result;
}
std::ofstream newXexFile(newXexPath, std::ios::binary);
if (!newXexFile.is_open())
{
return Result::FileOpenFailed;
}
newXexFile.write((const char *)(newXexBytes.data()), newXexBytes.size());
newXexFile.close();
if (newXexFile.bad())
{
std::filesystem::remove(newXexPath);
return Result::FileWriteFailed;
}
return Result::Success;
}
+35
View File
@@ -0,0 +1,35 @@
// Referenced from: https://github.com/xenia-canary/xenia-canary/blob/canary_experimental/src/xenia/cpu/xex_module.cc
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2023 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#pragma once
#include <cstdint>
#include <filesystem>
#include <span>
#include <vector>
struct XexPatcher
{
enum class Result {
Success,
FileOpenFailed,
FileWriteFailed,
XexFileUnsupported,
XexFileInvalid,
PatchFileInvalid,
PatchIncompatible,
PatchFailed,
PatchUnsupported
};
static Result apply(std::span<const uint8_t> xexBytes, std::span<const uint8_t> patchBytes, std::vector<uint8_t> &outBytes, bool skipData);
static Result apply(const std::filesystem::path &baseXexPath, const std::filesystem::path &patchXexPath, const std::filesystem::path &newXexPath);
};
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -12,6 +12,6 @@ enum class ELanguage : uint32_t
inline std::string g_localeMissing = "<missing string>"; inline std::string g_localeMissing = "<missing string>";
extern std::unordered_map<std::string_view, std::unordered_map<ELanguage, std::string>> g_locale; extern std::unordered_map<std::string, std::unordered_map<ELanguage, std::string>> g_locale;
std::string& Localise(const std::string_view& key); std::string& Localise(const char* key);
+19 -19
View File
@@ -106,33 +106,33 @@ uint32_t LdrLoadModule(const std::filesystem::path &path)
return 0; return 0;
} }
auto* header = reinterpret_cast<const Xex2Header*>(loadResult.data()); auto* xex = reinterpret_cast<XEX_HEADER*>(loadResult.data());
auto* security = reinterpret_cast<const Xex2SecurityInfo*>(loadResult.data() + header->securityOffset); auto security = reinterpret_cast<XEX2_SECURITY_INFO*>((char*)xex + xex->AddressOfSecurityInfo);
const auto* fileFormatInfo = reinterpret_cast<const Xex2OptFileFormatInfo*>(getOptHeaderPtr(loadResult.data(), XEX_HEADER_FILE_FORMAT_INFO));
auto entry = *reinterpret_cast<const uint32_t*>(getOptHeaderPtr(loadResult.data(), XEX_HEADER_ENTRY_POINT)); auto format = Xex2FindOptionalHeader<XEX_FILE_FORMAT_INFO>(xex, XEX_HEADER_FILE_FORMAT_INFO);
auto entry = *Xex2FindOptionalHeader<uint32_t>(xex, XEX_HEADER_ENTRY_POINT);
ByteSwapInplace(entry); ByteSwapInplace(entry);
auto srcData = loadResult.data() + header->headerSize; auto srcData = (char *)xex + xex->SizeOfHeader;
auto destData = reinterpret_cast<uint8_t*>(g_memory.Translate(security->loadAddress)); auto destData = (char *)g_memory.Translate(security->ImageBase);
if (format->CompressionType == 0)
if (fileFormatInfo->compressionType == XEX_COMPRESSION_NONE)
{ {
memcpy(destData, srcData, security->imageSize); memcpy(destData, srcData, security->SizeOfImage);
} }
else if (fileFormatInfo->compressionType == XEX_COMPRESSION_BASIC) else if (format->CompressionType == 1)
{ {
auto* blocks = reinterpret_cast<const Xex2FileBasicCompressionBlock*>(fileFormatInfo + 1); auto numBlocks = (format->SizeOfHeader / sizeof(XEX_BASIC_FILE_COMPRESSION_INFO)) - 1;
const size_t numBlocks = (fileFormatInfo->infoSize / sizeof(Xex2FileBasicCompressionInfo)) - 1; auto blocks = reinterpret_cast<const XEX_BASIC_FILE_COMPRESSION_INFO*>(format + 1);
for (size_t i = 0; i < numBlocks; i++) for (size_t i = 0; i < numBlocks; i++)
{ {
memcpy(destData, srcData, blocks[i].dataSize); memcpy(destData, srcData, blocks[i].SizeOfData);
srcData += blocks[i].dataSize; srcData += blocks[i].SizeOfData;
destData += blocks[i].dataSize; destData += blocks[i].SizeOfData;
memset(destData, 0, blocks[i].SizeOfPadding);
memset(destData, 0, blocks[i].zeroSize); destData += blocks[i].SizeOfPadding;
destData += blocks[i].zeroSize;
} }
} }
else else
@@ -140,9 +140,9 @@ uint32_t LdrLoadModule(const std::filesystem::path &path)
assert(false && "Unknown compression type."); assert(false && "Unknown compression type.");
} }
auto res = reinterpret_cast<const Xex2ResourceInfo*>(getOptHeaderPtr(loadResult.data(), XEX_HEADER_RESOURCE_INFO)); auto res = Xex2FindOptionalHeader<XEX_RESOURCE_INFO>(xex, XEX_HEADER_RESOURCE_INFO);
g_xdbfWrapper = XDBFWrapper((uint8_t*)g_memory.Translate(res->offset.get()), res->sizeOfData); g_xdbfWrapper = XDBFWrapper((uint8_t*)g_memory.Translate(res->Offset.get()), res->SizeOfData);
return entry; return entry;
} }
+20 -66
View File
@@ -562,68 +562,14 @@ PPC_FUNC(sub_82E0D3E8)
g_userHeap.Free(newArlFileData); g_userHeap.Free(newArlFileData);
} }
// Load elements have an unused "pretty name" field. We will use this field to store the archive file path,
// prefixed with a magic string. When the first load detects this string, it will load append archives
// and then clear the field to prevent remaining splits from loading the append archives again.
// We cannot rely on .ar.00 being the first split to be loaded, so this approach is necessary.
static thread_local uint32_t g_prefixedArFilePath = NULL;
// Hedgehog::Database::CDatabaseLoader::LoadArchives
PPC_FUNC_IMPL(__imp__sub_82E0CC38);
PPC_FUNC(sub_82E0CC38)
{
if (g_mods.empty())
{
__imp__sub_82E0CC38(ctx, base);
return;
}
auto r3 = ctx.r3;
auto r4 = ctx.r4;
auto r5 = ctx.r5;
auto r6 = ctx.r6;
auto r7 = ctx.r7;
auto r8 = ctx.r8;
const char* arFilePath = reinterpret_cast<const char*>(base + PPC_LOAD_U32(r5.u32));
// __HH_ALLOC
ctx.r3.u32 = 22 + strlen(arFilePath);
sub_822C0988(ctx, base);
char* prefixedArFilePath = reinterpret_cast<char*>(base + ctx.r3.u32);
*reinterpret_cast<be<uint32_t>*>(prefixedArFilePath) = 1;
strcpy(prefixedArFilePath + 0x4, "/UnleashedRecomp/");
strcpy(prefixedArFilePath + 0x15, arFilePath);
ctx.r1.u32 -= 0x10;
uint32_t stackSpace = ctx.r1.u32;
PPC_STORE_U32(stackSpace, static_cast<uint32_t>(reinterpret_cast<uint8_t*>(prefixedArFilePath) - base) + 0x4);
g_prefixedArFilePath = stackSpace;
ctx.r3 = r3;
ctx.r4 = r4;
ctx.r5 = r5;
ctx.r6 = r6;
ctx.r7 = r7;
ctx.r8 = r8;
__imp__sub_82E0CC38(ctx, base);
// Hedgehog::Base::CSharedString::~CSharedString
ctx.r3.u32 = stackSpace;
sub_82DFB148(ctx, base);
g_prefixedArFilePath = NULL;
ctx.r1.u32 += 0x10;
}
// Hedgehog::Database::SLoadElement::SLoadElement // Hedgehog::Database::SLoadElement::SLoadElement
PPC_FUNC_IMPL(__imp__sub_82E140D8); PPC_FUNC_IMPL(__imp__sub_82E140D8);
PPC_FUNC(sub_82E140D8) PPC_FUNC(sub_82E140D8)
{ {
// Store the prefixed archive file path as the pretty name. It's unused for archives we want to append to. // Store archive name as the pretty name to use it later for append archive loading.
if (!g_mods.empty() && PPC_LOAD_U32(ctx.r5.u32) == 0x8200A621 && g_prefixedArFilePath != NULL) // This is always set to an empty string for archives, so it should be safe to replace.
ctx.r5.u32 = g_prefixedArFilePath; if (!g_mods.empty() && PPC_LOAD_U32(ctx.r5.u32) == 0x8200A621)
ctx.r5.u32 = ctx.r6.u32;
__imp__sub_82E140D8(ctx, base); __imp__sub_82E140D8(ctx, base);
} }
@@ -638,17 +584,25 @@ PPC_FUNC(sub_82E0B500)
return; return;
} }
uint32_t prefixedArFilePath = PPC_LOAD_U32(ctx.r5.u32); std::u8string_view arFilePathU8(reinterpret_cast<const char8_t*>(base + PPC_LOAD_U32(ctx.r5.u32)));
std::u8string_view arFilePathU8(reinterpret_cast<const char8_t*>(base + prefixedArFilePath)); size_t index = arFilePathU8.find(u8".ar.00");
if (!arFilePathU8.starts_with(u8"/UnleashedRecomp/")) if (index == (arFilePathU8.size() - 6))
{ {
__imp__sub_82E0B500(ctx, base); arFilePathU8.remove_suffix(3);
return;
} }
else
{
index = arFilePathU8.find(u8".ar");
// Immediately clear the string, so the remaining splits don't load append archives again. if (index != (arFilePathU8.size() - 3) ||
PPC_STORE_U8(prefixedArFilePath, 0x00); arFilePathU8.starts_with(u8"tg-") ||
arFilePathU8.remove_prefix(0x11); arFilePathU8.starts_with(u8"gia-") ||
arFilePathU8.starts_with(u8"gi-texture-"))
{
__imp__sub_82E0B500(ctx, base);
return;
}
}
auto r3 = ctx.r3; // Callback auto r3 = ctx.r3; // Callback
auto r4 = ctx.r4; // Database auto r4 = ctx.r4; // Database
@@ -1,4 +1,5 @@
#include <os/registry.h> #include <os/registry.h>
#include <unordered_map>
inline const wchar_t* g_registryRoot = L"Software\\UnleashedRecomp"; inline const wchar_t* g_registryRoot = L"Software\\UnleashedRecomp";
@@ -172,7 +172,7 @@ PPC_FUNC(sub_824B0930)
if (*SWA::SGlobals::ms_IsRenderHud && pHudPause->m_IsShown && !pHudPause->m_Submenu && pHudPause->m_Transition == SWA::eTransitionType_Undefined) if (*SWA::SGlobals::ms_IsRenderHud && pHudPause->m_IsShown && !pHudPause->m_Submenu && pHudPause->m_Transition == SWA::eTransitionType_Undefined)
{ {
ButtonGuide::Open(Button("Achievements_Name", FLT_MAX, EButtonIcon::Back, EButtonAlignment::Left, EFontQuality::Low)); ButtonGuide::Open(Button("Achievements_Name", EButtonIcon::Back, EButtonAlignment::Left, EFontQuality::Low));
g_isClosed = false; g_isClosed = false;
} }
else if (!g_isClosed) else if (!g_isClosed)
+1 -1
View File
@@ -186,7 +186,7 @@ public:
case SDL_CONTROLLERTOUCHPADDOWN: case SDL_CONTROLLERTOUCHPADDOWN:
{ {
g_worldMapCursorParams = hid::g_inputDeviceExplicit == hid::EInputDeviceExplicit::DualSense g_worldMapCursorParams = hid::g_inputDevicePadExplicit == hid::EInputDeviceExplicit::DualSense
? (WorldMapCursorParams)g_worldMapCursorParamsProspero ? (WorldMapCursorParams)g_worldMapCursorParamsProspero
: (WorldMapCursorParams)g_worldMapCursorParamsOrbis; : (WorldMapCursorParams)g_worldMapCursorParamsOrbis;
+3 -37
View File
@@ -70,16 +70,17 @@ void ObjBigBarrelSetPositionMidAsmHook(PPCRegister& r3, PPCRegister& r4)
} }
} }
// SWA::CExBullet::AddCallback
// Tornado Defense bullet particles are colored by the button prompt, which differs on PlayStation 3. // Tornado Defense bullet particles are colored by the button prompt, which differs on PlayStation 3.
// Luckily, the PS3 particles are left in the files, and they get spawned by name when a bullet gets created. // Luckily, the PS3 particles are left in the files, and they get spawned by name when a bullet gets created.
// SWA::CExBullet::AddCallback
PPC_FUNC_IMPL(__imp__sub_82B14CC0); PPC_FUNC_IMPL(__imp__sub_82B14CC0);
PPC_FUNC(sub_82B14CC0) PPC_FUNC(sub_82B14CC0)
{ {
auto isPlayStation = Config::ControllerIcons == EControllerIcons::PlayStation; auto isPlayStation = Config::ControllerIcons == EControllerIcons::PlayStation;
if (Config::ControllerIcons == EControllerIcons::Auto) if (Config::ControllerIcons == EControllerIcons::Auto)
isPlayStation = hid::g_inputDeviceController == hid::EInputDevice::PlayStation; isPlayStation = hid::g_inputDevicePad == hid::EInputDevice::PlayStation;
if (isPlayStation) if (isPlayStation)
{ {
@@ -98,38 +99,3 @@ PPC_FUNC(sub_82B14CC0)
__imp__sub_82B14CC0(ctx, base); __imp__sub_82B14CC0(ctx, base);
} }
// CObjGrindDashPanel is particularly egregious when it comes to overlapping sounds at HFR
// due to the character proxy sending the hit message multiple times in a frame. This is a
// quick workaround to limit the message process function to occur at a 30 FPS time step.
static constexpr size_t OBJ_GRIND_DASH_PANEL_SIZE = 0x160;
void ObjGrindDashPanelAllocMidAsmHook(PPCRegister& r3)
{
r3.u32 += sizeof(double);
}
// SWA::CObjGrindDashPanel::CObjGrindDashPanel
PPC_FUNC_IMPL(__imp__sub_82614228);
PPC_FUNC(sub_82614228)
{
*reinterpret_cast<double*>(base + ctx.r3.u32 + OBJ_GRIND_DASH_PANEL_SIZE) = 0.0;
__imp__sub_82614228(ctx, base);
}
// SWA::CObjGrindDashPanel::MsgHitEventCollision::Impl
PPC_FUNC_IMPL(__imp__sub_826145D8);
PPC_FUNC(sub_826145D8)
{
constexpr double REFERENCE_DELTA_TIME = 1.0 / 30.0;
constexpr double DELTA_TIME_TOLERANCE = 0.0001;
auto lastHitTime = reinterpret_cast<double*>(base + ctx.r3.u32 + OBJ_GRIND_DASH_PANEL_SIZE);
auto deltaTime = App::s_time - *lastHitTime;
if ((deltaTime + DELTA_TIME_TOLERANCE) > REFERENCE_DELTA_TIME)
{
__imp__sub_826145D8(ctx, base);
*lastHitTime = App::s_time;
}
}
+1 -1
View File
@@ -161,7 +161,7 @@ void LoadingScreenControllerMidAsmHook()
auto isPlayStation = Config::ControllerIcons == EControllerIcons::PlayStation; auto isPlayStation = Config::ControllerIcons == EControllerIcons::PlayStation;
if (Config::ControllerIcons == EControllerIcons::Auto) if (Config::ControllerIcons == EControllerIcons::Auto)
isPlayStation = hid::g_inputDeviceController == hid::EInputDevice::PlayStation; isPlayStation = hid::g_inputDevicePad == hid::EInputDevice::PlayStation;
const char* prefix = isPlayStation ? "ps3" : "360"; const char* prefix = isPlayStation ? "ps3" : "360";
+2 -5
View File
@@ -1,6 +1,6 @@
#pragma once #pragma once
inline std::array<const char*, 17> g_credits = inline std::array<const char*, 14> g_credits =
{ {
"Skyth", "Skyth",
"Hyper", "Hyper",
@@ -15,8 +15,5 @@ inline std::array<const char*, 17> g_credits =
"LadyLunanova", "LadyLunanova",
"LJSTAR", "LJSTAR",
"Goalringmod27", "Goalringmod27",
"M&M", "M&M"
"DaGuAr",
"brianuuuSonic",
"Kitzuku"
}; };
+1 -1
View File
@@ -1,4 +1,4 @@
VERSION_MILESTONE="Release Candidate 1" VERSION_MILESTONE="Beta 3"
VERSION_MAJOR=1 VERSION_MAJOR=1
VERSION_MINOR=0 VERSION_MINOR=0
VERSION_REVISION=0 VERSION_REVISION=0
+1 -1
View File
@@ -763,7 +763,7 @@ void AchievementMenu::Open()
return std::get<1>(a) > std::get<1>(b); return std::get<1>(a) > std::get<1>(b);
}); });
ButtonGuide::Open(Button("Common_Back", FLT_MAX, EButtonIcon::B, EFontQuality::Low)); ButtonGuide::Open(Button("Common_Back", EButtonIcon::B));
ResetSelection(); ResetSelection();
Game_PlaySound("sys_actstg_pausewinopen"); Game_PlaySound("sys_actstg_pausewinopen");
@@ -183,9 +183,6 @@ void AchievementOverlay::Draw()
IM_COL32(255, 255, 255, 255) // col IM_COL32(255, 255, 255, 255) // col
); );
// Use low quality text.
SetShaderModifier(IMGUI_SHADER_MODIFIER_LOW_QUALITY_TEXT);
// Draw header text. // Draw header text.
DrawTextWithShadow DrawTextWithShadow
( (
@@ -211,9 +208,6 @@ void AchievementOverlay::Draw()
1.0f, // radius 1.0f, // radius
IM_COL32(0, 0, 0, 255) // shadowColour IM_COL32(0, 0, 0, 255) // shadowColour
); );
// Reset low quality text shader modifier.
SetShaderModifier(IMGUI_SHADER_MODIFIER_NONE);
} }
else else
{ {
+73 -68
View File
@@ -14,6 +14,7 @@
constexpr float DEFAULT_SIDE_MARGINS = 379; constexpr float DEFAULT_SIDE_MARGINS = 379;
ImFont* g_fntNewRodin; ImFont* g_fntNewRodin;
ImFont* g_fntNewRodinLQ;
std::unique_ptr<GuestTexture> g_upControllerIcons; std::unique_ptr<GuestTexture> g_upControllerIcons;
std::unique_ptr<GuestTexture> g_upKBMIcons; std::unique_ptr<GuestTexture> g_upKBMIcons;
@@ -66,7 +67,7 @@ std::tuple<std::tuple<ImVec2, ImVec2>, GuestTexture*> GetButtonIcon(EButtonIcon
GuestTexture* texture; GuestTexture* texture;
auto isPlayStation = Config::ControllerIcons == EControllerIcons::Auto auto isPlayStation = Config::ControllerIcons == EControllerIcons::Auto
? hid::g_inputDeviceController == hid::EInputDevice::PlayStation ? hid::g_inputDevicePad == hid::EInputDevice::PlayStation
: Config::ControllerIcons == EControllerIcons::PlayStation; : Config::ControllerIcons == EControllerIcons::PlayStation;
auto yOffsetCmn = isPlayStation ? 42 : 0; auto yOffsetCmn = isPlayStation ? 42 : 0;
@@ -143,74 +144,79 @@ std::tuple<std::tuple<ImVec2, ImVec2>, GuestTexture*> GetButtonIcon(EButtonIcon
return std::make_tuple(btn, texture); return std::make_tuple(btn, texture);
} }
ImFont* GetFont(EFontQuality fontQuality)
{
return fontQuality == EFontQuality::Low
? g_fntNewRodinLQ
: g_fntNewRodin;
}
static void DrawGuide(float* offset, ImVec2 regionMin, ImVec2 regionMax, EButtonIcon icon, static void DrawGuide(float* offset, ImVec2 regionMin, ImVec2 regionMax, EButtonIcon icon,
EButtonAlignment alignment, ImVec2 iconMin, ImVec2 iconMax, EFontQuality fontQuality, EButtonAlignment alignment, ImVec2 iconMin, ImVec2 iconMax, EFontQuality fontQuality,
float textWidth, float maxTextWidth, float textScale, float fontSize, const char* text) ImVec2 textSize, float fontSize, const char* text)
{ {
auto drawList = ImGui::GetBackgroundDrawList(); auto drawList = ImGui::GetBackgroundDrawList();
auto _icon = icon;
auto iconWidth = Scale(g_iconWidths[icon]); auto iconWidth = Scale(g_iconWidths[icon]);
auto textMarginY = regionMin.y + Scale(9.0f); auto dualIconMarginX = Scale(25);
ImVec2 textPos; if (icon == EButtonIcon::LBRB)
if (icon == EButtonIcon::LBRB || icon == EButtonIcon::LTRT)
{ {
auto iconMarginX = Scale(16); _icon = EButtonIcon::LB;
}
iconMin.x = alignment == EButtonAlignment::Left else if (icon == EButtonIcon::LTRT)
? regionMin.x + *offset - maxTextWidth + (iconWidth / 2) {
: regionMax.x - *offset - maxTextWidth - (iconWidth / 2) - iconMarginX; _icon = EButtonIcon::LT;
iconMax.x = iconMin.x + iconWidth;
// Left button.
auto btnIcon = GetButtonIcon(icon == EButtonIcon::LBRB ? EButtonIcon::LB : EButtonIcon::LT);
drawList->AddImage(std::get<1>(btnIcon), iconMin, iconMax, GET_UV_COORDS(std::get<0>(btnIcon)));
auto dualIconMin = alignment == EButtonAlignment::Left
? ImVec2(iconMax.x + maxTextWidth + iconMarginX, iconMin.y)
: ImVec2(regionMax.x - *offset + maxTextWidth - iconWidth, iconMin.y);
auto dualIconMax = ImVec2(dualIconMin.x + iconWidth, iconMax.y);
// Right button.
btnIcon = GetButtonIcon(icon == EButtonIcon::LBRB ? EButtonIcon::RB : EButtonIcon::RT);
drawList->AddImage(std::get<1>(btnIcon), dualIconMin, dualIconMax, GET_UV_COORDS(std::get<0>(btnIcon)));
textPos = { (iconMax.x + ((dualIconMin.x - iconMax.x) - maxTextWidth + std::max(0.0f, maxTextWidth - textWidth)) / 2) + Scale(2), textMarginY};
*offset += iconWidth;
} }
else else
{ {
auto btnIcon = GetButtonIcon(icon); dualIconMarginX = 0;
drawList->AddImage(std::get<1>(btnIcon), iconMin, iconMax, GET_UV_COORDS(std::get<0>(btnIcon)));
auto textMarginX = alignment == EButtonAlignment::Left
? regionMin.x + *offset
: regionMax.x - *offset;
textPos = { textMarginX, textMarginY };
} }
SetScale({ textScale, 1.0f }); if (icon == EButtonIcon::LBRB || icon == EButtonIcon::LTRT)
SetOrigin(textPos);
if (fontQuality == EFontQuality::Low)
SetShaderModifier(IMGUI_SHADER_MODIFIER_LOW_QUALITY_TEXT);
DrawTextWithOutline(g_fntNewRodin, fontSize, textPos, IM_COL32_WHITE, text, 4, IM_COL32_BLACK);
if (fontQuality == EFontQuality::Low)
{ {
// Add extra luminance to low quality text. iconMin = alignment == EButtonAlignment::Left
drawList->AddText(g_fntNewRodin, fontSize, textPos, IM_COL32(255, 255, 255, 127), text); ? ImVec2(/* X */ regionMin.x + *offset - dualIconMarginX * 3, /* Y */ iconMin.y)
SetShaderModifier(IMGUI_SHADER_MODIFIER_NONE); : ImVec2(/* X */ regionMax.x - *offset - textSize.x - iconWidth, /* Y */ iconMin.y);
iconMax = alignment == EButtonAlignment::Left
? ImVec2(iconMin.x + iconWidth, iconMax.y)
: ImVec2(iconMin.x, iconMax.y);
} }
SetScale({ 1.0f, 1.0f }); auto btnIcon = GetButtonIcon(_icon);
SetOrigin({ 0.0f, 0.0f }); drawList->AddImage(std::get<1>(btnIcon), iconMin, iconMax, GET_UV_COORDS(std::get<0>(btnIcon)));
auto font = GetFont(fontQuality);
auto textMarginX = alignment == EButtonAlignment::Left
? regionMin.x + *offset + dualIconMarginX
: regionMax.x - *offset - dualIconMarginX * 2;
auto textMarginY = regionMin.y + Scale(8);
ImVec2 textPosition = { textMarginX, textMarginY };
DrawTextWithOutline(font, fontSize, textPosition, IM_COL32_WHITE, text, 4, IM_COL32_BLACK);
// Add extra luminance to low quality text.
if (fontQuality == EFontQuality::Low)
drawList->AddText(font, fontSize, textPosition, IM_COL32(255, 255, 255, 127), text);
if (icon == EButtonIcon::LBRB || icon == EButtonIcon::LTRT)
{
auto btnIcon = GetButtonIcon(icon == EButtonIcon::LBRB ? EButtonIcon::RB : EButtonIcon::RT);
auto dualIconMin = alignment == EButtonAlignment::Left
? ImVec2(/* X */ regionMin.x + *offset + textSize.x + dualIconMarginX * 2, /* Y */ iconMin.y)
: ImVec2(/* X */ regionMax.x - *offset + textSize.x - dualIconMarginX, /* Y */ iconMin.y);
auto dualIconMax = ImVec2(dualIconMin.x + iconWidth, iconMax.y);
drawList->AddImage(std::get<1>(btnIcon), dualIconMin, dualIconMax, GET_UV_COORDS(std::get<0>(btnIcon)));
*offset += dualIconMarginX + iconWidth;
}
} }
void ButtonGuide::Init() void ButtonGuide::Init()
@@ -218,6 +224,8 @@ void ButtonGuide::Init()
auto& io = ImGui::GetIO(); auto& io = ImGui::GetIO();
g_fntNewRodin = ImFontAtlasSnapshot::GetFont("FOT-NewRodinPro-M.otf"); g_fntNewRodin = ImFontAtlasSnapshot::GetFont("FOT-NewRodinPro-M.otf");
g_fntNewRodinLQ = ImFontAtlasSnapshot::GetFont("FOT-NewRodinPro-M.otf");
g_upControllerIcons = LOAD_ZSTD_TEXTURE(g_controller); g_upControllerIcons = LOAD_ZSTD_TEXTURE(g_controller);
g_upKBMIcons = LOAD_ZSTD_TEXTURE(g_kbm); g_upKBMIcons = LOAD_ZSTD_TEXTURE(g_kbm);
} }
@@ -233,9 +241,10 @@ void ButtonGuide::Draw()
ImVec2 regionMin = { g_aspectRatioOffsetX + Scale(g_sideMargins), g_aspectRatioOffsetY * 2.0f + Scale(720.0f - 102.0f) }; ImVec2 regionMin = { g_aspectRatioOffsetX + Scale(g_sideMargins), g_aspectRatioOffsetY * 2.0f + Scale(720.0f - 102.0f) };
ImVec2 regionMax = { g_aspectRatioOffsetX + Scale(1280.0f - g_sideMargins), g_aspectRatioOffsetY * 2.0f + Scale(720.0f) }; ImVec2 regionMax = { g_aspectRatioOffsetX + Scale(1280.0f - g_sideMargins), g_aspectRatioOffsetY * 2.0f + Scale(720.0f) };
auto textMarginX = Scale(21.25f); auto textMarginX = Scale(57);
auto textMarginY = Scale(8);
auto iconMarginX = Scale(4); auto iconMarginX = Scale(4);
auto fontSize = Scale(21.8f); auto fontSize = Scale(22.5f);
auto offsetLeft = 0.0f; auto offsetLeft = 0.0f;
auto offsetRight = 0.0f; auto offsetRight = 0.0f;
@@ -251,20 +260,18 @@ void ButtonGuide::Draw()
if (btn.Visibility && !*btn.Visibility) if (btn.Visibility && !*btn.Visibility)
continue; continue;
auto str = Localise(btn.Name).c_str(); auto str = Localise(btn.Name.c_str()).c_str();
auto iconWidth = Scale(g_iconWidths[btn.Icon]); auto iconWidth = Scale(g_iconWidths[btn.Icon]);
auto iconHeight = Scale(g_iconHeights[btn.Icon]); auto iconHeight = Scale(g_iconHeights[btn.Icon]);
auto textWidth = g_fntNewRodin->CalcTextSizeA(fontSize, FLT_MAX, 0, str).x; auto textSize = g_fntNewRodin->CalcTextSizeA(fontSize, FLT_MAX, 0, str);
auto maxWidth = btn.MaxWidth == FLT_MAX ? textWidth : Scale(btn.MaxWidth);
auto textScale = std::min(1.0f, maxWidth / textWidth);
if (i > 0) if (i > 0)
offsetLeft += maxWidth + iconWidth + textMarginX; offsetLeft += textSize.x + iconWidth + textMarginX;
ImVec2 iconMin = { regionMin.x + offsetLeft - iconWidth - iconMarginX, regionMin.y }; ImVec2 iconMin = { regionMin.x + offsetLeft - iconWidth - iconMarginX, regionMin.y };
ImVec2 iconMax = { regionMin.x + offsetLeft - iconMarginX, regionMin.y + iconHeight }; ImVec2 iconMax = { regionMin.x + offsetLeft - iconMarginX, regionMin.y + iconHeight };
DrawGuide(&offsetLeft, regionMin, regionMax, btn.Icon, btn.Alignment, iconMin, iconMax, btn.FontQuality, textWidth, maxWidth, textScale, fontSize, str); DrawGuide(&offsetLeft, regionMin, regionMax, btn.Icon, btn.Alignment, iconMin, iconMax, btn.FontQuality, textSize, fontSize, str);
} }
// Draw right aligned icons. // Draw right aligned icons.
@@ -278,20 +285,18 @@ void ButtonGuide::Draw()
if (btn.Visibility && !*btn.Visibility) if (btn.Visibility && !*btn.Visibility)
continue; continue;
auto str = Localise(btn.Name).c_str(); auto str = Localise(btn.Name.c_str()).c_str();
auto iconWidth = Scale(g_iconWidths[btn.Icon]); auto iconWidth = Scale(g_iconWidths[btn.Icon]);
auto iconHeight = Scale(g_iconHeights[btn.Icon]); auto iconHeight = Scale(g_iconHeights[btn.Icon]);
auto textWidth = g_fntNewRodin->CalcTextSizeA(fontSize, FLT_MAX, 0, str).x; auto textSize = g_fntNewRodin->CalcTextSizeA(fontSize, FLT_MAX, 0, str);
auto maxWidth = btn.MaxWidth == FLT_MAX ? textWidth : Scale(btn.MaxWidth);
auto textScale = std::min(1.0f, maxWidth / textWidth);
if (i < g_buttons.size() - 1) if (i < g_buttons.size() - 1)
offsetRight += maxWidth + iconWidth + textMarginX; offsetRight += textSize.x + iconWidth + textMarginX;
ImVec2 iconMin = { regionMax.x - offsetRight - iconWidth - iconMarginX, regionMin.y }; ImVec2 iconMin = { regionMax.x - offsetRight - iconWidth - iconMarginX, regionMin.y };
ImVec2 iconMax = { regionMax.x - offsetRight - iconMarginX, regionMin.y + iconHeight }; ImVec2 iconMax = { regionMax.x - offsetRight - iconMarginX, regionMin.y + iconHeight };
DrawGuide(&offsetRight, regionMin, regionMax, btn.Icon, btn.Alignment, iconMin, iconMax, btn.FontQuality, textWidth, maxWidth, textScale, fontSize, str); DrawGuide(&offsetRight, regionMin, regionMax, btn.Icon, btn.Alignment, iconMin, iconMax, btn.FontQuality, textSize, fontSize, str);
} }
} }
+5 -9
View File
@@ -40,23 +40,19 @@ class Button
{ {
public: public:
std::string Name{}; std::string Name{};
float MaxWidth{ FLT_MAX };
EButtonIcon Icon{}; EButtonIcon Icon{};
EButtonAlignment Alignment{ EButtonAlignment::Right }; EButtonAlignment Alignment{ EButtonAlignment::Right };
EFontQuality FontQuality{ EFontQuality::High }; EFontQuality FontQuality{ EFontQuality::High };
bool* Visibility{ nullptr }; bool* Visibility{ nullptr };
Button(std::string name, float maxWidth, EButtonIcon icon, EButtonAlignment alignment, EFontQuality fontQuality = EFontQuality::High, bool* visibility = nullptr) Button(std::string name, EButtonIcon icon, EButtonAlignment alignment, EFontQuality fontQuality = EFontQuality::High, bool* visibility = nullptr)
: Name(name), MaxWidth(maxWidth), Icon(icon), Alignment(alignment), FontQuality(fontQuality), Visibility(visibility) {} : Name(name), Icon(icon), Alignment(alignment), FontQuality(fontQuality), Visibility(visibility) {}
Button(std::string name, float maxWidth, EButtonIcon icon, EButtonAlignment alignment, bool* visibility) Button(std::string name, EButtonIcon icon, EButtonAlignment alignment, bool* visibility) : Name(name), Icon(icon), Alignment(alignment), Visibility(visibility) {}
: Name(name), MaxWidth(maxWidth), Icon(icon), Alignment(alignment), Visibility(visibility) {}
Button(std::string name, float maxWidth, EButtonIcon icon, bool* visibility) Button(std::string name, EButtonIcon icon, bool* visibility) : Name(name), Icon(icon), Visibility(visibility) {}
: Name(name), MaxWidth(maxWidth), Icon(icon), Visibility(visibility) {}
Button(std::string name, float maxWidth, EButtonIcon icon, EFontQuality fontQuality = EFontQuality::High) Button(std::string name, EButtonIcon icon) : Name(name), Icon(icon) {}
: Name(name), MaxWidth(maxWidth), Icon(icon), FontQuality(fontQuality) {}
}; };
class ButtonGuide class ButtonGuide
+2 -135
View File
@@ -45,9 +45,6 @@ void ResetGradient()
void SetShaderModifier(uint32_t shaderModifier) void SetShaderModifier(uint32_t shaderModifier)
{ {
if (shaderModifier == IMGUI_SHADER_MODIFIER_LOW_QUALITY_TEXT && Config::DisableLowResolutionFontOnCustomUI)
shaderModifier = IMGUI_SHADER_MODIFIER_NONE;
auto callbackData = AddImGuiCallback(ImGuiCallback::SetShaderModifier); auto callbackData = AddImGuiCallback(ImGuiCallback::SetShaderModifier);
callbackData->setShaderModifier.shaderModifier = shaderModifier; callbackData->setShaderModifier.shaderModifier = shaderModifier;
} }
@@ -464,39 +461,17 @@ std::vector<std::string> Split(const char* strStart, const ImFont* font, float f
const bool wordWrapEnabled = (maxWidth > 0.0f); const bool wordWrapEnabled = (maxWidth > 0.0f);
const char *wordWrapEOL = nullptr; const char *wordWrapEOL = nullptr;
auto IsKanji = [](const char* str, const char* strEnd)
{
const char* tempStr = str;
unsigned int c = (unsigned int)*tempStr;
if (c < 0x80)
tempStr += 1;
else
tempStr += ImTextCharFromUtf8(&c, tempStr, strEnd);
// Basic CJK and CJK Extension A
return (c >= 0x4E00 && c <= 0x9FBF) || (c >= 0x3400 && c <= 0x4DBF);
};
while (*str != 0) while (*str != 0)
{ {
if (wordWrapEnabled) if (wordWrapEnabled)
{ {
if (wordWrapEOL == nullptr) if (wordWrapEOL == nullptr)
{ {
wordWrapEOL = CalcWordWrapPositionA(font, scale, str, strEnd, maxWidth - lineWidth); wordWrapEOL = font->CalcWordWrapPositionA(scale, str, strEnd, maxWidth - lineWidth);
} }
if (str >= wordWrapEOL) if (str >= wordWrapEOL)
{ {
if (IsKanji(str, strEnd))
{
// If the current character is Kanji, move back to prevent splitting Kanji
while (str > lineStart && IsKanji(str - 3, strEnd))
{
str -= 3;
}
}
if (textWidth < lineWidth) if (textWidth < lineWidth)
textWidth = lineWidth; textWidth = lineWidth;
@@ -670,7 +645,7 @@ ImVec2 MeasureCentredParagraph(const ImFont* font, float fontSize, float maxWidt
return MeasureCentredParagraph(font, fontSize, lineMargin, lines); return MeasureCentredParagraph(font, fontSize, lineMargin, lines);
} }
void DrawRubyAnnotatedText(const ImFont* font, float fontSize, float maxWidth, const ImVec2& pos, float lineMargin, const char* text, std::function<void(const char*, ImVec2)> drawMethod, std::function<void(const char*, float, ImVec2)> annotationDrawMethod, bool isCentred, bool leadingSpace) void DrawRubyAnnotatedText(const ImFont* font, float fontSize, float maxWidth, const ImVec2& pos, float lineMargin, const char* text, std::function<void(const char*, ImVec2)> drawMethod, std::function<void(const char*, float, ImVec2)> annotationDrawMethod, bool isCentred)
{ {
auto annotationFontSize = fontSize * ANNOTATION_FONT_SIZE_MODIFIER; auto annotationFontSize = fontSize * ANNOTATION_FONT_SIZE_MODIFIER;
@@ -678,13 +653,7 @@ void DrawRubyAnnotatedText(const ImFont* font, float fontSize, float maxWidth, c
auto lines = Split(input.first.c_str(), font, fontSize, maxWidth); auto lines = Split(input.first.c_str(), font, fontSize, maxWidth);
for (auto& line : lines) for (auto& line : lines)
{
line = ReAddRubyAnnotations(line, input.second); line = ReAddRubyAnnotations(line, input.second);
if (!line.empty() && line.substr(0, 3) != "" && leadingSpace)
{
line.insert(0, " ");
}
}
auto paragraphSize = MeasureCentredParagraph(font, fontSize, lineMargin, lines); auto paragraphSize = MeasureCentredParagraph(font, fontSize, lineMargin, lines);
auto offsetY = 0.0f; auto offsetY = 0.0f;
@@ -854,105 +823,3 @@ void DrawToggleLight(ImVec2 pos, bool isEnabled, float alpha)
drawList->AddImage(g_texLight.get(), min, max, GET_UV_COORDS(lightOffUVs), lightCol); drawList->AddImage(g_texLight.get(), min, max, GET_UV_COORDS(lightOffUVs), lightCol);
} }
} }
// Taken from ImGui because we need to modify to break for '\u200B\ too
// Simple word-wrapping for English, not full-featured. Please submit failing cases!
// This will return the next location to wrap from. If no wrapping if necessary, this will fast-forward to e.g. text_end.
// FIXME: Much possible improvements (don't cut things like "word !", "word!!!" but cut within "word,,,,", more sensible support for punctuations, support for Unicode punctuations, etc.)
const char* CalcWordWrapPositionA(const ImFont* font, float scale, const char* text, const char* text_end, float wrap_width)
{
// For references, possible wrap point marked with ^
// "aaa bbb, ccc,ddd. eee fff. ggg!"
// ^ ^ ^ ^ ^__ ^ ^
// List of hardcoded separators: .,;!?'"
// Skip extra blanks after a line returns (that includes not counting them in width computation)
// e.g. "Hello world" --> "Hello" "World"
// Cut words that cannot possibly fit within one line.
// e.g.: "The tropical fish" with ~5 characters worth of width --> "The tr" "opical" "fish"
float line_width = 0.0f;
float word_width = 0.0f;
float blank_width = 0.0f;
wrap_width /= scale; // We work with unscaled widths to avoid scaling every characters
const char* word_end = text;
const char* prev_word_end = NULL;
bool inside_word = true;
const char* s = text;
IM_ASSERT(text_end != NULL);
while (s < text_end)
{
unsigned int c = (unsigned int)*s;
const char* next_s;
if (c < 0x80)
next_s = s + 1;
else
next_s = s + ImTextCharFromUtf8(&c, s, text_end);
if (c < 32)
{
if (c == '\n')
{
line_width = word_width = blank_width = 0.0f;
inside_word = true;
s = next_s;
continue;
}
if (c == '\r')
{
s = next_s;
continue;
}
}
const float char_width = ((int)c < font->IndexAdvanceX.Size ? font->IndexAdvanceX.Data[c] : font->FallbackAdvanceX);
if (ImCharIsBlankW(c) || c == 0x200B)
{
if (inside_word)
{
line_width += blank_width;
blank_width = 0.0f;
word_end = s;
}
blank_width += char_width;
inside_word = false;
}
else
{
word_width += char_width;
if (inside_word)
{
word_end = next_s;
}
else
{
prev_word_end = word_end;
line_width += word_width + blank_width;
word_width = blank_width = 0.0f;
}
// Allow wrapping after punctuation.
inside_word = (c != '.' && c != ',' && c != ';' && c != '!' && c != '?' && c != '\"');
}
// We ignore blank width at the end of the line (they can be skipped)
if (line_width + word_width > wrap_width)
{
// Words that cannot possibly fit within an entire line will be cut anywhere.
if (word_width < wrap_width)
s = prev_word_end ? prev_word_end : word_end;
break;
}
s = next_s;
}
// Wrap_width is too small to fit anything. Force displaying 1 character to minimize the height discontinuity.
// +1 may not be a character start point in UTF-8 but it's ok because caller loops use (text >= word_wrap_eol).
if (s == text && text < text_end)
return s + 1;
return s;
}
+1 -2
View File
@@ -72,7 +72,7 @@ std::vector<std::string> RemoveAnnotationFromParagraph(const std::vector<std::st
std::string RemoveAnnotationFromParagraphLine(const std::vector<TextSegment>& annotatedLine); std::string RemoveAnnotationFromParagraphLine(const std::vector<TextSegment>& annotatedLine);
ImVec2 MeasureCentredParagraph(const ImFont* font, float fontSize, float lineMargin, const std::vector<std::string>& lines); ImVec2 MeasureCentredParagraph(const ImFont* font, float fontSize, float lineMargin, const std::vector<std::string>& lines);
ImVec2 MeasureCentredParagraph(const ImFont* font, float fontSize, float maxWidth, float lineMargin, const char* text); ImVec2 MeasureCentredParagraph(const ImFont* font, float fontSize, float maxWidth, float lineMargin, const char* text);
void DrawRubyAnnotatedText(const ImFont* font, float fontSize, float maxWidth, const ImVec2& pos, float lineMargin, const char* text, std::function<void(const char*, ImVec2)> drawMethod, std::function<void(const char*, float, ImVec2)> annotationDrawMethod, bool isCentred = false, bool leadingSpace = false); void DrawRubyAnnotatedText(const ImFont* font, float fontSize, float maxWidth, const ImVec2& pos, float lineMargin, const char* text, std::function<void(const char*, ImVec2)> drawMethod, std::function<void(const char*, float, ImVec2)> annotationDrawMethod, bool isCentred = false);
float Lerp(float a, float b, float t); float Lerp(float a, float b, float t);
float Cubic(float a, float b, float t); float Cubic(float a, float b, float t);
float Hermite(float a, float b, float t); float Hermite(float a, float b, float t);
@@ -81,4 +81,3 @@ ImU32 ColourLerp(ImU32 c0, ImU32 c1, float t);
void DrawVersionString(const ImFont* font, const ImU32 col = IM_COL32(255, 255, 255, 70)); void DrawVersionString(const ImFont* font, const ImU32 col = IM_COL32(255, 255, 255, 70));
void DrawSelectionContainer(ImVec2 min, ImVec2 max, bool fadeTop = false); void DrawSelectionContainer(ImVec2 min, ImVec2 max, bool fadeTop = false);
void DrawToggleLight(ImVec2 pos, bool isEnabled, float alpha = 1.0f); void DrawToggleLight(ImVec2 pos, bool isEnabled, float alpha = 1.0f);
const char* CalcWordWrapPositionA(const ImFont* font, float scale, const char* text, const char* text_end, float wrap_width);
+6 -18
View File
@@ -736,14 +736,6 @@ static void DrawDescriptionContainer()
} }
else if (g_currentPage == WizardPage::InstallFailed) else if (g_currentPage == WizardPage::InstallFailed)
{ {
// Japanese needs text to be brought in by a normal width space
// as it allows for text to begin further than others for
// special characters.
if (Config::Language == ELanguage::Japanese)
{
strncat(descriptionText, " ", 1);
}
strncat(descriptionText, g_installerErrorMessage.c_str(), sizeof(descriptionText) - 1); strncat(descriptionText, g_installerErrorMessage.c_str(), sizeof(descriptionText) - 1);
} }
@@ -777,8 +769,6 @@ static void DrawDescriptionContainer()
textX += annotationFontSize; textX += annotationFontSize;
textY += annotationFontSize; textY += annotationFontSize;
lineWidth += annotationFontSize;
} }
drawList->PushClipRect(clipRectMin, clipRectMax, false); drawList->PushClipRect(clipRectMin, clipRectMax, false);
@@ -798,9 +788,7 @@ static void DrawDescriptionContainer()
[=](const char* str, float size, ImVec2 pos) [=](const char* str, float size, ImVec2 pos)
{ {
DrawTextBasic(g_seuratFont, size, pos, IM_COL32(255, 255, 255, 255 * textAlpha), str); DrawTextBasic(g_seuratFont, size, pos, IM_COL32(255, 255, 255, 255 * textAlpha), str);
}, }
false,
Config::Language == ELanguage::Japanese
); );
drawList->PopClipRect(); drawList->PopClipRect();
@@ -884,7 +872,7 @@ static void DrawDescriptionContainer()
if (g_currentPage == WizardPage::InstallSucceeded && textAlpha >= 1.0) if (g_currentPage == WizardPage::InstallSucceeded && textAlpha >= 1.0)
{ {
ButtonGuide::Open(Button("Common_Select", 115.0f, selectIcon)); ButtonGuide::Open(Button("Common_Select", selectIcon));
} }
else if (g_currentPage != WizardPage::Installing && textAlpha >= 1.0) else if (g_currentPage != WizardPage::Installing && textAlpha >= 1.0)
{ {
@@ -896,15 +884,15 @@ static void DrawDescriptionContainer()
std::array<Button, 2> buttons = std::array<Button, 2> buttons =
{ {
Button("Common_Select", 115.0f, selectIcon), Button("Common_Select", selectIcon),
Button(backKey, FLT_MAX, backIcon) Button(backKey, backIcon)
}; };
ButtonGuide::Open(buttons); ButtonGuide::Open(buttons);
} }
else if (g_currentPage == WizardPage::Installing) else if (g_currentPage == WizardPage::Installing)
{ {
ButtonGuide::Open(Button("Common_Cancel", FLT_MAX, backIcon)); ButtonGuide::Open(Button("Common_Cancel", backIcon));
} }
else else
{ {
@@ -1411,7 +1399,7 @@ static void DrawNavigationButton()
float squashRatio; float squashRatio;
constexpr float NAV_BUTTON_MAX_TEXT_WIDTH = 90.0f; constexpr float NAV_BUTTON_MAX_TEXT_WIDTH = 90.0f;
std::string_view nextButtonKey = "Installer_Button_Next"; const char *nextButtonKey = "Installer_Button_Next";
if (skipButton) if (skipButton)
{ {
nextButtonKey = "Installer_Button_Skip"; nextButtonKey = "Installer_Button_Skip";
+7 -35
View File
@@ -223,10 +223,6 @@ void DrawButton(int rowIndex, float yOffset, float width, float height, std::str
auto fontSize = Scale(28); auto fontSize = Scale(28);
auto textSize = g_fntSeurat->CalcTextSizeA(fontSize, FLT_MAX, 0, text.c_str()); auto textSize = g_fntSeurat->CalcTextSizeA(fontSize, FLT_MAX, 0, text.c_str());
// Show low quality text in-game.
if (App::s_isInit)
SetShaderModifier(IMGUI_SHADER_MODIFIER_LOW_QUALITY_TEXT);
DrawTextWithShadow DrawTextWithShadow
( (
g_fntSeurat, g_fntSeurat,
@@ -235,10 +231,6 @@ void DrawButton(int rowIndex, float yOffset, float width, float height, std::str
isSelected ? IM_COL32(255, 128, 0, 255) : IM_COL32(255, 255, 255, 255), isSelected ? IM_COL32(255, 128, 0, 255) : IM_COL32(255, 255, 255, 255),
text.c_str() text.c_str()
); );
// Reset the shader modifier.
if (App::s_isInit)
SetShaderModifier(IMGUI_SHADER_MODIFIER_NONE);
} }
void DrawNextButtonGuide(bool isController, bool isKeyboard) void DrawNextButtonGuide(bool isController, bool isKeyboard)
@@ -249,16 +241,11 @@ void DrawNextButtonGuide(bool isController, bool isKeyboard)
? EButtonIcon::Enter ? EButtonIcon::Enter
: EButtonIcon::LMB; : EButtonIcon::LMB;
auto fontQuality = EFontQuality::High; // Always show controller prompt in-game.
// Always show controller prompt and low quality text in-game.
if (App::s_isInit) if (App::s_isInit)
{
icon = EButtonIcon::A; icon = EButtonIcon::A;
fontQuality = EFontQuality::Low;
}
ButtonGuide::Open(Button("Common_Next", FLT_MAX, icon, fontQuality)); ButtonGuide::Open(Button("Common_Next", icon));
} }
static void ResetSelection() static void ResetSelection()
@@ -351,10 +338,6 @@ void MessageWindow::Draw()
if (DrawContainer(g_appearTime, centre, { textSize.x / 2 + textMarginX, textSize.y / 2 + textMarginY }, !g_isControlsVisible)) if (DrawContainer(g_appearTime, centre, { textSize.x / 2 + textMarginX, textSize.y / 2 + textMarginY }, !g_isControlsVisible))
{ {
// Use low quality text when the game is booted to not clash with existing UI.
if (App::s_isInit)
SetShaderModifier(IMGUI_SHADER_MODIFIER_LOW_QUALITY_TEXT);
DrawRubyAnnotatedText DrawRubyAnnotatedText
( (
g_fntSeurat, g_fntSeurat,
@@ -370,16 +353,12 @@ void MessageWindow::Draw()
}, },
[=](const char* str, float size, ImVec2 pos) [=](const char* str, float size, ImVec2 pos)
{ {
DrawTextWithShadow(g_fntSeurat, size, pos, IM_COL32(255, 255, 255, 255), str, 1.5f, 1.5f); DrawTextWithShadow(g_fntSeurat, size, pos, IM_COL32(255, 255, 255, 255), str, 1.0f);
}, },
true true
); );
// Reset the shader modifier.
if (App::s_isInit)
SetShaderModifier(IMGUI_SHADER_MODIFIER_LOW_QUALITY_TEXT);
drawList->PopClipRect(); drawList->PopClipRect();
if (g_buttons.size()) if (g_buttons.size())
@@ -444,17 +423,10 @@ void MessageWindow::Draw()
backIcon = EButtonIcon::Escape; backIcon = EButtonIcon::Escape;
} }
auto fontQuality = EFontQuality::High;
if (App::s_isInit)
{
// Show low quality text in-game.
fontQuality = EFontQuality::Low;
}
std::array<Button, 2> buttons = std::array<Button, 2> buttons =
{ {
Button("Common_Select", 115.0f, selectIcon, fontQuality), Button("Common_Select", selectIcon),
Button("Common_Back", FLT_MAX, backIcon, fontQuality), Button("Common_Back", backIcon),
}; };
ButtonGuide::Open(buttons); ButtonGuide::Open(buttons);
@@ -498,8 +470,8 @@ void MessageWindow::Draw()
std::array<Button, 2> buttons = std::array<Button, 2> buttons =
{ {
Button("Common_Select", 115.0f, EButtonIcon::LMB), Button("Common_Select", EButtonIcon::LMB),
Button("Common_Back", FLT_MAX, EButtonIcon::Escape), Button("Common_Back", EButtonIcon::Escape),
}; };
ButtonGuide::Open(buttons); ButtonGuide::Open(buttons);
+11 -13
View File
@@ -449,8 +449,11 @@ static bool DrawCategories()
auto inputState = SWA::CInputState::GetInstance(); auto inputState = SWA::CInputState::GetInstance();
bool moveLeft = !g_lockedOnOption && inputState->GetPadState().IsTapped(SWA::eKeyState_LeftBumper); bool moveLeft = !g_lockedOnOption && (inputState->GetPadState().IsTapped(SWA::eKeyState_LeftBumper) ||
bool moveRight = !g_lockedOnOption && inputState->GetPadState().IsTapped(SWA::eKeyState_RightBumper); inputState->GetPadState().IsTapped(SWA::eKeyState_LeftTrigger));
bool moveRight = !g_lockedOnOption && (inputState->GetPadState().IsTapped(SWA::eKeyState_RightBumper) ||
inputState->GetPadState().IsTapped(SWA::eKeyState_RightTrigger));
if (moveLeft) if (moveLeft)
{ {
@@ -1494,7 +1497,6 @@ static void DrawInfoPanel(ImVec2 infoMin, ImVec2 infoMax)
auto textX = clipRectMin.x - Scale(0.5f); auto textX = clipRectMin.x - Scale(0.5f);
auto textY = thumbnailMax.y + offsetY; auto textY = thumbnailMax.y + offsetY;
float lineWidth = clipRectMax.x - clipRectMin.x;
if (Config::Language == ELanguage::Japanese) if (Config::Language == ELanguage::Japanese)
{ {
@@ -1509,13 +1511,9 @@ static void DrawInfoPanel(ImVec2 infoMin, ImVec2 infoMax)
clipRectMax.x += annotationFontSize; clipRectMax.x += annotationFontSize;
textY += annotationFontSize; textY += annotationFontSize;
// Dirty hack to disallow clipping on Japanese text
// whilst allowing annotations to go over the border
lineWidth -= annotationFontSize;
} }
auto textSize = MeasureCentredParagraph(g_seuratFont, fontSize, lineWidth, 5.0f, desc.c_str()); auto textSize = MeasureCentredParagraph(g_seuratFont, fontSize, clipRectMax.x - clipRectMin.x, 5.0f, desc.c_str());
drawList->PushClipRect(clipRectMin, clipRectMax, false); drawList->PushClipRect(clipRectMin, clipRectMax, false);
@@ -1591,7 +1589,7 @@ static void DrawInfoPanel(ImVec2 infoMin, ImVec2 infoMax)
( (
g_seuratFont, g_seuratFont,
fontSize, fontSize,
lineWidth, clipRectMax.x - clipRectMin.x,
{ textX, textY - scrollOffset }, { textX, textY - scrollOffset },
5.0f, 5.0f,
desc.c_str(), desc.c_str(),
@@ -1792,10 +1790,10 @@ void OptionsMenu::Open(bool isPause, SWA::EMenuType pauseMenuType)
std::array<Button, 4> buttons = std::array<Button, 4> buttons =
{ {
Button("Common_Switch", 115.0f, EButtonIcon::LBRB, EButtonAlignment::Left, &g_isControlsVisible), Button("Common_Switch", EButtonIcon::LBRB, EButtonAlignment::Left, &g_isControlsVisible),
Button("Common_Reset", 110.0f, EButtonIcon::X, &g_canReset), Button("Common_Reset", EButtonIcon::X, &g_canReset),
Button("Common_Select", 115.0f, EButtonIcon::A, &g_isControlsVisible), Button("Common_Select", EButtonIcon::A, &g_isControlsVisible),
Button("Common_Back", 65.0f, EButtonIcon::B, &g_isControlsVisible) Button("Common_Back", EButtonIcon::B, &g_isControlsVisible)
}; };
ButtonGuide::Open(buttons); ButtonGuide::Open(buttons);
+42 -56
View File
@@ -2,9 +2,9 @@
#include <decompressor.h> #include <decompressor.h>
#include <hid/hid.h> #include <hid/hid.h>
// TODO (Hyper): lower the resolution of these textures once final.
#include <res/images/options_menu/thumbnails/achievement_notifications.dds.h> #include <res/images/options_menu/thumbnails/achievement_notifications.dds.h>
#include <res/images/options_menu/thumbnails/allow_background_input_xb.dds.h> #include <res/images/options_menu/thumbnails/allow_background_input.dds.h>
#include <res/images/options_menu/thumbnails/allow_background_input_ps.dds.h>
#include <res/images/options_menu/thumbnails/antialiasing_none.dds.h> #include <res/images/options_menu/thumbnails/antialiasing_none.dds.h>
#include <res/images/options_menu/thumbnails/antialiasing_2x.dds.h> #include <res/images/options_menu/thumbnails/antialiasing_2x.dds.h>
#include <res/images/options_menu/thumbnails/antialiasing_4x.dds.h> #include <res/images/options_menu/thumbnails/antialiasing_4x.dds.h>
@@ -14,8 +14,8 @@
#include <res/images/options_menu/thumbnails/brightness.dds.h> #include <res/images/options_menu/thumbnails/brightness.dds.h>
#include <res/images/options_menu/thumbnails/channel_stereo.dds.h> #include <res/images/options_menu/thumbnails/channel_stereo.dds.h>
#include <res/images/options_menu/thumbnails/channel_surround.dds.h> #include <res/images/options_menu/thumbnails/channel_surround.dds.h>
#include <res/images/options_menu/thumbnails/control_tutorial_ps.dds.h>
#include <res/images/options_menu/thumbnails/control_tutorial_xb.dds.h> #include <res/images/options_menu/thumbnails/control_tutorial_xb.dds.h>
#include <res/images/options_menu/thumbnails/control_tutorial_ps.dds.h>
#include <res/images/options_menu/thumbnails/controller_icons.dds.h> #include <res/images/options_menu/thumbnails/controller_icons.dds.h>
#include <res/images/options_menu/thumbnails/default.dds.h> #include <res/images/options_menu/thumbnails/default.dds.h>
#include <res/images/options_menu/thumbnails/effects_volume.dds.h> #include <res/images/options_menu/thumbnails/effects_volume.dds.h>
@@ -35,21 +35,23 @@
#include <res/images/options_menu/thumbnails/movie_scale_fill.dds.h> #include <res/images/options_menu/thumbnails/movie_scale_fill.dds.h>
#include <res/images/options_menu/thumbnails/music_attenuation.dds.h> #include <res/images/options_menu/thumbnails/music_attenuation.dds.h>
#include <res/images/options_menu/thumbnails/music_volume.dds.h> #include <res/images/options_menu/thumbnails/music_volume.dds.h>
#include <res/images/options_menu/thumbnails/resolution_scale.dds.h>
#include <res/images/options_menu/thumbnails/shadow_resolution_original.dds.h>
#include <res/images/options_menu/thumbnails/shadow_resolution_x512.dds.h> #include <res/images/options_menu/thumbnails/shadow_resolution_x512.dds.h>
#include <res/images/options_menu/thumbnails/shadow_resolution_x1024.dds.h> #include <res/images/options_menu/thumbnails/shadow_resolution_x1024.dds.h>
#include <res/images/options_menu/thumbnails/shadow_resolution_x2048.dds.h> #include <res/images/options_menu/thumbnails/shadow_resolution_x2048.dds.h>
#include <res/images/options_menu/thumbnails/shadow_resolution_x4096.dds.h> #include <res/images/options_menu/thumbnails/shadow_resolution_x4096.dds.h>
#include <res/images/options_menu/thumbnails/shadow_resolution_x8192.dds.h> #include <res/images/options_menu/thumbnails/shadow_resolution_x8192.dds.h>
#include <res/images/options_menu/thumbnails/time_transition_ps.dds.h> #include <res/images/options_menu/thumbnails/subtitles.dds.h>
#include <res/images/options_menu/thumbnails/time_transition_xb.dds.h> #include <res/images/options_menu/thumbnails/time_transition_xb.dds.h>
#include <res/images/options_menu/thumbnails/time_transition_ps.dds.h>
#include <res/images/options_menu/thumbnails/transparency_antialiasing_false.dds.h> #include <res/images/options_menu/thumbnails/transparency_antialiasing_false.dds.h>
#include <res/images/options_menu/thumbnails/transparency_antialiasing_true.dds.h> #include <res/images/options_menu/thumbnails/transparency_antialiasing_true.dds.h>
#include <res/images/options_menu/thumbnails/ui_alignment_centre.dds.h> #include <res/images/options_menu/thumbnails/ui_alignment_centre.dds.h>
#include <res/images/options_menu/thumbnails/ui_alignment_edge.dds.h> #include <res/images/options_menu/thumbnails/ui_alignment_edge.dds.h>
#include <res/images/options_menu/thumbnails/vertical_camera.dds.h> #include <res/images/options_menu/thumbnails/vertical_camera.dds.h>
#include <res/images/options_menu/thumbnails/voice_language.dds.h> #include <res/images/options_menu/thumbnails/voice_language.dds.h>
#include <res/images/options_menu/thumbnails/vibration_ps.dds.h> #include <res/images/options_menu/thumbnails/vibration.dds.h>
#include <res/images/options_menu/thumbnails/vibration_xb.dds.h>
#include <res/images/options_menu/thumbnails/vsync_on.dds.h> #include <res/images/options_menu/thumbnails/vsync_on.dds.h>
#include <res/images/options_menu/thumbnails/vsync_off.dds.h> #include <res/images/options_menu/thumbnails/vsync_off.dds.h>
#include <res/images/options_menu/thumbnails/window_size.dds.h> #include <res/images/options_menu/thumbnails/window_size.dds.h>
@@ -57,15 +59,7 @@
#define VALUE_THUMBNAIL_MAP(type) std::unordered_map<type, std::unique_ptr<GuestTexture>> #define VALUE_THUMBNAIL_MAP(type) std::unordered_map<type, std::unique_ptr<GuestTexture>>
static std::unique_ptr<GuestTexture> g_defaultThumbnail; static std::unordered_map<std::string_view, std::unique_ptr<GuestTexture>> g_namedThumbnails;
static std::unique_ptr<GuestTexture> g_controlTutorialXBThumbnail;
static std::unique_ptr<GuestTexture> g_controlTutorialPSThumbnail;
static std::unique_ptr<GuestTexture> g_vibrationXBThumbnail;
static std::unique_ptr<GuestTexture> g_vibrationPSThumbnail;
static std::unique_ptr<GuestTexture> g_backgroundInputXBThumbnail;
static std::unique_ptr<GuestTexture> g_backgroundInputPSThumbnail;
static std::unordered_map<const IConfigDef*, std::unique_ptr<GuestTexture>> g_configThumbnails; static std::unordered_map<const IConfigDef*, std::unique_ptr<GuestTexture>> g_configThumbnails;
static VALUE_THUMBNAIL_MAP(ETimeOfDayTransition) g_timeOfDayTransitionThumbnails; static VALUE_THUMBNAIL_MAP(ETimeOfDayTransition) g_timeOfDayTransitionThumbnails;
@@ -82,17 +76,14 @@ static VALUE_THUMBNAIL_MAP(EUIAlignmentMode) g_uiAlignmentThumbnails;
void LoadThumbnails() void LoadThumbnails()
{ {
g_defaultThumbnail = LOAD_ZSTD_TEXTURE(g_default); g_namedThumbnails["Default"] = LOAD_ZSTD_TEXTURE(g_default);
g_namedThumbnails["WindowSize"] = LOAD_ZSTD_TEXTURE(g_window_size);
g_controlTutorialXBThumbnail = LOAD_ZSTD_TEXTURE(g_control_tutorial_xb); g_namedThumbnails["ControlTutorialXB"] = LOAD_ZSTD_TEXTURE(g_control_tutorial_xb);
g_controlTutorialPSThumbnail = LOAD_ZSTD_TEXTURE(g_control_tutorial_ps); g_namedThumbnails["ControlTutorialPS"] = LOAD_ZSTD_TEXTURE(g_control_tutorial_ps);
g_vibrationXBThumbnail = LOAD_ZSTD_TEXTURE(g_vibration_xb);
g_vibrationPSThumbnail = LOAD_ZSTD_TEXTURE(g_vibration_ps);
g_backgroundInputXBThumbnail = LOAD_ZSTD_TEXTURE(g_allow_background_input_xb);
g_backgroundInputPSThumbnail = LOAD_ZSTD_TEXTURE(g_allow_background_input_ps);
g_configThumbnails[&Config::Language] = LOAD_ZSTD_TEXTURE(g_language); g_configThumbnails[&Config::Language] = LOAD_ZSTD_TEXTURE(g_language);
g_configThumbnails[&Config::VoiceLanguage] = LOAD_ZSTD_TEXTURE(g_voice_language); g_configThumbnails[&Config::VoiceLanguage] = LOAD_ZSTD_TEXTURE(g_voice_language);
g_configThumbnails[&Config::Subtitles] = LOAD_ZSTD_TEXTURE(g_subtitles);
g_configThumbnails[&Config::Hints] = LOAD_ZSTD_TEXTURE(g_hints); g_configThumbnails[&Config::Hints] = LOAD_ZSTD_TEXTURE(g_hints);
g_configThumbnails[&Config::AchievementNotifications] = LOAD_ZSTD_TEXTURE(g_achievement_notifications); g_configThumbnails[&Config::AchievementNotifications] = LOAD_ZSTD_TEXTURE(g_achievement_notifications);
@@ -101,6 +92,8 @@ void LoadThumbnails()
g_configThumbnails[&Config::HorizontalCamera] = LOAD_ZSTD_TEXTURE(g_horizontal_camera); g_configThumbnails[&Config::HorizontalCamera] = LOAD_ZSTD_TEXTURE(g_horizontal_camera);
g_configThumbnails[&Config::VerticalCamera] = LOAD_ZSTD_TEXTURE(g_vertical_camera); g_configThumbnails[&Config::VerticalCamera] = LOAD_ZSTD_TEXTURE(g_vertical_camera);
g_configThumbnails[&Config::Vibration] = LOAD_ZSTD_TEXTURE(g_vibration);
g_configThumbnails[&Config::AllowBackgroundInput] = LOAD_ZSTD_TEXTURE(g_allow_background_input);
g_configThumbnails[&Config::ControllerIcons] = LOAD_ZSTD_TEXTURE(g_controller_icons); g_configThumbnails[&Config::ControllerIcons] = LOAD_ZSTD_TEXTURE(g_controller_icons);
g_configThumbnails[&Config::MasterVolume] = LOAD_ZSTD_TEXTURE(g_master_volume); g_configThumbnails[&Config::MasterVolume] = LOAD_ZSTD_TEXTURE(g_master_volume);
g_configThumbnails[&Config::MusicVolume] = LOAD_ZSTD_TEXTURE(g_music_volume); g_configThumbnails[&Config::MusicVolume] = LOAD_ZSTD_TEXTURE(g_music_volume);
@@ -114,8 +107,8 @@ void LoadThumbnails()
g_configThumbnails[&Config::WindowSize] = LOAD_ZSTD_TEXTURE(g_window_size); g_configThumbnails[&Config::WindowSize] = LOAD_ZSTD_TEXTURE(g_window_size);
g_configThumbnails[&Config::Monitor] = LOAD_ZSTD_TEXTURE(g_monitor); g_configThumbnails[&Config::Monitor] = LOAD_ZSTD_TEXTURE(g_monitor);
g_configThumbnails[&Config::AspectRatio] = LOAD_ZSTD_TEXTURE(g_aspect_ratio); g_configThumbnails[&Config::AspectRatio] = LOAD_ZSTD_TEXTURE(g_aspect_ratio);
g_configThumbnails[&Config::ResolutionScale] = LOAD_ZSTD_TEXTURE(g_resolution_scale);
g_configThumbnails[&Config::Fullscreen] = LOAD_ZSTD_TEXTURE(g_fullscreen); g_configThumbnails[&Config::Fullscreen] = LOAD_ZSTD_TEXTURE(g_fullscreen);
g_configThumbnails[&Config::XboxColorCorrection] = LOAD_ZSTD_TEXTURE(g_xbox_color_correction);
g_vsyncThumbnails[false] = LOAD_ZSTD_TEXTURE(g_vsync_off); g_vsyncThumbnails[false] = LOAD_ZSTD_TEXTURE(g_vsync_off);
g_vsyncThumbnails[true] = LOAD_ZSTD_TEXTURE(g_vsync_on); g_vsyncThumbnails[true] = LOAD_ZSTD_TEXTURE(g_vsync_on);
@@ -131,6 +124,7 @@ void LoadThumbnails()
g_transparencyAntiAliasingThumbnails[false] = LOAD_ZSTD_TEXTURE(g_transparency_antialiasing_false); g_transparencyAntiAliasingThumbnails[false] = LOAD_ZSTD_TEXTURE(g_transparency_antialiasing_false);
g_transparencyAntiAliasingThumbnails[true] = LOAD_ZSTD_TEXTURE(g_transparency_antialiasing_true); g_transparencyAntiAliasingThumbnails[true] = LOAD_ZSTD_TEXTURE(g_transparency_antialiasing_true);
g_shadowResolutionThumbnails[EShadowResolution::Original] = LOAD_ZSTD_TEXTURE(g_shadow_resolution_original);
g_shadowResolutionThumbnails[EShadowResolution::x512] = LOAD_ZSTD_TEXTURE(g_shadow_resolution_x512); g_shadowResolutionThumbnails[EShadowResolution::x512] = LOAD_ZSTD_TEXTURE(g_shadow_resolution_x512);
g_shadowResolutionThumbnails[EShadowResolution::x1024] = LOAD_ZSTD_TEXTURE(g_shadow_resolution_x1024); g_shadowResolutionThumbnails[EShadowResolution::x1024] = LOAD_ZSTD_TEXTURE(g_shadow_resolution_x1024);
g_shadowResolutionThumbnails[EShadowResolution::x2048] = LOAD_ZSTD_TEXTURE(g_shadow_resolution_x2048); g_shadowResolutionThumbnails[EShadowResolution::x2048] = LOAD_ZSTD_TEXTURE(g_shadow_resolution_x2048);
@@ -149,6 +143,8 @@ void LoadThumbnails()
g_uiAlignmentThumbnails[EUIAlignmentMode::Centre] = LOAD_ZSTD_TEXTURE(g_ui_alignment_centre); g_uiAlignmentThumbnails[EUIAlignmentMode::Centre] = LOAD_ZSTD_TEXTURE(g_ui_alignment_centre);
g_uiAlignmentThumbnails[EUIAlignmentMode::Edge] = LOAD_ZSTD_TEXTURE(g_ui_alignment_edge); g_uiAlignmentThumbnails[EUIAlignmentMode::Edge] = LOAD_ZSTD_TEXTURE(g_ui_alignment_edge);
g_configThumbnails[&Config::XboxColorCorrection] = LOAD_ZSTD_TEXTURE(g_xbox_color_correction);
} }
template<typename T> template<typename T>
@@ -160,50 +156,40 @@ bool TryGetValueThumbnail(const IConfigDef* cfg, VALUE_THUMBNAIL_MAP(T)* thumbna
if (!cfg->GetValue()) if (!cfg->GetValue())
return false; return false;
T value = *(T*)cfg->GetValue(); auto result = thumbnails->at(*(T*)cfg->GetValue()).get();
if constexpr (std::is_same_v<T, EShadowResolution>) if (!result)
{ return false;
if (value == EShadowResolution::Original)
value = EShadowResolution::x1024;
}
auto findResult = thumbnails->find(value); *texture = result;
if (findResult != thumbnails->end()) return true;
{ }
*texture = findResult->second.get();
return true;
}
return false; GuestTexture* GetThumbnail(const std::string_view name)
{
if (!g_namedThumbnails.count(name))
return g_namedThumbnails["Default"].get();
return g_namedThumbnails[name].get();
} }
GuestTexture* GetThumbnail(const IConfigDef* cfg) GuestTexture* GetThumbnail(const IConfigDef* cfg)
{ {
auto findResult = g_configThumbnails.find(cfg); if (!g_configThumbnails.count(cfg))
if (findResult == g_configThumbnails.end())
{ {
auto texture = g_defaultThumbnail.get(); auto texture = g_namedThumbnails["Default"].get();
bool isPlayStation = Config::ControllerIcons == EControllerIcons::PlayStation;
if (Config::ControllerIcons == EControllerIcons::Auto)
isPlayStation = hid::g_inputDeviceController == hid::EInputDevice::PlayStation;
if (cfg == &Config::ControlTutorial) if (cfg == &Config::ControlTutorial)
{ {
texture = isPlayStation ? g_controlTutorialPSThumbnail.get() : g_controlTutorialXBThumbnail.get(); bool isPlayStation = Config::ControllerIcons == EControllerIcons::PlayStation;
if (Config::ControllerIcons == EControllerIcons::Auto)
isPlayStation = hid::g_inputDevicePad == hid::EInputDevice::PlayStation;
texture = isPlayStation ? g_namedThumbnails["ControlTutorialPS"].get() : g_namedThumbnails["ControlTutorialXB"].get();
} }
else if (cfg == &Config::Vibration) if (cfg == &Config::TimeOfDayTransition)
{
texture = isPlayStation ? g_vibrationPSThumbnail.get() : g_vibrationXBThumbnail.get();
}
else if (cfg == &Config::AllowBackgroundInput)
{
texture = isPlayStation ? g_backgroundInputPSThumbnail.get() : g_backgroundInputXBThumbnail.get();
}
else if (cfg == &Config::TimeOfDayTransition)
{ {
TryGetValueThumbnail<ETimeOfDayTransition>(cfg, &g_timeOfDayTransitionThumbnails, &texture); TryGetValueThumbnail<ETimeOfDayTransition>(cfg, &g_timeOfDayTransitionThumbnails, &texture);
} }
@@ -251,5 +237,5 @@ GuestTexture* GetThumbnail(const IConfigDef* cfg)
return texture; return texture;
} }
return findResult->second.get(); return g_configThumbnails[cfg].get();
} }
@@ -5,4 +5,5 @@
void LoadThumbnails(); void LoadThumbnails();
GuestTexture* GetThumbnail(const std::string_view name);
GuestTexture* GetThumbnail(const IConfigDef* cfg); GuestTexture* GetThumbnail(const IConfigDef* cfg);
+58 -48
View File
@@ -511,33 +511,43 @@ std::string_view ConfigDef<T, isHidden>::GetName() const
template<typename T, bool isHidden> template<typename T, bool isHidden>
std::string ConfigDef<T, isHidden>::GetNameLocalised(ELanguage language) const std::string ConfigDef<T, isHidden>::GetNameLocalised(ELanguage language) const
{ {
if (Locale != nullptr) if (!Locale)
{ return Name;
auto languageFindResult = Locale->find(language);
if (languageFindResult == Locale->end())
languageFindResult = Locale->find(ELanguage::English);
if (languageFindResult != Locale->end()) if (!Locale->count(language))
return std::get<0>(languageFindResult->second); {
if (Locale->count(ELanguage::English))
{
return std::get<0>(Locale->at(ELanguage::English));
}
else
{
return Name;
}
} }
return Name; return std::get<0>(Locale->at(language));
} }
template<typename T, bool isHidden> template<typename T, bool isHidden>
std::string ConfigDef<T, isHidden>::GetDescription(ELanguage language) const std::string ConfigDef<T, isHidden>::GetDescription(ELanguage language) const
{ {
if (Locale != nullptr) if (!Locale)
{ return "";
auto languageFindResult = Locale->find(language);
if (languageFindResult == Locale->end())
languageFindResult = Locale->find(ELanguage::English);
if (languageFindResult != Locale->end()) if (!Locale->count(language))
return std::get<1>(languageFindResult->second); {
if (Locale->count(ELanguage::English))
{
return std::get<1>(Locale->at(ELanguage::English));
}
else
{
return "";
}
} }
return ""; return std::get<1>(Locale->at(language));
} }
template<typename T, bool isHidden> template<typename T, bool isHidden>
@@ -568,27 +578,27 @@ std::string ConfigDef<T, isHidden>::GetValueLocalised(ELanguage language) const
: Localise("Common_Off"); : Localise("Common_Off");
} }
if (locale != nullptr) if (!locale)
return ToString(false);
if (!locale->count(language))
{ {
ELanguage languages[] = { language, ELanguage::English }; if (locale->count(ELanguage::English))
for (auto languageToFind : languages)
{ {
auto languageFindResult = locale->find(languageToFind); language = ELanguage::English;
}
if (languageFindResult != locale->end()) else
{ {
auto valueFindResult = languageFindResult->second.find(Value); return ToString(false);
if (valueFindResult != languageFindResult->second.end())
return std::get<0>(valueFindResult->second);
}
if (languageToFind == ELanguage::English)
break;
} }
} }
return ToString(false); auto strings = locale->at(language);
if (!strings.count(Value))
return ToString(false);
return std::get<0>(strings.at(Value));
} }
template<typename T, bool isHidden> template<typename T, bool isHidden>
@@ -605,27 +615,27 @@ std::string ConfigDef<T, isHidden>::GetValueDescription(ELanguage language) cons
return ""; return "";
} }
if (locale != nullptr) if (!locale)
return "";
if (!locale->count(language))
{ {
ELanguage languages[] = { language, ELanguage::English }; if (locale->count(ELanguage::English))
for (auto languageToFind : languages)
{ {
auto languageFindResult = locale->find(languageToFind); language = ELanguage::English;
}
if (languageFindResult != locale->end()) else
{ {
auto valueFindResult = languageFindResult->second.find(Value); return "";
if (valueFindResult != languageFindResult->second.end())
return std::get<1>(valueFindResult->second);
}
if (languageToFind == ELanguage::English)
break;
} }
} }
return ""; auto strings = locale->at(language);
if (!strings.count(Value))
return "";
return std::get<1>(strings.at(Value));
} }
template<typename T, bool isHidden> template<typename T, bool isHidden>
-1
View File
@@ -91,6 +91,5 @@ CONFIG_DEFINE_HIDDEN("Codes", bool, SaveScoreAtCheckpoints, false);
CONFIG_DEFINE_HIDDEN("Codes", bool, SkipIntroLogos, false); CONFIG_DEFINE_HIDDEN("Codes", bool, SkipIntroLogos, false);
CONFIG_DEFINE_HIDDEN("Codes", bool, UseArrowsForTimeOfDayTransition, false); CONFIG_DEFINE_HIDDEN("Codes", bool, UseArrowsForTimeOfDayTransition, false);
CONFIG_DEFINE_HIDDEN("Codes", bool, UseOfficialTitleOnTitleBar, false); CONFIG_DEFINE_HIDDEN("Codes", bool, UseOfficialTitleOnTitleBar, false);
CONFIG_DEFINE_HIDDEN("Codes", bool, DisableLowResolutionFontOnCustomUI, false);
CONFIG_DEFINE("Update", time_t, LastChecked, 0); CONFIG_DEFINE("Update", time_t, LastChecked, 0);
+1 -4
View File
@@ -28,10 +28,7 @@ endforeach()
add_custom_command( add_custom_command(
OUTPUT ${UNLEASHED_RECOMP_PPC_RECOMPILED_SOURCES} OUTPUT ${UNLEASHED_RECOMP_PPC_RECOMPILED_SOURCES}
COMMAND $<TARGET_FILE:XenonRecomp> COMMAND $<TARGET_FILE:XenonRecomp>
DEPENDS DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/private/default.xex" "${CMAKE_CURRENT_SOURCE_DIR}/config/SWA.toml"
"${CMAKE_CURRENT_SOURCE_DIR}/private/default.xex"
"${CMAKE_CURRENT_SOURCE_DIR}/private/default.xexp"
"${CMAKE_CURRENT_SOURCE_DIR}/config/SWA.toml"
) )
set(XENOS_RECOMP_ROOT "${UNLEASHED_RECOMP_TOOLS_ROOT}/XenosRecomp/XenosRecomp") set(XENOS_RECOMP_ROOT "${UNLEASHED_RECOMP_TOOLS_ROOT}/XenosRecomp/XenosRecomp")
-7
View File
@@ -1,7 +1,5 @@
[main] [main]
file_path = "../private/default.xex" file_path = "../private/default.xex"
patch_file_path = "../private/default.xexp"
patched_file_path = "../private/default_patched.xex"
out_directory_path = "../ppc" out_directory_path = "../ppc"
switch_table_file_path = "SWA_switch_tables.toml" switch_table_file_path = "SWA_switch_tables.toml"
@@ -1088,8 +1086,3 @@ registers = ["r4"]
name = "AnimationDataMakeMidAsmHook" name = "AnimationDataMakeMidAsmHook"
address = 0x82BB38E4 address = 0x82BB38E4
registers = ["r31", "r29", "r28"] registers = ["r31", "r29", "r28"]
[[midasm_hook]]
name = "ObjGrindDashPanelAllocMidAsmHook"
address = 0x82614948
registers = ["r3"]
@@ -38,16 +38,6 @@
"path": "private/default.xex", "path": "private/default.xex",
"dest": "UnleashedRecompLib/private" "dest": "UnleashedRecompLib/private"
}, },
{
"type": "file",
"path": "private/default.xexp",
"dest": "UnleashedRecompLib/private"
},
{
"type": "file",
"path": "private/default_patched.xex",
"dest": "UnleashedRecompLib/private"
},
{ {
"type": "file", "type": "file",
"path": "private/shader.ar", "path": "private/shader.ar",
+223
View File
@@ -0,0 +1,223 @@
/*
*
* TinySHA1 - a header only implementation of the SHA1 algorithm in C++. Based
* on the implementation in boost::uuid::details.
*
* SHA1 Wikipedia Page: http://en.wikipedia.org/wiki/SHA-1
*
* Copyright (c) 2012-22 SAURAV MOHAPATRA <mohaps@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Taken from https://github.com/mohaps/TinySHA1
* Modified for use by Xenia
*/
#ifndef _TINY_SHA1_HPP_
#define _TINY_SHA1_HPP_
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <stdint.h>
namespace sha1 {
class SHA1 {
public:
typedef uint32_t digest32_t[5];
typedef uint8_t digest8_t[20];
inline static uint32_t LeftRotate(uint32_t value, size_t count) {
return (value << count) ^ (value >> (32 - count));
}
SHA1() { reset(); }
virtual ~SHA1() {}
SHA1(const SHA1& s) { *this = s; }
const SHA1& operator=(const SHA1& s) {
memcpy(m_digest, s.m_digest, 5 * sizeof(uint32_t));
memcpy(m_block, s.m_block, 64);
m_blockByteIndex = s.m_blockByteIndex;
m_byteCount = s.m_byteCount;
return *this;
}
SHA1& init(const uint32_t digest[5], const uint8_t block[64],
uint32_t count) {
std::memcpy(m_digest, digest, 20);
std::memcpy(m_block, block, count % 64);
m_byteCount = count;
m_blockByteIndex = count % 64;
return *this;
}
const uint32_t* getDigest() const { return m_digest; }
const uint8_t* getBlock() const { return m_block; }
size_t getBlockByteIndex() const { return m_blockByteIndex; }
size_t getByteCount() const { return m_byteCount; }
SHA1& reset() {
m_digest[0] = 0x67452301;
m_digest[1] = 0xEFCDAB89;
m_digest[2] = 0x98BADCFE;
m_digest[3] = 0x10325476;
m_digest[4] = 0xC3D2E1F0;
m_blockByteIndex = 0;
m_byteCount = 0;
return *this;
}
SHA1& processByte(uint8_t octet) {
this->m_block[this->m_blockByteIndex++] = octet;
++this->m_byteCount;
if (m_blockByteIndex == 64) {
this->m_blockByteIndex = 0;
processBlock();
}
return *this;
}
SHA1& processBlock(const void* const start, const void* const end) {
const uint8_t* begin = static_cast<const uint8_t*>(start);
const uint8_t* finish = static_cast<const uint8_t*>(end);
while (begin != finish) {
processByte(*begin);
begin++;
}
return *this;
}
SHA1& processBytes(const void* const data, size_t len) {
const uint8_t* block = static_cast<const uint8_t*>(data);
processBlock(block, block + len);
return *this;
}
const uint32_t* finalize(digest32_t digest) {
size_t bitCount = this->m_byteCount * 8;
processByte(0x80);
if (this->m_blockByteIndex > 56) {
while (m_blockByteIndex != 0) {
processByte(0);
}
while (m_blockByteIndex < 56) {
processByte(0);
}
} else {
while (m_blockByteIndex < 56) {
processByte(0);
}
}
processByte(0);
processByte(0);
processByte(0);
processByte(0);
processByte(static_cast<unsigned char>((bitCount >> 24) & 0xFF));
processByte(static_cast<unsigned char>((bitCount >> 16) & 0xFF));
processByte(static_cast<unsigned char>((bitCount >> 8) & 0xFF));
processByte(static_cast<unsigned char>((bitCount)&0xFF));
memcpy(digest, m_digest, 5 * sizeof(uint32_t));
return digest;
}
const uint8_t* finalize(digest8_t digest) {
digest32_t d32;
finalize(d32);
size_t di = 0;
digest[di++] = ((d32[0] >> 24) & 0xFF);
digest[di++] = ((d32[0] >> 16) & 0xFF);
digest[di++] = ((d32[0] >> 8) & 0xFF);
digest[di++] = ((d32[0]) & 0xFF);
digest[di++] = ((d32[1] >> 24) & 0xFF);
digest[di++] = ((d32[1] >> 16) & 0xFF);
digest[di++] = ((d32[1] >> 8) & 0xFF);
digest[di++] = ((d32[1]) & 0xFF);
digest[di++] = ((d32[2] >> 24) & 0xFF);
digest[di++] = ((d32[2] >> 16) & 0xFF);
digest[di++] = ((d32[2] >> 8) & 0xFF);
digest[di++] = ((d32[2]) & 0xFF);
digest[di++] = ((d32[3] >> 24) & 0xFF);
digest[di++] = ((d32[3] >> 16) & 0xFF);
digest[di++] = ((d32[3] >> 8) & 0xFF);
digest[di++] = ((d32[3]) & 0xFF);
digest[di++] = ((d32[4] >> 24) & 0xFF);
digest[di++] = ((d32[4] >> 16) & 0xFF);
digest[di++] = ((d32[4] >> 8) & 0xFF);
digest[di++] = ((d32[4]) & 0xFF);
return digest;
}
protected:
void processBlock() {
uint32_t w[80];
for (size_t i = 0; i < 16; i++) {
w[i] = (m_block[i * 4 + 0] << 24);
w[i] |= (m_block[i * 4 + 1] << 16);
w[i] |= (m_block[i * 4 + 2] << 8);
w[i] |= (m_block[i * 4 + 3]);
}
for (size_t i = 16; i < 80; i++) {
w[i] = LeftRotate((w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]), 1);
}
uint32_t a = m_digest[0];
uint32_t b = m_digest[1];
uint32_t c = m_digest[2];
uint32_t d = m_digest[3];
uint32_t e = m_digest[4];
for (std::size_t i = 0; i < 80; ++i) {
uint32_t f = 0;
uint32_t k = 0;
if (i < 20) {
f = (b & c) | (~b & d);
k = 0x5A827999;
} else if (i < 40) {
f = b ^ c ^ d;
k = 0x6ED9EBA1;
} else if (i < 60) {
f = (b & c) | (b & d) | (c & d);
k = 0x8F1BBCDC;
} else {
f = b ^ c ^ d;
k = 0xCA62C1D6;
}
uint32_t temp = LeftRotate(a, 5) + f + e + k + w[i];
e = d;
d = c;
c = LeftRotate(b, 30);
b = a;
a = temp;
}
m_digest[0] += a;
m_digest[1] += b;
m_digest[2] += c;
m_digest[3] += d;
m_digest[4] += e;
}
private:
digest32_t m_digest;
uint8_t m_block[64];
size_t m_blockByteIndex;
size_t m_byteCount;
};
}
#endif
Vendored Submodule
+1
Submodule thirdparty/libmspack added at 305907723a
Vendored Submodule
+1
Submodule thirdparty/tiny-AES-c added at 23856752fb