Compare commits

...

92 Commits

Author SHA1 Message Date
Skyth 95c044299e Set version milestone to Beta 3. 2025-02-14 02:09:47 +03:00
Hyper b04d9e683c options_menu: fix info scroll marquee fade (#404) 2025-02-14 02:04:49 +03:00
Skyth (Asilkan) 5e8d15e334 Implement options menu TV static animation. (#403) 2025-02-14 01:41:18 +03:00
Hyper 8f8b574e56 Added debug view codes (#402)
Co-authored-by: RadiantDerg <9061202+RadiantDerg@users.noreply.github.com>
2025-02-13 19:55:08 +00:00
Hyper c8feff3f8d Moved Eggmanland loading transition fix to a code (#401) 2025-02-13 21:19:26 +03:00
Hyper 414ccb3637 Moved callbacks to config.cpp, fixed Subtitles option not applying to stages (#400) 2025-02-13 20:30:12 +03:00
Skyth (Asilkan) 431a6d9841 Fix shadow map values getting lost on resize. (#399) 2025-02-13 20:30:03 +03:00
Hyper 7a5cfe55f5 mod_loader: implemented file logging (#392) 2025-02-13 19:07:19 +03:00
Skyth (Asilkan) 4f3144472a Fix model explosion in Temple Entrance cutscene. (#395) 2025-02-13 18:25:42 +03:00
Skyth (Asilkan) e9f510e8c9 Comment out PSO_CACHING. (#394)
* Comment out PSO_CACHING.

* Comment out PSO_CACHING further.
2025-02-13 16:05:13 +03:00
Hyper a54f850425 Added more D-Pad hooks (#390) 2025-02-13 15:43:23 +03:00
Hyper ab382deb04 video: fix profiler not revealing mouse cursor in fullscreen (#391) 2025-02-13 15:43:03 +03:00
Hyper 8df3e0e440 Added hooks to disable voice hints (#389) 2025-02-13 15:42:48 +03:00
Skyth (Asilkan) fe083f0eed Fix loading arrow texture filtering. (#386) 2025-02-12 23:13:49 +03:00
Michael a8e78c21d1 imgui_utils: Make text shadows thicker for Original 4:3 (#385) 2025-02-12 22:35:44 +03:00
Skyth (Asilkan) 9ec6bd95e1 Account for control tutorial option in Dark Gaia boss. (#384) 2025-02-12 21:51:50 +03:00
Skyth (Asilkan) 771c2626b8 Fix the gauges in Dark Gaia boss. (#381)
* Apply offset scaling to Dark Gaia boss gauges.

* Hide boss gauge in Inspire cutscenes.
2025-02-12 21:19:40 +03:00
Hyper 54a63e857a Added empty locale entries for untranslated languages (#379) 2025-02-12 16:40:48 +00:00
Hyper a13fdc136b fps_patches: fix Egg Dragoon drill missile rotating incorrectly (#378)
Co-authored-by: Skyth (Asilkan) <19259897+blueskythlikesclouds@users.noreply.github.com>
2025-02-12 18:40:12 +03:00
Darío 86b346e037 Add maximum size to tabs in options menu. (#376)
* Add maximum size to tabs in options menu.

* Simplify squash ratio computation.

* Allow room for less padding if the text can't fit to the fixed width.

* Stretch every tab instead of individual ones.

---------

Co-authored-by: Skyth <19259897+blueskythlikesclouds@users.noreply.github.com>
2025-02-12 15:19:06 +03:00
Darío 3081aec7c1 Collect samples over one second for FPS counter and average. (#377) 2025-02-12 00:25:03 +00:00
Skyth (Asilkan) dd294a30d4 Fix achievement sound not playing in certain conditions. (#373)
* Fix achievement sound not playing in cutscenes.

* Prevent achievement overlay from showing if sounds cannot be played.
2025-02-11 23:50:24 +03:00
Hyper 99d6cebf92 Update Battle Theme description to mention "Exorcism missions" instead of "Rescue missions" (#372) 2025-02-11 18:18:59 +00:00
Hyper e86922b2b2 Fix Rooftop Run barrels rolling backwards at HFR (#368) 2025-02-11 15:15:43 +00:00
Hyper a07610fb7a Implemented FPS overlay (#361) 2025-02-11 15:15:28 +00:00
Skyth (Asilkan) db4974a9ca Fix text bars in result screen moving at ultrawide. (#367) 2025-02-11 17:25:25 +03:00
SuperSonic16 4b09a22041 Add Linux CI for Debugging (#366)
* Attempt 0

* Attempt 1

* Attempt 2

* Attempt 3

* Removed push event

* L
2025-02-11 17:04:15 +03:00
Skyth (Asilkan) 989bf85986 Fix arrows in item result title for ultrawide. (#365) 2025-02-11 17:03:35 +03:00
Darío 0194c372e9 Replace the default value when applying low end defaults so reset button works properly. (#359)
* Replace the default value when applying low end defaults so reset button works properly.

* Update video.cpp
2025-02-11 15:13:43 +03:00
Hyper 854d7412fa Fixed Werehog battle theme returning after defeating bosses when disabled (#360) 2025-02-11 14:22:18 +03:00
Darío 0441845f2a Add Auto Graphics API option, add AMD driver workaround. (#358)
* Add Auto Graphics API option, add AMD driver workaround.

* Remove unused line.
2025-02-11 14:22:03 +03:00
Darío f6d820faae GPU Profiling functionality. (#363)
* Added profiling timestamps to Vulkan. Added more profilers in general.

* Add timestamps to D3D12.

* Add update director to the profiler.

---------

Co-authored-by: Skyth <19259897+blueskythlikesclouds@users.noreply.github.com>
2025-02-11 14:21:43 +03:00
Hyper a7063dc118 Added code to use arrow transition for Time of Day Transition (#362) 2025-02-11 14:21:30 +03:00
Hyper fb55ac1087 Adjust credits logo position in installer wizard dynamically to fit text (#357) 2025-02-10 22:25:53 +00:00
Hyper e76cbff3ea achievement_menu: added minimum header size, improved separators
(and some extra junk I was researching)
2025-02-10 21:42:54 +00:00
Darío d2a3818700 Add support for preferred device to configuration. (#355)
* Preferred device support.

* Add GraphicsDevice option and fix error in Config class to accept strings.

Co-authored-by: Hyper <34012267+hyperbx@users.noreply.github.com>

---------

Co-authored-by: Hyper <34012267+hyperbx@users.noreply.github.com>
2025-02-11 00:01:12 +03:00
RadiantDerg 3285ad045f Thumbnails for options menu (#45)
* Rebase options-thumbnails branch

* Modify brightness locale

* Implement Antialiasing and Control Tutorial thumbnails

* Finish rebase

* Added vsync and channel thumbnails

* Modify time of day transition thumbnails

* Implement UI Scale Thumbnails

* Update UnleashedRecompResources

* Update DDS naming

* Update UnleashedRecompResources
2025-02-10 19:20:31 +03:00
Skyth (Asilkan) c687d87126 Apply offset scaling to Tornado Defense hit counter. (#354) 2025-02-10 18:02:04 +03:00
Skyth (Asilkan) 4502a9efee Fix boss name arrows for ultrawide. (#351) 2025-02-10 14:23:29 +03:00
Hyper fc99db7c97 Fixed village camera receiving input in options menu, replaced extra nav inputs in achievements menu with fast scroll (#344)
* options_menu: prohibit right stick input to prevent camera moving in villages

* achievement_menu: remove extra nav inputs, implemented fast scroll

* achievement_menu: replicate skills menu fast scrolling
2025-02-09 21:18:59 +00:00
Skyth (Asilkan) 0b8b243404 Disable primitive 2D pixel snapping & enforce linear only above 720p. (#346) 2025-02-09 23:42:30 +03:00
Hyper 8220f6772b CHudPause_patches: fix achievements prompt appearing over PS3 loading 2025-02-09 18:06:16 +00:00
Skyth (Asilkan) b7cee84bda Prevent calling GameWindow::Update() outside the main thread. (#340) 2025-02-09 20:04:13 +03:00
Hyper 073065c6a6 CTitleStateIntro_patches: delay quit input to allow update function to run once 2025-02-09 17:00:54 +00:00
Skyth (Asilkan) ed6f8f082b Replicate the thicker shadows for FCO text at 4:3 aspect ratio. (#341) 2025-02-09 18:43:57 +03:00
Skyth (Asilkan) f23d8ae08e Stretch shop fade in/out to the screen for ultrawide. (#337) 2025-02-09 15:21:08 +03:00
Skyth (Asilkan) f7e660b5d8 Fix NPC talk button cast modifier flags. (#336) 2025-02-09 15:13:52 +03:00
Skyth (Asilkan) 7c1aea4e24 Fix the custom UI drawing in front of ImGui windows. (#335) 2025-02-09 14:25:06 +03:00
Skyth (Asilkan) 138d9b616d Fix the Gaia Temple pedestal flashing black. (#334) 2025-02-09 13:16:03 +03:00
DeaTh-G 7f108285fe Fix clipping detail from the next not visible element on the options list (#321)
* decrease maximum clip rect size for options list

* respect window scaling

* move clip rect fix location
2025-02-09 00:33:49 +03:00
Skyth 97b20742a3 Make scroll bar in options menu more accurate. 2025-02-09 00:29:40 +03:00
Hyper fcd1673cfc Fix localisation not updating in real-time for button guides (#322) 2025-02-08 20:17:53 +00:00
Skyth (Asilkan) fff96a8528 Implement fix for Sparkle locus particle vertex corruption. (#317) 2025-02-08 20:45:14 +03:00
Hyper 2fdd32e889 locale: update localisation notes header 2025-02-08 17:40:36 +00:00
Hyper 54aacf9a1f Implemented codes list reading for mod loader (#315) 2025-02-08 17:12:53 +00:00
Hyper 39272b7975 config_locale: update Channel Configuration description 2025-02-08 13:46:37 +00:00
Hyper d696524dbf Rename UI Scale Mode to UI Alignment Mode 2025-02-08 12:09:15 +00:00
Skyth (Asilkan) e3ac47a797 Fix werehog HUD guide not having correct position at 4:3. (#311) 2025-02-08 14:16:33 +03:00
Hyper 72640c8749 options_menu: reset auto-scroll timer after manual scroll and retain last stick direction 2025-02-08 10:08:08 +00:00
Hyper 1687c65be9 Update resources submodule 2025-02-08 10:01:54 +00:00
Hyper 6dac017d00 Fix mission UI not extending for centred scale mode (#309) 2025-02-07 23:12:58 +00:00
Hyper 2150faed2e game_window: fix mouse cursor flickering after controller input 2025-02-07 20:42:02 +00:00
Hyper 2f1b98c570 game_window: revert window resize fix
This partially reverts commit 70ebdaa685.
2025-02-07 20:31:41 +00:00
Hyper 70ebdaa685 game_window: don't allow resizing until after boot, fix monitor switching 2025-02-07 20:28:57 +00:00
Hyper e1edd5f35d Fix Tornado Defense boss firing missiles too frequently (#306)
Co-authored-by: Skyth (Asilkan) <19259897+blueskythlikesclouds@users.noreply.github.com>
Co-authored-by: Darío <538504+DarioSamo@users.noreply.github.com>
2025-02-07 20:11:25 +00:00
Hyper 88df0e08b7 config_locale: update localisation
Co-Authored-By: Michael <15317421+ActualMandM@users.noreply.github.com>
2025-02-07 13:41:13 +00:00
Hyper 511c670def options_menu: fix inconsistent info scrolling delay 2025-02-07 10:59:44 +00:00
Hyper 16c35b45c2 options_menu: fix info scrolling not resetting if text is too small 2025-02-07 10:46:34 +00:00
Darío b291bdba91 Cherry-pick text changes from thumbnails branch. (#301)
* Cherry-pick text changes from thumbnails branch.

Co-authored-by: RadiantDerg <9061202+RadiantDerg@users.noreply.github.com>

* config_locale: update Aspect Ratio locale

Co-Authored-By: Michael <15317421+ActualMandM@users.noreply.github.com>

---------

Co-authored-by: RadiantDerg <9061202+RadiantDerg@users.noreply.github.com>
Co-authored-by: Hyper <34012267+hyperbx@users.noreply.github.com>
Co-authored-by: Michael <15317421+ActualMandM@users.noreply.github.com>
2025-02-07 10:45:01 +00:00
Hyper 727be2b47c Update resources submodule 2025-02-07 10:35:11 +00:00
Hyper 62ad3a1a13 achievement_manager: added UnlockAll method
Because I'm tired of writing this every time I need to debug unlocking achievements.
2025-02-07 10:30:03 +00:00
Darío 6c8dbdb6bc Check for closing during AchievementOverlay::Open (#302) 2025-02-07 10:23:10 +00:00
Hyper 71bb081645 game_window: respect per-monitor DPI scale
Co-Authored-By: Darío <538504+DarioSamo@users.noreply.github.com>
2025-02-07 10:11:06 +00:00
Hyper 871515b3be options_menu: start monitor index from 1 2025-02-07 09:58:39 +00:00
Darío 266d436c28 Block exit button from terminating the installer while it's in progress. (#300)
* Block exit button from terminating the installer while it's in progress.

* Fix quit prompt not appearing when closing the game from the taskbar.

---------

Co-authored-by: Skyth <19259897+blueskythlikesclouds@users.noreply.github.com>
2025-02-07 01:21:18 +03:00
Darío e7cc5a858e Change progress bar animation on installer. (#299) 2025-02-07 00:38:18 +03:00
Skyth (Asilkan) 9549ba54aa Implement black bar drawing for loading and cutscenes. (#298) 2025-02-07 00:30:08 +03:00
Hyper 47b1f20679 message_window: added selection changed anim to options 2025-02-06 17:19:03 +00:00
Darío ef51f04d4f Add back button functionality to the installer. (#279)
* Add back button functionality to the installer.

* Nuclear exits.

* Adjust error code.

* Rework waiting time into the installer process instead.

* Extra waiting time during quitting.

* Restore button max widths.

* button_guide: set up Esc key texture

* Update installer_wizard.cpp

* Update resources submodule

* installer_wizard: decrease nav button margin

---------

Co-authored-by: Hyper <34012267+hyperbx@users.noreply.github.com>
2025-02-05 22:36:30 +03:00
Hyper b68dbec612 Update resources submodule 2025-02-05 17:25:41 +00:00
Skyth (Asilkan) 96108e1759 Use a separate upload buffer allocator for the main thread. (#281) 2025-02-05 17:02:27 +03:00
Hyper d3589979e4 imgui_utils: update selection textures 2025-02-05 11:45:49 +00:00
Darío 2db4a9c78c Expand on the text for the installer tutorials (#272)
* Expand on the text for the installer tutorials.

* Make both descriptions share the same paragraph.

* Update locale.cpp

Co-Authored-By: Goalringmod27 <88973067+Goalringmod27@users.noreply.github.com>

---------

Co-authored-by: Hyper <34012267+hyperbx@users.noreply.github.com>
Co-authored-by: Goalringmod27 <88973067+Goalringmod27@users.noreply.github.com>
2025-02-05 11:13:22 +00:00
Skyth 2d56566924 Remove the yield from the render thread. 2025-02-05 12:59:42 +03:00
Hyper 553e011dad CTitleStateMenu_patches: reset achievements on New Game (#278) 2025-02-05 12:19:18 +03:00
Darío 900ba7c916 Simplify required space text. (#280) 2025-02-05 02:47:31 +03:00
Hyper 66648d550a Implemented vertical scrolling for descriptions (#271)
* options_menu: implemented vertical scrolling for descriptions

* use correct size calculation

* remove empty lines from descriptions without value desc, move fix

* remove calculating the space for the next annotation after the last line

---------

Co-authored-by: DeaTh-G <hatvongeorge@gmail.com>
Co-authored-by: DeaTh-G <55578911+DeaTh-G@users.noreply.github.com>
2025-02-04 17:30:42 +00:00
Hyper c40ffbc70d Implemented fast scrolling on up/down hold for options menu (#275) 2025-02-04 15:29:22 +00:00
Skyth (Asilkan) ee97736d58 Make custom UI a bit more accurate looking. (#274)
* Update spacing for default font & pixel snap 2D primitives.

* Replicate the missing info outline from world map.
2025-02-04 14:53:43 +03:00
Skyth bbb3ebc25d Remove Win32 implementation of SetThreadIdealProcessor. 2025-02-04 12:41:19 +03:00
Hyper 52558a674e installer_wizard: fix source button text and alpha 2025-02-03 22:05:41 +00:00
Skyth (Asilkan) 11d0fd2f9c Implement rasterization path for framebuffer copy operations. (#270)
* Move all copies & resolves to rasterization.

* Fix Vulkan crash.

* Implement hardware resolve path for both color and depth targets.
2025-02-03 23:02:47 +03:00
99 changed files with 4737 additions and 1024 deletions
+73
View File
@@ -0,0 +1,73 @@
name: Build Project (Linux)
on:
workflow_dispatch:
env:
LLVM_VERSION: 18
CMAKE_PRESET: linux-relwithdebinfo
jobs:
build:
name: Build Linux
runs-on: ubuntu-24.04
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
submodules: recursive
token: ${{ secrets.ORG_TOKEN }}
- name: Checkout Private Repository
uses: actions/checkout@v4
with:
repository: ${{ secrets.ASSET_REPO }}
token: ${{ secrets.ASSET_REPO_TOKEN }}
path: ./private
- name: Setup ccache
uses: hendrikmuhs/ccache-action@v1.2
with:
key: ccache-${{ runner.os }}
- name: Cache vcpkg
uses: actions/cache@v4
with:
path: |
./thirdparty/vcpkg/downloads
./thirdparty/vcpkg/packages
key: vcpkg-${{ runner.os }}-${{ hashFiles('vcpkg.json') }}
restore-keys: |
vcpkg-${{ runner.os }}-
- name: Install Dependencies
run: |-
sudo apt update
sudo apt install -y ninja-build llvm-${{ env.LLVM_VERSION }}-dev libgtk-3-dev
- name: Cache ccache Directory
uses: actions/cache@v4
with:
path: /tmp/ccache
key: ccache-${{ runner.os }}
- name: Prepare Project
run: cp ./private/* ./UnleashedRecompLib/private
- name: Configure Project
env:
CCACHE_DIR: /tmp/ccache
run: cmake . --preset ${{ env.CMAKE_PRESET }} -DSDL2MIXER_VORBIS=VORBISFILE -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache
- name: Build Project
env:
CCACHE_DIR: /tmp/ccache
run: cmake --build ./out/build/${{ env.CMAKE_PRESET }} --target UnleashedRecomp
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: UnleashedRecomp-Linux
path: ./out/build/${{ env.CMAKE_PRESET }}/UnleashedRecomp/UnleashedRecomp
+39 -11
View File
@@ -150,6 +150,7 @@ set(UNLEASHED_RECOMP_PATCHES_CXX_SOURCES
set(UNLEASHED_RECOMP_UI_CXX_SOURCES
"ui/achievement_menu.cpp"
"ui/achievement_overlay.cpp"
"ui/black_bar.cpp"
"ui/button_guide.cpp"
"ui/fader.cpp"
"ui/game_window.cpp"
@@ -158,6 +159,7 @@ set(UNLEASHED_RECOMP_UI_CXX_SOURCES
"ui/message_window.cpp"
"ui/options_menu.cpp"
"ui/options_menu_thumbnails.cpp"
"ui/tv_static.cpp"
)
set(UNLEASHED_RECOMP_INSTALL_CXX_SOURCES
@@ -351,9 +353,11 @@ file(CHMOD ${DIRECTX_DXC_TOOL} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE)
if (WIN32)
target_link_libraries(UnleashedRecomp PRIVATE
comctl32
dwmapi
ntdll
winmm
Shcore
Synchronization
winmm
)
endif()
@@ -415,6 +419,8 @@ endfunction()
compile_pixel_shader(blend_color_alpha_ps)
compile_vertex_shader(copy_vs)
compile_pixel_shader(copy_color_ps)
compile_pixel_shader(copy_depth_ps)
compile_pixel_shader(csd_filter_ps)
compile_vertex_shader(csd_no_tex_vs)
compile_vertex_shader(csd_vs)
@@ -428,6 +434,9 @@ compile_pixel_shader(imgui_ps)
compile_vertex_shader(imgui_vs)
compile_pixel_shader(movie_ps)
compile_vertex_shader(movie_vs)
compile_pixel_shader(resolve_msaa_color_2x)
compile_pixel_shader(resolve_msaa_color_4x)
compile_pixel_shader(resolve_msaa_color_8x)
compile_pixel_shader(resolve_msaa_depth_2x)
compile_pixel_shader(resolve_msaa_depth_4x)
compile_pixel_shader(resolve_msaa_depth_8x)
@@ -464,6 +473,7 @@ generate_aggregate_header(
set(RESOURCES_SOURCE_PATH "${PROJECT_SOURCE_DIR}/../UnleashedRecompResources")
set(RESOURCES_OUTPUT_PATH "${PROJECT_SOURCE_DIR}/res")
## Miscellaneous ##
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/bc_diff/button_bc_diff.bin" DEST_FILE "${RESOURCES_OUTPUT_PATH}/bc_diff/button_bc_diff.bin" ARRAY_NAME "g_button_bc_diff" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/font/im_font_atlas.bin" DEST_FILE "${RESOURCES_OUTPUT_PATH}/font/im_font_atlas.bin" ARRAY_NAME "g_im_font_atlas" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/font/im_font_atlas.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/font/im_font_atlas.dds" ARRAY_NAME "g_im_font_atlas_texture" COMPRESSION_TYPE "zstd")
@@ -472,9 +482,10 @@ BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/co
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/general_window.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/general_window.dds" ARRAY_NAME "g_general_window" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/hedge-dev.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/hedge-dev.dds" ARRAY_NAME "g_hedgedev" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/kbm.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/kbm.dds" ARRAY_NAME "g_kbm" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/select_fade.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/select_fade.dds" ARRAY_NAME "g_select_fade" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/select_fill.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/select_fill.dds" ARRAY_NAME "g_select_fill" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/select.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/select.dds" ARRAY_NAME "g_select" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/light.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/light.dds" ARRAY_NAME "g_light" COMPRESSION_TYPE "zstd")
## Installer ##
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/arrow_circle.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/arrow_circle.dds" ARRAY_NAME "g_arrow_circle" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/install_001.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/install_001.dds" ARRAY_NAME "g_install_001" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/install_002.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/install_002.dds" ARRAY_NAME "g_install_002" COMPRESSION_TYPE "zstd")
@@ -486,13 +497,22 @@ BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/in
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/install_008.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/install_008.dds" ARRAY_NAME "g_install_008" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/miles_electric_icon.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/miles_electric_icon.dds" ARRAY_NAME "g_miles_electric_icon" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/pulse_install.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/pulse_install.dds" ARRAY_NAME "g_pulse_install" COMPRESSION_TYPE "zstd")
## 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/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/antialiasing.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/antialiasing.dds" ARRAY_NAME "g_antialiasing" 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_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_8x.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/antialiasing_8x.dds" ARRAY_NAME "g_antialiasing_8x" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/aspect_ratio.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/aspect_ratio.dds" ARRAY_NAME "g_aspect_ratio" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/battle_theme.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/battle_theme.dds" ARRAY_NAME "g_battle_theme" 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/control_tutorial.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/control_tutorial.dds" ARRAY_NAME "g_control_tutorial" 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/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/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")
@@ -508,7 +528,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/motion_blur_off.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/motion_blur_off.dds" ARRAY_NAME "g_motion_blur_off" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/motion_blur_original.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/motion_blur_original.dds" ARRAY_NAME "g_motion_blur_original" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/motion_blur_enhanced.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/motion_blur_enhanced.dds" ARRAY_NAME "g_motion_blur_enhanced" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/movie_scale_mode.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/movie_scale_mode.dds" ARRAY_NAME "g_movie_scale_mode" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/movie_scale_fit.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/movie_scale_fit.dds" ARRAY_NAME "g_movie_scale_fit" 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_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")
@@ -519,20 +540,28 @@ BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/op
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/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_of_day_transition_xbox.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/time_of_day_transition_xbox.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_of_day_transition_playstation.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/time_of_day_transition_playstation.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/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_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_scale_mode.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/ui_scale_mode.dds" ARRAY_NAME "g_ui_scale_mode" 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/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/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/vsync.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/vsync.dds" ARRAY_NAME "g_vsync" 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/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/xbox_color_correction.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/xbox_color_correction.dds" ARRAY_NAME "g_xbox_color_correction" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/miles_electric.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/miles_electric.dds" ARRAY_NAME "g_miles_electric" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/options_static.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/options_static.dds" ARRAY_NAME "g_options_static" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/options_static_flash.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/options_static_flash.dds" ARRAY_NAME "g_options_static_flash" COMPRESSION_TYPE "zstd")
## Game Icon ##
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/game_icon.bmp" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/game_icon.bmp" ARRAY_NAME "g_game_icon")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/game_icon_night.bmp" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/game_icon_night.bmp" ARRAY_NAME "g_game_icon_night")
## Audio ##
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/sounds/sys_worldmap_cursor.ogg" DEST_FILE "${RESOURCES_OUTPUT_PATH}/sounds/sys_worldmap_cursor.ogg" ARRAY_NAME "g_sys_worldmap_cursor")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/sounds/sys_worldmap_finaldecide.ogg" DEST_FILE "${RESOURCES_OUTPUT_PATH}/sounds/sys_worldmap_finaldecide.ogg" ARRAY_NAME "g_sys_worldmap_finaldecide")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/sounds/sys_actstg_pausecansel.ogg" DEST_FILE "${RESOURCES_OUTPUT_PATH}/sounds/sys_actstg_pausecansel.ogg" ARRAY_NAME "g_sys_actstg_pausecansel")
@@ -540,4 +569,3 @@ BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/sounds/sy
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/sounds/sys_actstg_pausedecide.ogg" DEST_FILE "${RESOURCES_OUTPUT_PATH}/sounds/sys_actstg_pausedecide.ogg" ARRAY_NAME "g_sys_actstg_pausedecide")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/sounds/sys_actstg_pausewinclose.ogg" DEST_FILE "${RESOURCES_OUTPUT_PATH}/sounds/sys_actstg_pausewinclose.ogg" ARRAY_NAME "g_sys_actstg_pausewinclose")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/sounds/sys_actstg_pausewinopen.ogg" DEST_FILE "${RESOURCES_OUTPUT_PATH}/sounds/sys_actstg_pausewinopen.ogg" ARRAY_NAME "g_sys_actstg_pausewinopen")
@@ -0,0 +1,27 @@
#pragma once
#include <SWA.inl>
namespace Hedgehog::Math
{
class CMatrix
{
public:
be<float> m_M00;
be<float> m_M01;
be<float> m_M02;
be<float> m_M03;
be<float> m_M10;
be<float> m_M11;
be<float> m_M12;
be<float> m_M13;
be<float> m_M20;
be<float> m_M21;
be<float> m_M22;
be<float> m_M23;
be<float> m_M30;
be<float> m_M31;
be<float> m_M32;
be<float> m_M33;
};
}
@@ -0,0 +1,15 @@
#pragma once
#include <SWA.inl>
namespace Hedgehog::Math
{
class CQuaternion
{
public:
be<float> X;
be<float> Y;
be<float> Z;
be<float> W;
};
}
@@ -0,0 +1,16 @@
#pragma once
#include <SWA.inl>
namespace Hedgehog::Mirage
{
class CMatrixNodeListener;
class CMatrixNode : public Base::CObject
{
public:
SWA_INSERT_PADDING(0x60);
};
SWA_ASSERT_SIZEOF(CMatrixNode, 0x60);
}
@@ -0,0 +1,19 @@
#pragma once
#include <SWA.inl>
namespace Hedgehog::Mirage
{
class CTransform
{
public:
Math::CQuaternion m_Rotation;
Math::CVector m_Position;
Math::CMatrix m_Matrix;
};
SWA_ASSERT_OFFSETOF(CTransform, m_Rotation, 0x00);
SWA_ASSERT_OFFSETOF(CTransform, m_Position, 0x10);
SWA_ASSERT_OFFSETOF(CTransform, m_Matrix, 0x20);
SWA_ASSERT_SIZEOF(CTransform, 0x60);
}
+13
View File
@@ -27,7 +27,11 @@
#include "Hedgehog/Base/Type/hhSharedString.h"
#include "Hedgehog/Base/hhObject.h"
#include "Hedgehog/Database/System/hhDatabaseData.h"
#include "Hedgehog/Math/Matrix.h"
#include "Hedgehog/Math/Quaternion.h"
#include "Hedgehog/Math/Vector.h"
#include "Hedgehog/MirageCore/MatrixNode/hhMatrixNode.h"
#include "Hedgehog/MirageCore/Misc/hhTransform.h"
#include "Hedgehog/MirageCore/Misc/hhVertexDeclarationPtr.h"
#include "Hedgehog/MirageCore/RenderData/hhMaterialData.h"
#include "Hedgehog/MirageCore/RenderData/hhMeshData.h"
@@ -55,6 +59,7 @@
#include "SWA/Achievement/AchievementID.h"
#include "SWA/Achievement/AchievementManager.h"
#include "SWA/Achievement/AchievementTest.h"
#include "SWA/Boss/EggDragoon/Object/BossEggDragoonDrillMissile.h"
#include "SWA/CSD/CsdDatabaseWrapper.h"
#include "SWA/CSD/CsdProject.h"
#include "SWA/CSD/CsdTexListMirage.h"
@@ -62,6 +67,10 @@
#include "SWA/Camera/Camera.h"
#include "SWA/Camera/CameraController.h"
#include "SWA/CharacterUtility/CharacterProxy.h"
#include "SWA/ExtraStage/Tails/Enemy/Boss/ExStageBoss.h"
#include "SWA/ExtraStage/Tails/Enemy/Boss/State/StateBase.h"
#include "SWA/ExtraStage/Tails/Enemy/Boss/State/StateBattle.h"
#include "SWA/ExtraStage/Tails/Player/ExPlayerTails.h"
#include "SWA/Globals.h"
#include "SWA/HUD/GeneralWindow/GeneralWindow.h"
#include "SWA/HUD/Loading/Loading.h"
@@ -80,6 +89,8 @@
#include "SWA/Menu/MenuWindowBase.h"
#include "SWA/Movie/MovieDisplayer.h"
#include "SWA/Movie/MovieManager.h"
#include "SWA/Object/Common/DashPanel/ObjDashPanel.h"
#include "SWA/Object/SonicStage/EU/RollingBarrel/ObjRollingBarrel.h"
#include "SWA/Player/Character/EvilSonic/EvilSonic.h"
#include "SWA/Player/Character/EvilSonic/EvilSonicContext.h"
#include "SWA/Player/Character/EvilSonic/Hud/EvilHudGuide.h"
@@ -102,6 +113,7 @@
#include "SWA/System/GameMode/Title/TitleMenu.h"
#include "SWA/System/GameMode/Title/TitleStateBase.h"
#include "SWA/System/GameMode/Title/TitleStateIntro.h"
#include "SWA/System/GameMode/Title/TitleStateMenu.h"
#include "SWA/System/GameMode/Title/TitleStateWorldMap.h"
#include "SWA/System/GameMode/WorldMap/WorldMapCamera.h"
#include "SWA/System/GameMode/WorldMap/WorldMapCursor.h"
@@ -109,6 +121,7 @@
#include "SWA/System/GameParameter.h"
#include "SWA/System/GammaController.h"
#include "SWA/System/InputState.h"
#include "SWA/System/MatrixNodeTransform.h"
#include "SWA/System/PadState.h"
#include "SWA/System/World.h"
#include "boost/smart_ptr/make_shared_object.h"
@@ -0,0 +1,16 @@
#pragma once
#include <SWA.inl>
#include "SWA/System/MatrixNodeTransform.h"
namespace SWA::Boss::EggDragoon::DrillMissile
{
class CMissile
{
public:
SWA_INSERT_PADDING(0xC4);
xpointer<CMatrixNodeTransform> m_pMatrixNodeTransform;
SWA_INSERT_PADDING(0x68);
be<float> m_Speed;
};
}
@@ -0,0 +1,36 @@
#pragma once
#include <SWA.inl>
namespace SWA
{
class CExStageBoss
{
public:
class CStateBase;
class CStateBattle;
class CExStageBossStateContext
{
public:
SWA_INSERT_PADDING(0x14C);
be<float> m_SplineProgress;
SWA_INSERT_PADDING(0x0C);
be<float> m_SplineSpeed;
SWA_INSERT_PADDING(0x28);
be<float> m_Field188;
be<float> m_Field18C;
SWA_INSERT_PADDING(0x21);
bool m_IsBattleStart;
SWA_INSERT_PADDING(0x36E);
be<float> m_Field520;
};
};
SWA_ASSERT_OFFSETOF(CExStageBoss::CExStageBossStateContext, m_SplineProgress, 0x14C);
SWA_ASSERT_OFFSETOF(CExStageBoss::CExStageBossStateContext, m_SplineSpeed, 0x15C);
SWA_ASSERT_OFFSETOF(CExStageBoss::CExStageBossStateContext, m_Field188, 0x188);
SWA_ASSERT_OFFSETOF(CExStageBoss::CExStageBossStateContext, m_Field18C, 0x18C);
SWA_ASSERT_OFFSETOF(CExStageBoss::CExStageBossStateContext, m_IsBattleStart, 0x1B1);
SWA_ASSERT_OFFSETOF(CExStageBoss::CExStageBossStateContext, m_Field520, 0x520);
}
@@ -0,0 +1,9 @@
#pragma once
#include <SWA.inl>
#include "SWA/ExtraStage/Tails/Enemy/Boss/ExStageBoss.h"
namespace SWA
{
class CExStageBoss::CStateBase : public Hedgehog::Universe::CStateMachineBase::CStateBase {};
}
@@ -0,0 +1,18 @@
#pragma once
#include <SWA.inl>
#include "SWA/ExtraStage/Tails/Enemy/Boss/ExStageBoss.h"
namespace SWA
{
class CExStageBoss::CStateBattle : public CExStageBoss::CStateBase
{
public:
SWA_INSERT_PADDING(0x08);
be<float> m_Field68;
be<float> m_FramesSinceLastMissile;
};
SWA_ASSERT_OFFSETOF(CExStageBoss::CStateBattle, m_Field68, 0x68);
SWA_ASSERT_OFFSETOF(CExStageBoss::CStateBattle, m_FramesSinceLastMissile, 0x6C);
}
@@ -0,0 +1,35 @@
#pragma once
#include <SWA.inl>
namespace SWA
{
class CExPlayerTails
{
public:
class CExPlayerTailsStateContext
{
public:
SWA_INSERT_PADDING(0x1F8);
be<float> m_SplineBossStart;
be<float> m_SplineEnd;
SWA_INSERT_PADDING(0x30);
be<float> m_SplineProgress;
SWA_INSERT_PADDING(0x10);
xpointer<void> m_Field244;
SWA_INSERT_PADDING(0x18);
be<float> m_SplineSpeed;
SWA_INSERT_PADDING(0x0C);
be<uint32_t> m_State; // 0 - Intro; 1 - Boss Intro; 3 - Boss
};
class CStateBase : public Hedgehog::Universe::CStateMachineBase::CStateBase {};
};
SWA_ASSERT_OFFSETOF(CExPlayerTails::CExPlayerTailsStateContext, m_SplineBossStart, 0x1F8);
SWA_ASSERT_OFFSETOF(CExPlayerTails::CExPlayerTailsStateContext, m_SplineEnd, 0x1FC);
SWA_ASSERT_OFFSETOF(CExPlayerTails::CExPlayerTailsStateContext, m_SplineProgress, 0x230);
SWA_ASSERT_OFFSETOF(CExPlayerTails::CExPlayerTailsStateContext, m_Field244, 0x244);
SWA_ASSERT_OFFSETOF(CExPlayerTails::CExPlayerTailsStateContext, m_SplineSpeed, 0x260);
SWA_ASSERT_OFFSETOF(CExPlayerTails::CExPlayerTailsStateContext, m_State, 0x270);
}
+8
View File
@@ -9,6 +9,9 @@ namespace SWA
// ms_DrawLightFieldSamplingPoint: サンプリング点をデバッグ表示
static inline bool* ms_DrawLightFieldSamplingPoint;
// N/A
static inline bool* ms_IsAutoSaveWarningShown;
// ms_IgnoreLightFieldData: データを無視する
static inline bool* ms_IgnoreLightFieldData;
@@ -39,6 +42,9 @@ namespace SWA
// ms_IsRenderHudPause: ポーズメニュー 描画
static inline bool* ms_IsRenderHudPause;
// N/A
static inline bool* ms_IsTitleStateMenu;
// IsTriggerRender
static inline bool* ms_IsTriggerRender;
@@ -52,6 +58,7 @@ namespace SWA
{
ms_DrawLightFieldSamplingPoint = (bool*)MmGetHostAddress(0x83367BCE);
ms_IgnoreLightFieldData = (bool*)MmGetHostAddress(0x83367BCF);
ms_IsAutoSaveWarningShown = (bool*)MmGetHostAddress(0x83367BC1);
ms_IsCollisionRender = (bool*)MmGetHostAddress(0x833678A6);
ms_IsLoading = (bool*)MmGetHostAddress(0x83367A4C);
ms_IsObjectCollisionRender = (bool*)MmGetHostAddress(0x83367905);
@@ -61,6 +68,7 @@ namespace SWA
ms_IsRenderGameMainHud = (bool*)MmGetHostAddress(0x8328BB27);
ms_IsRenderHud = (bool*)MmGetHostAddress(0x8328BB26);
ms_IsRenderHudPause = (bool*)MmGetHostAddress(0x8328BB28);
ms_IsTitleStateMenu = (bool*)MmGetHostAddress(0x83367BC0);
ms_IsTriggerRender = (bool*)MmGetHostAddress(0x83367904);
ms_LightFieldDebug = (bool*)MmGetHostAddress(0x83367BCD);
ms_VisualizeLoadedLevel = (bool*)MmGetHostAddress(0x833678C1);
@@ -4,6 +4,15 @@
namespace SWA
{
enum EWindowStatus : uint32_t
{
eWindowStatus_Closed,
eWindowStatus_OpeningMessage = 2,
eWindowStatus_DisplayingMessage,
eWindowStatus_OpeningControls,
eWindowStatus_DisplayingControls
};
class CGeneralWindow
{
public:
@@ -14,5 +23,20 @@ namespace SWA
Chao::CSD::RCPtr<Chao::CSD::CScene> m_rcWindow_2;
Chao::CSD::RCPtr<Chao::CSD::CScene> m_rcWindowSelect;
Chao::CSD::RCPtr<Chao::CSD::CScene> m_rcFooter;
SWA_INSERT_PADDING(0x58);
be<EWindowStatus> m_Status;
be<uint32_t> m_CursorIndex;
SWA_INSERT_PADDING(0x04);
be<uint32_t> m_SelectedIndex;
};
SWA_ASSERT_OFFSETOF(CGeneralWindow, m_rcGeneral, 0xD0);
SWA_ASSERT_OFFSETOF(CGeneralWindow, m_rcBg, 0xD8);
SWA_ASSERT_OFFSETOF(CGeneralWindow, m_rcWindow, 0xE0);
SWA_ASSERT_OFFSETOF(CGeneralWindow, m_rcWindow_2, 0xE8);
SWA_ASSERT_OFFSETOF(CGeneralWindow, m_rcWindowSelect, 0xF0);
SWA_ASSERT_OFFSETOF(CGeneralWindow, m_rcFooter, 0xF8);
SWA_ASSERT_OFFSETOF(CGeneralWindow, m_Status, 0x158);
SWA_ASSERT_OFFSETOF(CGeneralWindow, m_CursorIndex, 0x15C);
SWA_ASSERT_OFFSETOF(CGeneralWindow, m_SelectedIndex, 0x164);
}
@@ -0,0 +1,14 @@
#pragma once
#include <SWA.inl>
namespace SWA
{
class CObjDashPanel
{
public:
SWA_INSERT_PADDING(0xE8);
be<float> m_FieldE8;
be<float> m_Speed;
};
}
@@ -0,0 +1,35 @@
#pragma once
#include <SWA.inl>
namespace SWA
{
class CObjRollingBarrel
{
public:
class CElement
{
public:
SWA_INSERT_PADDING(0xEC);
xpointer<void> m_FieldEC;
SWA_INSERT_PADDING(0x20);
be<float> m_Roll;
};
SWA_INSERT_PADDING(0xE8);
be<float> m_GenerationTime;
SWA_INSERT_PADDING(0x09);
bool m_FieldF5;
SWA_INSERT_PADDING(0x0A);
be<float> m_RandomRange;
be<float> m_Field104;
};
SWA_ASSERT_OFFSETOF(CObjRollingBarrel::CElement, m_FieldEC, 0xEC);
SWA_ASSERT_OFFSETOF(CObjRollingBarrel::CElement, m_Roll, 0x110);
SWA_ASSERT_OFFSETOF(CObjRollingBarrel, m_GenerationTime, 0xE8);
SWA_ASSERT_OFFSETOF(CObjRollingBarrel, m_FieldF5, 0xF5);
SWA_ASSERT_OFFSETOF(CObjRollingBarrel, m_RandomRange, 0x100);
SWA_ASSERT_OFFSETOF(CObjRollingBarrel, m_Field104, 0x104);
}
@@ -71,7 +71,8 @@ namespace SWA
boost::shared_ptr<Hedgehog::Mirage::CMatrixNode> m_spMatrixNodeRoot;
SWA_INSERT_PADDING(0x14);
CGammaController m_GammaController;
SWA_INSERT_PADDING(0x1C);
boost::shared_ptr<CLoading> m_spLoading;
SWA_INSERT_PADDING(0x14);
boost::shared_ptr<Achievement::CManager> m_spAchievementManager;
boost::shared_ptr<CDatabaseTree> m_spDatabaseTree;
Hedgehog::Base::CSharedString m_Field10C;
@@ -104,6 +105,7 @@ namespace SWA
SWA_ASSERT_OFFSETOF(CApplicationDocument::CMember, m_Field74, 0x74);
SWA_ASSERT_OFFSETOF(CApplicationDocument::CMember, m_spMatrixNodeRoot, 0x84);
SWA_ASSERT_OFFSETOF(CApplicationDocument::CMember, m_GammaController, 0xA0);
SWA_ASSERT_OFFSETOF(CApplicationDocument::CMember, m_spLoading, 0xE0);
SWA_ASSERT_OFFSETOF(CApplicationDocument::CMember, m_spAchievementManager, 0xFC);
SWA_ASSERT_OFFSETOF(CApplicationDocument::CMember, m_spDatabaseTree, 0x104);
SWA_ASSERT_OFFSETOF(CApplicationDocument::CMember, m_Field10C, 0x10C);
@@ -34,7 +34,9 @@ namespace SWA
SWA_INSERT_PADDING(0x88);
Hedgehog::Base::CSharedString m_StageName;
xpointer<CSoundAdministrator> m_pSoundAdministrator;
SWA_INSERT_PADDING(0x124);
SWA_INSERT_PADDING(0x48);
xpointer<CGeneralWindow> m_pGeneralWindow;
SWA_INSERT_PADDING(0xD8);
SScoreInfo m_ScoreInfo;
SWA_INSERT_PADDING(0x0C);
};
@@ -60,6 +62,7 @@ namespace SWA
SWA_ASSERT_OFFSETOF(CGameDocument::CMember, m_spDatabase, 0x1C);
SWA_ASSERT_OFFSETOF(CGameDocument::CMember, m_StageName, 0xAC);
SWA_ASSERT_OFFSETOF(CGameDocument::CMember, m_pSoundAdministrator, 0xB0);
SWA_ASSERT_OFFSETOF(CGameDocument::CMember, m_pGeneralWindow, 0xFC);
SWA_ASSERT_OFFSETOF(CGameDocument::CMember, m_ScoreInfo, 0x1D8);
SWA_ASSERT_SIZEOF(CGameDocument::CMember, 0x230);
@@ -4,10 +4,36 @@
namespace SWA
{
class CTitleMenu
class CTitleMenu : public CMenuWindowBase
{
public:
SWA_INSERT_PADDING(0x44);
SWA_INSERT_PADDING(0x28);
be<uint32_t> m_Field38;
bool m_Field3C; // Seems to be related to exit transition.
SWA_INSERT_PADDING(0x04);
be<uint32_t> m_CursorIndex;
SWA_INSERT_PADDING(0x0C);
bool m_Field54; // Seems to be related to exit transition.
SWA_INSERT_PADDING(0x0B);
be<float> m_Field60;
SWA_INSERT_PADDING(0x34);
bool m_Field98;
bool m_IsDeleteCheckMessageOpen;
bool m_Field9A; // Seems to be related to cursor selection.
SWA_INSERT_PADDING(0x04);
bool m_Field9F;
SWA_INSERT_PADDING(0x02);
bool m_IsDLCInfoMessageOpen;
};
SWA_ASSERT_OFFSETOF(CTitleMenu, m_Field38, 0x38);
SWA_ASSERT_OFFSETOF(CTitleMenu, m_Field3C, 0x3C);
SWA_ASSERT_OFFSETOF(CTitleMenu, m_CursorIndex, 0x44);
SWA_ASSERT_OFFSETOF(CTitleMenu, m_Field54, 0x54);
SWA_ASSERT_OFFSETOF(CTitleMenu, m_Field60, 0x60);
SWA_ASSERT_OFFSETOF(CTitleMenu, m_Field98, 0x98);
SWA_ASSERT_OFFSETOF(CTitleMenu, m_IsDeleteCheckMessageOpen, 0x99);
SWA_ASSERT_OFFSETOF(CTitleMenu, m_Field9A, 0x9A);
SWA_ASSERT_OFFSETOF(CTitleMenu, m_Field9F, 0x9F);
SWA_ASSERT_OFFSETOF(CTitleMenu, m_IsDLCInfoMessageOpen, 0xA2);
}
@@ -4,14 +4,5 @@
namespace SWA
{
class CTitleStateBase : public Hedgehog::Universe::CStateMachineBase::CStateBase
{
public:
class CTitleStateContext
{
public:
SWA_INSERT_PADDING(0x1E8);
xpointer<CTitleMenu> m_pTitleMenu;
};
};
class CTitleStateBase : public Hedgehog::Universe::CStateMachineBase::CStateBase {};
}
@@ -0,0 +1,17 @@
#pragma once
#include <SWA.inl>
namespace SWA
{
class CTitleStateMenu : public CTitleStateBase
{
public:
class CTitleStateMenuContext
{
public:
SWA_INSERT_PADDING(0x1E8);
xpointer<CTitleMenu> m_pTitleMenu;
};
};
}
@@ -0,0 +1,17 @@
#pragma once
#include <SWA.inl>
namespace SWA
{
class CMatrixNodeTransform : public Hedgehog::Mirage::CMatrixNode
{
public:
Hedgehog::Mirage::CTransform m_Transform;
Hedgehog::Math::CMatrix m_WorldMatrix;
};
SWA_ASSERT_OFFSETOF(CMatrixNodeTransform, m_Transform, 0x60);
SWA_ASSERT_OFFSETOF(CMatrixNodeTransform, m_WorldMatrix, 0xC0);
SWA_ASSERT_SIZEOF(CMatrixNodeTransform, 0x100);
}
+18 -2
View File
@@ -28,7 +28,7 @@ void App::Exit()
std::_Exit(0);
}
// SWA::CApplication
// SWA::CApplication::CApplication
PPC_FUNC_IMPL(__imp__sub_824EB490);
PPC_FUNC(sub_824EB490)
{
@@ -69,12 +69,28 @@ PPC_FUNC(sub_822C1130)
{
SDL_PumpEvents();
SDL_FlushEvents(SDL_FIRSTEVENT, SDL_LASTEVENT);
GameWindow::Update();
}
GameWindow::Update();
AudioPatches::Update(App::s_deltaTime);
InspirePatches::Update();
// Apply subtitles option.
if (auto pApplicationDocument = SWA::CApplicationDocument::GetInstance())
pApplicationDocument->m_InspireSubtitles = Config::Subtitles;
if (Config::EnableEventCollisionDebugView)
*SWA::SGlobals::ms_IsTriggerRender = true;
if (Config::EnableGIMipLevelDebugView)
*SWA::SGlobals::ms_VisualizeLoadedLevel = true;
if (Config::EnableObjectCollisionDebugView)
*SWA::SGlobals::ms_IsObjectCollisionRender = true;
if (Config::EnableStageCollisionDebugView)
*SWA::SGlobals::ms_IsCollisionRender = true;
__imp__sub_822C1130(ctx, base);
}
-4
View File
@@ -165,11 +165,7 @@ int GetThreadPriorityImpl(GuestThreadHandle* hThread)
uint32_t SetThreadIdealProcessorImpl(GuestThreadHandle* hThread, uint32_t dwIdealProcessor)
{
#ifdef _WIN32
return SetThreadIdealProcessor(hThread == GetKernelObject(CURRENT_THREAD_HANDLE) ? GetCurrentThread() : hThread->thread.native_handle(), dwIdealProcessor);
#else
return 0;
#endif
}
GUEST_FUNCTION_HOOK(sub_82DFA2E8, SetThreadNameImpl);
+5 -11
View File
@@ -5,6 +5,7 @@
#include <kernel/heap.h>
#include <kernel/memory.h>
#include <ui/game_window.h>
#include <patches/inspire_patches.h>
void Game_PlaySound(const char* pName)
{
@@ -14,8 +15,11 @@ void Game_PlaySound(const char* pName)
}
else
{
// Use EVENT category in cutscenes since SYSTEM gets muted by the game.
uint32_t category = !InspirePatches::s_sceneName.empty() ? 10 : 7;
guest_stack_var<boost::anonymous_shared_ptr> soundPlayer;
GuestToHostFunction<void>(sub_82B4DF50, soundPlayer.get(), ((be<uint32_t>*)g_memory.Translate(0x83367900))->get(), 7, 0, 0);
GuestToHostFunction<void>(sub_82B4DF50, soundPlayer.get(), ((be<uint32_t>*)g_memory.Translate(0x83367900))->get(), category, 0, 0);
auto soundPlayerVtable = (be<uint32_t>*)g_memory.Translate(*(be<uint32_t>*)soundPlayer->get());
uint32_t virtualFunction = *(soundPlayerVtable + 1);
@@ -27,13 +31,3 @@ void Game_PlaySound(const char* pName)
g_userHeap.Free(strAllocation);
}
}
void Window_SetDisplay(int displayIndex)
{
GameWindow::SetDisplay(displayIndex);
}
void Window_SetFullscreen(bool isEnabled)
{
GameWindow::SetFullscreen(isEnabled);
}
-2
View File
@@ -1,5 +1,3 @@
#pragma once
void Game_PlaySound(const char* pName);
void Window_SetDisplay(int displayIndex);
void Window_SetFullscreen(bool isEnabled);
+1 -1
View File
@@ -11,7 +11,7 @@ ImGuiCallbackData* AddImGuiCallback(ImGuiCallback callback)
auto& callbackData = g_callbackData[g_callbackDataIndex];
++g_callbackDataIndex;
ImGui::GetForegroundDrawList()->AddCallback(reinterpret_cast<ImDrawCallback>(callback), callbackData.get());
ImGui::GetBackgroundDrawList()->AddCallback(reinterpret_cast<ImDrawCallback>(callback), callbackData.get());
return callbackData.get();
}
@@ -221,6 +221,8 @@ static bool FontBuilder_Build(ImFontAtlas* atlas)
for (size_t i = 0; i < atlas->ConfigData.size(); i++)
{
auto& config = atlas->ConfigData[i];
bool increaseSpacing = strstr(config.Name, "Seurat") != nullptr;
auto& [index, count] = ranges[i];
for (size_t j = 0; j < count; j++)
{
@@ -228,6 +230,11 @@ static bool FontBuilder_Build(ImFontAtlas* atlas)
double x0, y0, x1, y1, u0, v0, u1, v1;
glyph.getQuadPlaneBounds(x0, y0, x1, y1);
glyph.getQuadAtlasBounds(u0, v0, u1, v1);
double advance = glyph.getAdvance();
if (increaseSpacing && glyph.getCodepoint() == ' ')
advance *= 1.5;
config.DstFont->AddGlyph(
&config,
glyph.getCodepoint(),
@@ -239,7 +246,7 @@ static bool FontBuilder_Build(ImFontAtlas* atlas)
v1 / packer.height,
u1 / packer.width,
v0 / packer.height,
glyph.getAdvance());
advance);
}
config.DstFont->BuildLookupTable();
+124 -7
View File
@@ -27,6 +27,8 @@
//# define D3D12_DEBUG_LAYER_GPU_BASED_VALIDATION_ENABLED
#endif
//#define D3D12_DEBUG_SET_STABLE_POWER_STATE
// Old Windows SDK versions don't provide this macro, so we workaround it by making sure it is defined.
#ifndef D3D12_RESOURCE_STATE_ALL_SHADER_RESOURCE
#define D3D12_RESOURCE_STATE_ALL_SHADER_RESOURCE (D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE)
@@ -692,6 +694,20 @@ namespace plume {
);
}
static D3D12_RESOLVE_MODE toD3D12(RenderResolveMode resolveMode) {
switch (resolveMode) {
case RenderResolveMode::MIN:
return D3D12_RESOLVE_MODE_MIN;
case RenderResolveMode::MAX:
return D3D12_RESOLVE_MODE_MAX;
case RenderResolveMode::AVERAGE:
return D3D12_RESOLVE_MODE_AVERAGE;
default:
assert(false && "Unknown resolve mode.");
return D3D12_RESOLVE_MODE_AVERAGE;
}
}
static void setObjectName(ID3D12Object *object, const std::string &name) {
const std::wstring wideCharName = Utf8ToUtf16(name);
object->SetName(wideCharName.c_str());
@@ -1421,6 +1437,52 @@ namespace plume {
return height;
}
// D3D12QueryPool
D3D12QueryPool::D3D12QueryPool(D3D12Device *device, uint32_t queryCount) {
assert(device != nullptr);
assert(queryCount > 0);
this->device = device;
D3D12_QUERY_HEAP_DESC queryHeapDesc = {};
queryHeapDesc.Type = D3D12_QUERY_HEAP_TYPE_TIMESTAMP;
queryHeapDesc.Count = queryCount;
HRESULT res = device->d3d->CreateQueryHeap(&queryHeapDesc, IID_PPV_ARGS(&d3d));
if (FAILED(res)) {
fprintf(stderr, "CreateQueryHeap failed with error code 0x%lX.\n", res);
return;
}
readbackBuffer = device->createBuffer(RenderBufferDesc::ReadbackBuffer(sizeof(uint64_t) * queryCount));
results.resize(queryCount);
}
D3D12QueryPool::~D3D12QueryPool() {
if (d3d != nullptr) {
d3d->Release();
}
}
void D3D12QueryPool::queryResults() {
void *readbackData = readbackBuffer->map();
memcpy(results.data(), readbackData, sizeof(uint64_t) * results.size());
readbackBuffer->unmap();
for (uint64_t &result : results) {
result = result / double(device->timestampFrequency) * 1000000000.0;
}
}
const uint64_t *D3D12QueryPool::getResults() const {
return results.data();
}
uint32_t D3D12QueryPool::getCount() const {
return uint32_t(results.size());
}
// D3D12CommandList
D3D12CommandList::D3D12CommandList(D3D12Device *device, RenderCommandListType type) {
@@ -1916,7 +1978,7 @@ namespace plume {
resetSamplePositions();
}
void D3D12CommandList::resolveTextureRegion(const RenderTexture *dstTexture, uint32_t dstX, uint32_t dstY, const RenderTexture *srcTexture, const RenderRect *srcRect) {
void D3D12CommandList::resolveTextureRegion(const RenderTexture *dstTexture, uint32_t dstX, uint32_t dstY, const RenderTexture *srcTexture, const RenderRect *srcRect, RenderResolveMode resolveMode) {
assert(dstTexture != nullptr);
assert(srcTexture != nullptr);
@@ -1931,7 +1993,7 @@ namespace plume {
}
setSamplePositions(interfaceDstTexture);
d3d->ResolveSubresourceRegion(interfaceDstTexture->d3d, 0, dstX, dstY, interfaceSrcTexture->d3d, 0, (srcRect != nullptr) ? &rect : nullptr, toDXGI(interfaceDstTexture->desc.format), D3D12_RESOLVE_MODE_AVERAGE);
d3d->ResolveSubresourceRegion(interfaceDstTexture->d3d, 0, dstX, dstY, interfaceSrcTexture->d3d, 0, (srcRect != nullptr) ? &rect : nullptr, toDXGI(interfaceDstTexture->desc.format), toD3D12(resolveMode));
resetSamplePositions();
}
@@ -1988,6 +2050,19 @@ namespace plume {
d3d->DiscardResource(interfaceTexture->d3d, nullptr);
}
void D3D12CommandList::resetQueryPool(const RenderQueryPool *queryPool, uint32_t queryFirstIndex, uint32_t queryCount) {
// Do nothing.
}
void D3D12CommandList::writeTimestamp(const RenderQueryPool *queryPool, uint32_t queryIndex) {
assert(queryPool != nullptr);
const D3D12QueryPool *interfaceQueryPool = static_cast<const D3D12QueryPool *>(queryPool);
const D3D12Buffer *readbackBuffer = static_cast<const D3D12Buffer *>(interfaceQueryPool->readbackBuffer.get());
d3d->EndQuery(interfaceQueryPool->d3d, D3D12_QUERY_TYPE_TIMESTAMP, queryIndex);
d3d->ResolveQueryData(interfaceQueryPool->d3d, D3D12_QUERY_TYPE_TIMESTAMP, queryIndex, 1, readbackBuffer->d3d, queryIndex * sizeof(uint64_t));
}
void D3D12CommandList::checkDescriptorHeaps() {
if (!descriptorHeapsSet) {
ID3D12DescriptorHeap *descriptorHeaps[] = { device->viewHeapAllocator->heap, device->samplerHeapAllocator->heap };
@@ -3243,7 +3318,7 @@ namespace plume {
// D3D12Device
D3D12Device::D3D12Device(D3D12Interface *renderInterface) {
D3D12Device::D3D12Device(D3D12Interface *renderInterface, const std::string &preferredDeviceName) {
assert(renderInterface != nullptr);
this->renderInterface = renderInterface;
@@ -3333,9 +3408,10 @@ namespace plume {
}
// Pick this adapter and device if it has better feature support than the current one.
std::string deviceName = Utf16ToUtf8(adapterDesc.Description);
bool preferOverNothing = (adapter == nullptr) || (d3d == nullptr);
bool preferVideoMemory = adapterDesc.DedicatedVideoMemory > description.dedicatedVideoMemory;
bool preferUserChoice = false;//wcsstr(adapterDesc.Description, L"AMD") != nullptr;
bool preferUserChoice = preferredDeviceName == deviceName;
bool preferOption = preferOverNothing || preferVideoMemory || preferUserChoice;
if (preferOption) {
if (d3d != nullptr) {
@@ -3355,8 +3431,15 @@ namespace plume {
capabilities.triangleFan = triangleFanSupportOption;
capabilities.dynamicDepthBias = dynamicDepthBiasOption;
capabilities.uma = uma;
description.name = Utf16ToUtf8(adapterDesc.Description);
description.name = deviceName;
description.dedicatedVideoMemory = adapterDesc.DedicatedVideoMemory;
description.vendor = RenderDeviceVendor(adapterDesc.VendorId);
LARGE_INTEGER adapterVersion = {};
res = adapter->CheckInterfaceSupport(__uuidof(IDXGIDevice), &adapterVersion);
if (SUCCEEDED(res)) {
description.driverVersion = adapterVersion.QuadPart;
}
if (preferUserChoice) {
break;
@@ -3373,6 +3456,10 @@ namespace plume {
return;
}
#ifdef D3D12_DEBUG_SET_STABLE_POWER_STATE
d3d->SetStablePowerState(TRUE);
#endif
D3D12MA::ALLOCATOR_DESC allocatorDesc = {};
allocatorDesc.pDevice = d3d;
allocatorDesc.pAdapter = adapter;
@@ -3440,6 +3527,13 @@ namespace plume {
samplerHeapAllocator = std::make_unique<D3D12DescriptorHeapAllocator>(this, SamplerDescriptorHeapSize, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
colorTargetHeapAllocator = std::make_unique<D3D12DescriptorHeapAllocator>(this, TargetDescriptorHeapSize, D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
depthTargetHeapAllocator = std::make_unique<D3D12DescriptorHeapAllocator>(this, TargetDescriptorHeapSize, D3D12_DESCRIPTOR_HEAP_TYPE_DSV);
// Create a command queue only for retrieving the timestamp frequency. Delete it immediately afterwards.
std::unique_ptr<D3D12CommandQueue> timestampCommandQueue = std::make_unique<D3D12CommandQueue>(this, RenderCommandListType::DIRECT);
res = timestampCommandQueue->d3d->GetTimestampFrequency(&timestampFrequency);
if (FAILED(res)) {
fprintf(stderr, "GetTimestampFrequency failed with error code 0x%lX. Timestamps will be inaccurate.\n", res);
}
}
D3D12Device::~D3D12Device() {
@@ -3514,6 +3608,10 @@ namespace plume {
return std::make_unique<D3D12Framebuffer>(this, desc);
}
std::unique_ptr<RenderQueryPool> D3D12Device::createQueryPool(uint32_t queryCount) {
return std::make_unique<D3D12QueryPool>(this, queryCount);
}
void D3D12Device::setBottomLevelASBuildInfo(RenderBottomLevelASBuildInfo &buildInfo, const RenderBottomLevelASMesh *meshes, uint32_t meshCount, bool preferFastBuild, bool preferFastTrace) {
assert(meshes != nullptr);
assert(meshCount > 0);
@@ -3747,6 +3845,21 @@ namespace plume {
// Fill capabilities.
capabilities.shaderFormat = RenderShaderFormat::DXIL;
// Fill device names.
UINT adapterIndex = 0;
IDXGIAdapter1 *adapterOption = nullptr;
while (dxgiFactory->EnumAdapters1(adapterIndex++, &adapterOption) != DXGI_ERROR_NOT_FOUND) {
DXGI_ADAPTER_DESC1 adapterDesc;
adapterOption->GetDesc1(&adapterDesc);
// Ignore remote or software adapters.
if ((adapterDesc.Flags & (DXGI_ADAPTER_FLAG_REMOTE | DXGI_ADAPTER_FLAG_SOFTWARE)) == 0) {
deviceNames.emplace_back(Utf16ToUtf8(adapterDesc.Description));
}
adapterOption->Release();
}
}
D3D12Interface::~D3D12Interface() {
@@ -3755,8 +3868,8 @@ namespace plume {
}
}
std::unique_ptr<RenderDevice> D3D12Interface::createDevice() {
std::unique_ptr<D3D12Device> createdDevice = std::make_unique<D3D12Device>(this);
std::unique_ptr<RenderDevice> D3D12Interface::createDevice(const std::string &preferredDeviceName) {
std::unique_ptr<D3D12Device> createdDevice = std::make_unique<D3D12Device>(this, preferredDeviceName);
return createdDevice->isValid() ? std::move(createdDevice) : nullptr;
}
@@ -3764,6 +3877,10 @@ namespace plume {
return capabilities;
}
const std::vector<std::string> &D3D12Interface::getDeviceNames() const {
return deviceNames;
}
bool D3D12Interface::isValid() const {
return dxgiFactory != nullptr;
}
+22 -3
View File
@@ -144,6 +144,19 @@ namespace plume {
uint32_t getHeight() const override;
};
struct D3D12QueryPool : RenderQueryPool {
D3D12Device *device = nullptr;
ID3D12QueryHeap *d3d = nullptr;
std::vector<uint64_t> results;
std::unique_ptr<RenderBuffer> readbackBuffer;
D3D12QueryPool(D3D12Device *device, uint32_t queryCount);
virtual ~D3D12QueryPool() override;
virtual void queryResults() override;
virtual const uint64_t *getResults() const override;
virtual uint32_t getCount() const override;
};
struct D3D12CommandList : RenderCommandList {
ID3D12GraphicsCommandList9 *d3d = nullptr;
ID3D12CommandAllocator *commandAllocator = nullptr;
@@ -192,10 +205,12 @@ namespace plume {
void copyBuffer(const RenderBuffer *dstBuffer, const RenderBuffer *srcBuffer) override;
void copyTexture(const RenderTexture *dstTexture, const RenderTexture *srcTexture) override;
void resolveTexture(const RenderTexture *dstTexture, const RenderTexture *srcTexture) override;
void resolveTextureRegion(const RenderTexture *dstTexture, uint32_t dstX, uint32_t dstY, const RenderTexture *srcTexture, const RenderRect *srcRect) override;
void resolveTextureRegion(const RenderTexture *dstTexture, uint32_t dstX, uint32_t dstY, const RenderTexture *srcTexture, const RenderRect *srcRect, RenderResolveMode resolveMode) override;
void buildBottomLevelAS(const RenderAccelerationStructure *dstAccelerationStructure, RenderBufferReference scratchBuffer, const RenderBottomLevelASBuildInfo &buildInfo) override;
void buildTopLevelAS(const RenderAccelerationStructure *dstAccelerationStructure, RenderBufferReference scratchBuffer, RenderBufferReference instancesBuffer, const RenderTopLevelASBuildInfo &buildInfo) override;
void discardTexture(const RenderTexture* texture) override;
void resetQueryPool(const RenderQueryPool *queryPool, uint32_t queryFirstIndex, uint32_t queryCount) override;
void writeTimestamp(const RenderQueryPool *queryPool, uint32_t queryIndex) override;
void checkDescriptorHeaps();
void notifyDescriptorHeapWasChangedExternally();
void checkTopology();
@@ -417,8 +432,9 @@ namespace plume {
std::unique_ptr<D3D12DescriptorHeapAllocator> depthTargetHeapAllocator;
RenderDeviceCapabilities capabilities;
RenderDeviceDescription description;
uint64_t timestampFrequency = 1;
D3D12Device(D3D12Interface *renderInterface);
D3D12Device(D3D12Interface *renderInterface, const std::string &preferredDeviceName);
~D3D12Device() override;
std::unique_ptr<RenderCommandList> createCommandList(RenderCommandListType type) override;
std::unique_ptr<RenderDescriptorSet> createDescriptorSet(const RenderDescriptorSetDesc &desc) override;
@@ -436,6 +452,7 @@ namespace plume {
std::unique_ptr<RenderCommandFence> createCommandFence() override;
std::unique_ptr<RenderCommandSemaphore> createCommandSemaphore() override;
std::unique_ptr<RenderFramebuffer> createFramebuffer(const RenderFramebufferDesc &desc) override;
std::unique_ptr<RenderQueryPool> createQueryPool(uint32_t queryCount) override;
void setBottomLevelASBuildInfo(RenderBottomLevelASBuildInfo &buildInfo, const RenderBottomLevelASMesh *meshes, uint32_t meshCount, bool preferFastBuild, bool preferFastTrace) override;
void setTopLevelASBuildInfo(RenderTopLevelASBuildInfo &buildInfo, const RenderTopLevelASInstance *instances, uint32_t instanceCount, bool preferFastBuild, bool preferFastTrace) override;
void setShaderBindingTableInfo(RenderShaderBindingTableInfo &tableInfo, const RenderShaderBindingGroups &groups, const RenderPipeline *pipeline, RenderDescriptorSet **descriptorSets, uint32_t descriptorSetCount) override;
@@ -450,11 +467,13 @@ namespace plume {
struct D3D12Interface : RenderInterface {
IDXGIFactory4 *dxgiFactory = nullptr;
RenderInterfaceCapabilities capabilities;
std::vector<std::string> deviceNames;
D3D12Interface();
~D3D12Interface() override;
std::unique_ptr<RenderDevice> createDevice() override;
std::unique_ptr<RenderDevice> createDevice(const std::string &preferredDeviceName) override;
const RenderInterfaceCapabilities &getCapabilities() const override;
const std::vector<std::string> &getDeviceNames() const override;
bool isValid() const;
};
};
@@ -143,10 +143,12 @@ namespace plume {
virtual void copyBuffer(const RenderBuffer *dstBuffer, const RenderBuffer *srcBuffer) = 0;
virtual void copyTexture(const RenderTexture *dstTexture, const RenderTexture *srcTexture) = 0;
virtual void resolveTexture(const RenderTexture *dstTexture, const RenderTexture *srcTexture) = 0;
virtual void resolveTextureRegion(const RenderTexture *dstTexture, uint32_t dstX, uint32_t dstY, const RenderTexture *srcTexture, const RenderRect *srcRect = nullptr) = 0;
virtual void resolveTextureRegion(const RenderTexture *dstTexture, uint32_t dstX, uint32_t dstY, const RenderTexture *srcTexture, const RenderRect *srcRect = nullptr, RenderResolveMode resolveMode = RenderResolveMode::AVERAGE) = 0;
virtual void buildBottomLevelAS(const RenderAccelerationStructure *dstAccelerationStructure, RenderBufferReference scratchBuffer, const RenderBottomLevelASBuildInfo &buildInfo) = 0;
virtual void buildTopLevelAS(const RenderAccelerationStructure *dstAccelerationStructure, RenderBufferReference scratchBuffer, RenderBufferReference instancesBuffer, const RenderTopLevelASBuildInfo &buildInfo) = 0;
virtual void discardTexture(const RenderTexture* texture) = 0; // D3D12 only.
virtual void resetQueryPool(const RenderQueryPool *queryPool, uint32_t queryFirstIndex, uint32_t queryCount) = 0;
virtual void writeTimestamp(const RenderQueryPool *queryPool, uint32_t queryIndex) = 0;
// Concrete implementation shortcuts.
inline void barriers(RenderBarrierStages stages, const RenderBufferBarrier &barrier) {
@@ -208,6 +210,13 @@ namespace plume {
virtual std::unique_ptr<RenderTexture> createTexture(const RenderTextureDesc &desc) = 0;
};
struct RenderQueryPool {
virtual ~RenderQueryPool() { }
virtual void queryResults() = 0;
virtual const uint64_t *getResults() const = 0;
virtual uint32_t getCount() const = 0;
};
struct RenderDevice {
virtual ~RenderDevice() { }
virtual std::unique_ptr<RenderCommandList> createCommandList(RenderCommandListType type) = 0;
@@ -226,6 +235,7 @@ namespace plume {
virtual std::unique_ptr<RenderCommandFence> createCommandFence() = 0;
virtual std::unique_ptr<RenderCommandSemaphore> createCommandSemaphore() = 0;
virtual std::unique_ptr<RenderFramebuffer> createFramebuffer(const RenderFramebufferDesc &desc) = 0;
virtual std::unique_ptr<RenderQueryPool> createQueryPool(uint32_t queryCount) = 0;
virtual void setBottomLevelASBuildInfo(RenderBottomLevelASBuildInfo &buildInfo, const RenderBottomLevelASMesh *meshes, uint32_t meshCount, bool preferFastBuild = true, bool preferFastTrace = false) = 0;
virtual void setTopLevelASBuildInfo(RenderTopLevelASBuildInfo &buildInfo, const RenderTopLevelASInstance *instances, uint32_t instanceCount, bool preferFastBuild = true, bool preferFastTrace = false) = 0;
virtual void setShaderBindingTableInfo(RenderShaderBindingTableInfo &tableInfo, const RenderShaderBindingGroups &groups, const RenderPipeline *pipeline, RenderDescriptorSet **descriptorSets, uint32_t descriptorSetCount) = 0;
@@ -237,7 +247,8 @@ namespace plume {
struct RenderInterface {
virtual ~RenderInterface() { }
virtual std::unique_ptr<RenderDevice> createDevice() = 0;
virtual std::unique_ptr<RenderDevice> createDevice(const std::string &preferredDeviceName = "") = 0;
virtual const std::vector<std::string> &getDeviceNames() const = 0;
virtual const RenderInterfaceCapabilities &getCapabilities() const = 0;
};
@@ -69,9 +69,17 @@ namespace plume {
struct RenderSampler;
struct RenderShader;
struct RenderTexture;
struct RenderQueryPool;
// Enums.
enum class RenderDeviceVendor {
UNKNOWN = 0x0,
AMD = 0x1002,
NVIDIA = 0x10DE,
INTEL = 0x8086
};
enum class RenderFormat {
UNKNOWN,
R32G32B32A32_TYPELESS,
@@ -483,6 +491,12 @@ namespace plume {
CPU
};
enum class RenderResolveMode {
MIN,
MAX,
AVERAGE
};
// Global functions.
constexpr uint32_t RenderFormatSize(RenderFormat format) {
@@ -1763,7 +1777,8 @@ namespace plume {
struct RenderDeviceDescription {
std::string name = "Unknown";
RenderDeviceType type = RenderDeviceType::UNKNOWN;
uint32_t driverVersion = 0;
RenderDeviceVendor vendor = RenderDeviceVendor::UNKNOWN;
uint64_t driverVersion = 0;
uint64_t dedicatedVideoMemory = 0;
};
+128 -7
View File
@@ -2522,6 +2522,80 @@ namespace plume {
return (depthAttachment == attachment);
}
// VulkanQueryPool
VulkanQueryPool::VulkanQueryPool(VulkanDevice *device, uint32_t queryCount) {
assert(device != nullptr);
assert(queryCount > 0);
this->device = device;
VkQueryPoolCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;
createInfo.queryType = VK_QUERY_TYPE_TIMESTAMP;
createInfo.queryCount = queryCount;
VkResult res = vkCreateQueryPool(device->vk, &createInfo, nullptr, &vk);
if (res != VK_SUCCESS) {
fprintf(stderr, "vkCreateQueryPool failed with error code 0x%X.\n", res);
return;
}
results.resize(queryCount);
}
VulkanQueryPool::~VulkanQueryPool() {
vkDestroyQueryPool(device->vk, vk, nullptr);
}
void VulkanQueryPool::queryResults() {
VkResult res = vkGetQueryPoolResults(device->vk, vk, 0, uint32_t(results.size()), sizeof(uint64_t) * results.size(), results.data(), sizeof(uint64_t), VK_QUERY_RESULT_64_BIT);
if (res != VK_SUCCESS) {
fprintf(stderr, "vkGetQueryPoolResults failed with error code 0x%X.\n", res);
return;
}
// Conversion sourced from Godot Engine's Vulkan Rendering Driver.
auto mult64to128 = [](uint64_t u, uint64_t v, uint64_t &h, uint64_t &l) {
uint64_t u1 = (u & 0xffffffff);
uint64_t v1 = (v & 0xffffffff);
uint64_t t = (u1 * v1);
uint64_t w3 = (t & 0xffffffff);
uint64_t k = (t >> 32);
u >>= 32;
t = (u * v1) + k;
k = (t & 0xffffffff);
uint64_t w1 = (t >> 32);
v >>= 32;
t = (u1 * v) + k;
k = (t >> 32);
h = (u * v) + w1 + k;
l = (t << 32) + w3;
};
// Convert results to timestamps.
constexpr uint64_t shift_bits = 16;
double timestampPeriod = double(device->physicalDeviceProperties.limits.timestampPeriod);
uint64_t h = 0, l = 0;
for (uint64_t &result : results) {
mult64to128(result, uint64_t(timestampPeriod * double(1 << shift_bits)), h, l);
result = l;
result >>= shift_bits;
result |= h << (64 - shift_bits);
}
}
const uint64_t *VulkanQueryPool::getResults() const {
return results.data();
}
uint32_t VulkanQueryPool::getCount() const {
return uint32_t(results.size());
}
// VulkanCommandList
VulkanCommandList::VulkanCommandList(VulkanDevice *device, RenderCommandListType type) {
@@ -3074,12 +3148,13 @@ namespace plume {
}
void VulkanCommandList::resolveTexture(const RenderTexture *dstTexture, const RenderTexture *srcTexture) {
resolveTextureRegion(dstTexture, 0, 0, srcTexture, nullptr);
resolveTextureRegion(dstTexture, 0, 0, srcTexture, nullptr, RenderResolveMode::AVERAGE);
}
void VulkanCommandList::resolveTextureRegion(const RenderTexture *dstTexture, uint32_t dstX, uint32_t dstY, const RenderTexture *srcTexture, const RenderRect *srcRect) {
void VulkanCommandList::resolveTextureRegion(const RenderTexture *dstTexture, uint32_t dstX, uint32_t dstY, const RenderTexture *srcTexture, const RenderRect *srcRect, RenderResolveMode resolveMode) {
assert(dstTexture != nullptr);
assert(srcTexture != nullptr);
assert(resolveMode == RenderResolveMode::AVERAGE && "Vulkan only supports AVERAGE resolve mode.");
thread_local std::vector<VkImageResolve> imageResolves;
imageResolves.clear();
@@ -3209,6 +3284,20 @@ namespace plume {
// Not required in Vulkan.
}
void VulkanCommandList::resetQueryPool(const RenderQueryPool *queryPool, uint32_t queryFirstIndex, uint32_t queryCount) {
assert(queryPool != nullptr);
const VulkanQueryPool *interfaceQueryPool = static_cast<const VulkanQueryPool *>(queryPool);
vkCmdResetQueryPool(vk, interfaceQueryPool->vk, queryFirstIndex, queryCount);
}
void VulkanCommandList::writeTimestamp(const RenderQueryPool *queryPool, uint32_t queryIndex) {
assert(queryPool != nullptr);
const VulkanQueryPool *interfaceQueryPool = static_cast<const VulkanQueryPool *>(queryPool);
vkCmdWriteTimestamp(vk, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, interfaceQueryPool->vk, queryIndex);
}
void VulkanCommandList::checkActiveRenderPass() {
assert(targetFramebuffer != nullptr);
@@ -3472,7 +3561,7 @@ namespace plume {
// VulkanDevice
VulkanDevice::VulkanDevice(VulkanInterface *renderInterface) {
VulkanDevice::VulkanDevice(VulkanInterface *renderInterface, const std::string &preferredDeviceName) {
assert(renderInterface != nullptr);
this->renderInterface = renderInterface;
@@ -3505,15 +3594,22 @@ namespace plume {
continue;
}
std::string deviceName(deviceProperties.deviceName);
uint32_t deviceTypeScore = deviceTypeScoreTable[deviceTypeIndex];
bool preferDeviceTypeScore = (deviceTypeScore > currentDeviceTypeScore);
bool preferOption = preferDeviceTypeScore;
bool preferUserChoice = preferredDeviceName == deviceName;
bool preferOption = preferDeviceTypeScore || preferUserChoice;
if (preferOption) {
physicalDevice = physicalDevices[i];
description.name = std::string(deviceProperties.deviceName);
description.name = deviceName;
description.type = toDeviceType(deviceProperties.deviceType);
description.driverVersion = deviceProperties.driverVersion;
description.vendor = RenderDeviceVendor(deviceProperties.vendorID);
currentDeviceTypeScore = deviceTypeScore;
if (preferUserChoice) {
break;
}
}
}
@@ -3884,6 +3980,10 @@ namespace plume {
return std::make_unique<VulkanFramebuffer>(this, desc);
}
std::unique_ptr<RenderQueryPool> VulkanDevice::createQueryPool(uint32_t queryCount) {
return std::make_unique<VulkanQueryPool>(this, queryCount);
}
void VulkanDevice::setBottomLevelASBuildInfo(RenderBottomLevelASBuildInfo &buildInfo, const RenderBottomLevelASMesh *meshes, uint32_t meshCount, bool preferFastBuild, bool preferFastTrace) {
assert(meshes != nullptr);
assert(meshCount > 0);
@@ -4232,6 +4332,23 @@ namespace plume {
// Fill capabilities.
capabilities.shaderFormat = RenderShaderFormat::SPIRV;
// Fill device names.
uint32_t deviceCount = 0;
vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
if (deviceCount > 0) {
std::vector<VkPhysicalDevice> physicalDevices(deviceCount);
vkEnumeratePhysicalDevices(instance, &deviceCount, physicalDevices.data());
for (uint32_t i = 0; i < deviceCount; i++) {
VkPhysicalDeviceProperties deviceProperties;
vkGetPhysicalDeviceProperties(physicalDevices[i], &deviceProperties);
uint32_t deviceTypeIndex = deviceProperties.deviceType;
if (deviceTypeIndex <= 4) {
deviceNames.emplace_back(deviceProperties.deviceName);
}
}
}
}
VulkanInterface::~VulkanInterface() {
@@ -4240,8 +4357,8 @@ namespace plume {
}
}
std::unique_ptr<RenderDevice> VulkanInterface::createDevice() {
std::unique_ptr<VulkanDevice> createdDevice = std::make_unique<VulkanDevice>(this);
std::unique_ptr<RenderDevice> VulkanInterface::createDevice(const std::string &preferredDeviceName) {
std::unique_ptr<VulkanDevice> createdDevice = std::make_unique<VulkanDevice>(this, preferredDeviceName);
return createdDevice->isValid() ? std::move(createdDevice) : nullptr;
}
@@ -4249,6 +4366,10 @@ namespace plume {
return capabilities;
}
const std::vector<std::string> &VulkanInterface::getDeviceNames() const {
return deviceNames;
}
bool VulkanInterface::isValid() const {
return instance != nullptr;
}
+20 -3
View File
@@ -271,6 +271,18 @@ namespace plume {
bool contains(const VulkanTexture *attachment) const;
};
struct VulkanQueryPool : RenderQueryPool {
VulkanDevice *device = nullptr;
std::vector<uint64_t> results;
VkQueryPool vk = VK_NULL_HANDLE;
VulkanQueryPool(VulkanDevice *device, uint32_t queryCount);
virtual ~VulkanQueryPool() override;
virtual void queryResults() override;
virtual const uint64_t *getResults() const override;
virtual uint32_t getCount() const override;
};
struct VulkanCommandList : RenderCommandList {
VkCommandBuffer vk = VK_NULL_HANDLE;
VkCommandPool commandPool = VK_NULL_HANDLE;
@@ -315,10 +327,12 @@ namespace plume {
void copyBuffer(const RenderBuffer *dstBuffer, const RenderBuffer *srcBuffer) override;
void copyTexture(const RenderTexture *dstTexture, const RenderTexture *srcTexture) override;
void resolveTexture(const RenderTexture *dstTexture, const RenderTexture *srcTexture) override;
void resolveTextureRegion(const RenderTexture *dstTexture, uint32_t dstX, uint32_t dstY, const RenderTexture *srcTexture, const RenderRect *srcRect) override;
void resolveTextureRegion(const RenderTexture *dstTexture, uint32_t dstX, uint32_t dstY, const RenderTexture *srcTexture, const RenderRect *srcRect, RenderResolveMode resolveMode) override;
void buildBottomLevelAS(const RenderAccelerationStructure *dstAccelerationStructure, RenderBufferReference scratchBuffer, const RenderBottomLevelASBuildInfo &buildInfo) override;
void buildTopLevelAS(const RenderAccelerationStructure *dstAccelerationStructure, RenderBufferReference scratchBuffer, RenderBufferReference instancesBuffer, const RenderTopLevelASBuildInfo &buildInfo) override;
void discardTexture(const RenderTexture* texture) override;
void resetQueryPool(const RenderQueryPool *queryPool, uint32_t queryFirstIndex, uint32_t queryCount) override;
void writeTimestamp(const RenderQueryPool *queryPool, uint32_t queryIndex) override;
void checkActiveRenderPass();
void endActiveRenderPass();
void setDescriptorSet(VkPipelineBindPoint bindPoint, const VulkanPipelineLayout *pipelineLayout, const RenderDescriptorSet *descriptorSet, uint32_t setIndex);
@@ -391,7 +405,7 @@ namespace plume {
VkPhysicalDeviceSampleLocationsPropertiesEXT sampleLocationProperties = {};
bool loadStoreOpNoneSupported = false;
VulkanDevice(VulkanInterface *renderInterface);
VulkanDevice(VulkanInterface *renderInterface, const std::string &preferredDeviceName);
~VulkanDevice() override;
std::unique_ptr<RenderCommandList> createCommandList(RenderCommandListType type) override;
std::unique_ptr<RenderDescriptorSet> createDescriptorSet(const RenderDescriptorSetDesc &desc) override;
@@ -409,6 +423,7 @@ namespace plume {
std::unique_ptr<RenderCommandFence> createCommandFence() override;
std::unique_ptr<RenderCommandSemaphore> createCommandSemaphore() override;
std::unique_ptr<RenderFramebuffer> createFramebuffer(const RenderFramebufferDesc &desc) override;
std::unique_ptr<RenderQueryPool> createQueryPool(uint32_t queryCount) override;
void setBottomLevelASBuildInfo(RenderBottomLevelASBuildInfo &buildInfo, const RenderBottomLevelASMesh *meshes, uint32_t meshCount, bool preferFastBuild, bool preferFastTrace) override;
void setTopLevelASBuildInfo(RenderTopLevelASBuildInfo &buildInfo, const RenderTopLevelASInstance *instances, uint32_t instanceCount, bool preferFastBuild, bool preferFastTrace) override;
void setShaderBindingTableInfo(RenderShaderBindingTableInfo &tableInfo, const RenderShaderBindingGroups &groups, const RenderPipeline *pipeline, RenderDescriptorSet **descriptorSets, uint32_t descriptorSetCount) override;
@@ -424,6 +439,7 @@ namespace plume {
VkInstance instance = VK_NULL_HANDLE;
VkApplicationInfo appInfo = {};
RenderInterfaceCapabilities capabilities;
std::vector<std::string> deviceNames;
# if SDL_VULKAN_ENABLED
VulkanInterface(RenderWindow sdlWindow);
@@ -432,8 +448,9 @@ namespace plume {
# endif
~VulkanInterface() override;
std::unique_ptr<RenderDevice> createDevice() override;
std::unique_ptr<RenderDevice> createDevice(const std::string &preferredDeviceName) override;
const RenderInterfaceCapabilities &getCapabilities() const override;
const std::vector<std::string> &getDeviceNames() const override;
bool isValid() const;
};
};
@@ -0,0 +1,8 @@
#include "copy_common.hlsli"
Texture2D<float4> g_Texture2DDescriptorHeap[] : register(t0, space0);
float4 main(in float4 position : SV_Position) : SV_Target
{
return g_Texture2DDescriptorHeap[g_PushConstants.ResourceDescriptorIndex].Load(int3(position.xy, 0));
}
@@ -0,0 +1,8 @@
#pragma once
struct PushConstants
{
uint ResourceDescriptorIndex;
};
[[vk::push_constant]] ConstantBuffer<PushConstants> g_PushConstants : register(b3, space4);
@@ -0,0 +1,8 @@
#include "copy_common.hlsli"
Texture2D<float> g_Texture2DDescriptorHeap[] : register(t0, space0);
float main(in float4 position : SV_Position) : SV_Depth
{
return g_Texture2DDescriptorHeap[g_PushConstants.ResourceDescriptorIndex].Load(int3(position.xy, 0));
}
+2 -2
View File
@@ -138,14 +138,14 @@ float4 main(in Interpolators interpolators) : SV_Target
if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_HORIZONTAL_MARQUEE_FADE)
{
float minAlpha = saturate((interpolators.Position.x - g_PushConstants.BoundsMin.x) / g_PushConstants.Scale.x);
float maxAlpha = saturate((g_PushConstants.BoundsMax.x - interpolators.Position.x) / g_PushConstants.Scale.x);
float maxAlpha = saturate((g_PushConstants.BoundsMax.x - interpolators.Position.x) / g_PushConstants.Scale.y);
color.a *= minAlpha;
color.a *= maxAlpha;
}
else if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_VERTICAL_MARQUEE_FADE)
{
float minAlpha = saturate((interpolators.Position.y - g_PushConstants.BoundsMin.y) / g_PushConstants.Scale.y);
float minAlpha = saturate((interpolators.Position.y - g_PushConstants.BoundsMin.y) / g_PushConstants.Scale.x);
float maxAlpha = saturate((g_PushConstants.BoundsMax.y - interpolators.Position.y) / g_PushConstants.Scale.y);
color.a *= minAlpha;
@@ -0,0 +1,15 @@
#pragma once
#include "copy_common.hlsli"
Texture2DMS<float4, SAMPLE_COUNT> g_Texture2DMSDescriptorHeap[] : register(t0, space0);
float4 main(in float4 position : SV_Position) : SV_Target
{
float4 result = g_Texture2DMSDescriptorHeap[g_PushConstants.ResourceDescriptorIndex].Load(int2(position.xy), 0);
[unroll] for (int i = 1; i < SAMPLE_COUNT; i++)
result += g_Texture2DMSDescriptorHeap[g_PushConstants.ResourceDescriptorIndex].Load(int2(position.xy), i);
return result / SAMPLE_COUNT;
}
@@ -0,0 +1,2 @@
#define SAMPLE_COUNT 2
#include "resolve_msaa_color.hlsli"
@@ -0,0 +1,2 @@
#define SAMPLE_COUNT 4
#include "resolve_msaa_color.hlsli"
@@ -0,0 +1,2 @@
#define SAMPLE_COUNT 8
#include "resolve_msaa_color.hlsli"
@@ -1,11 +1,6 @@
#pragma once
struct PushConstants
{
uint ResourceDescriptorIndex;
};
[[vk::push_constant]] ConstantBuffer<PushConstants> g_PushConstants : register(b3, space4);
#include "copy_common.hlsli"
Texture2DMS<float, SAMPLE_COUNT> g_Texture2DMSDescriptorHeap[] : register(t0, space0);
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1,7 +1,7 @@
#pragma once
//#define ASYNC_PSO_DEBUG
#define PSO_CACHING
/////////////////////////////////////////////////////////////////////#define PSO_CACHING
//#define PSO_CACHING_CLEANUP
#include "rhi/plume_render_interface.h"
+10
View File
@@ -239,12 +239,16 @@ int HID_OnSDLEvent(void*, SDL_Event* event)
if (event->type == SDL_CONTROLLERAXISMOTION)
{
if (abs(event->caxis.value) > 8000)
{
SDL_ShowCursor(SDL_DISABLE);
SetControllerInputDevice(controller);
}
controller->PollAxis();
}
else
{
SDL_ShowCursor(SDL_DISABLE);
SetControllerInputDevice(controller);
controller->Poll();
@@ -261,8 +265,14 @@ int HID_OnSDLEvent(void*, SDL_Event* event)
case SDL_MOUSEMOTION:
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
{
if (!GameWindow::IsFullscreen() || GameWindow::s_isFullscreenCursorVisible)
SDL_ShowCursor(SDL_ENABLE);
hid::g_inputDevice = hid::EInputDevice::Mouse;
break;
}
case SDL_WINDOWEVENT:
{
+5 -1
View File
@@ -7,10 +7,14 @@ hid::EInputDevice hid::g_inputDeviceController;
hid::EInputDeviceExplicit hid::g_inputDeviceExplicit;
uint16_t hid::g_prohibitedButtons;
bool hid::g_isLeftStickProhibited;
bool hid::g_isRightStickProhibited;
void hid::SetProhibitedButtons(uint16_t wButtons)
void hid::SetProhibitedInputs(uint16_t wButtons, bool leftStick, bool rightStick)
{
hid::g_prohibitedButtons = wButtons;
hid::g_isLeftStickProhibited = leftStick;
hid::g_isRightStickProhibited = rightStick;
}
bool hid::IsInputAllowed()
+3 -1
View File
@@ -33,6 +33,8 @@ namespace hid
extern EInputDeviceExplicit g_inputDeviceExplicit;
extern uint16_t g_prohibitedButtons;
extern bool g_isLeftStickProhibited;
extern bool g_isRightStickProhibited;
void Init();
@@ -40,7 +42,7 @@ namespace hid
uint32_t SetState(uint32_t dwUserIndex, XAMINPUT_VIBRATION* pVibration);
uint32_t GetCapabilities(uint32_t dwUserIndex, XAMINPUT_CAPABILITIES* pCaps);
void SetProhibitedButtons(uint16_t wButtons);
void SetProhibitedInputs(uint16_t wButtons = 0, bool leftStick = false, bool rightStick = false);
bool IsInputAllowed();
bool IsInputDeviceController();
std::string GetInputDeviceName();
+26 -5
View File
@@ -67,7 +67,7 @@ static std::unique_ptr<VirtualFileSystem> createFileSystemFromPath(const std::fi
}
}
static bool copyFile(const FilePair &pair, const uint64_t *fileHashes, VirtualFileSystem &sourceVfs, const std::filesystem::path &targetDirectory, bool skipHashChecks, std::vector<uint8_t> &fileData, Journal &journal, const std::function<void()> &progressCallback) {
static bool copyFile(const FilePair &pair, const uint64_t *fileHashes, VirtualFileSystem &sourceVfs, const std::filesystem::path &targetDirectory, bool skipHashChecks, std::vector<uint8_t> &fileData, Journal &journal, const std::function<bool()> &progressCallback) {
const std::string filename(pair.first);
const uint32_t hashCount = pair.second;
if (!sourceVfs.exists(filename))
@@ -139,7 +139,13 @@ static bool copyFile(const FilePair &pair, const uint64_t *fileHashes, VirtualFi
}
journal.progressCounter += fileData.size();
progressCallback();
if (!progressCallback())
{
journal.lastResult = Journal::Result::Cancelled;
journal.lastErrorMessage = "Installation was cancelled.";
return false;
}
return true;
}
@@ -266,7 +272,7 @@ bool Installer::computeTotalSize(std::span<const FilePair> filePairs, const uint
return true;
}
bool Installer::copyFiles(std::span<const FilePair> filePairs, const uint64_t *fileHashes, VirtualFileSystem &sourceVfs, const std::filesystem::path &targetDirectory, const std::string &validationFile, bool skipHashChecks, Journal &journal, const std::function<void()> &progressCallback)
bool Installer::copyFiles(std::span<const FilePair> filePairs, const uint64_t *fileHashes, VirtualFileSystem &sourceVfs, const std::filesystem::path &targetDirectory, const std::string &validationFile, bool skipHashChecks, Journal &journal, const std::function<bool()> &progressCallback)
{
std::error_code ec;
if (!std::filesystem::exists(targetDirectory) && !std::filesystem::create_directories(targetDirectory, ec))
@@ -429,7 +435,7 @@ bool Installer::parseSources(const Input &input, Journal &journal, Sources &sour
return true;
}
bool Installer::install(const Sources &sources, const std::filesystem::path &targetDirectory, bool skipHashChecks, Journal &journal, const std::function<void()> &progressCallback)
bool Installer::install(const Sources &sources, const std::filesystem::path &targetDirectory, bool skipHashChecks, Journal &journal, std::chrono::seconds endWaitTime, const std::function<bool()> &progressCallback)
{
// Install files in reverse order of importance. In case of a process crash or power outage, this will increase the likelihood of the installation
// missing critical files required for the game to run. These files are used as the way to detect if the game is installed.
@@ -497,7 +503,22 @@ bool Installer::install(const Sources &sources, const std::filesystem::path &tar
// Update the progress with the artificial amount attributed to the patching.
journal.progressCounter += PatcherContribution;
progressCallback();
for (uint32_t i = 0; i < 2; i++)
{
if (!progressCallback())
{
journal.lastResult = Journal::Result::Cancelled;
journal.lastErrorMessage = "Installation was cancelled.";
return false;
}
if (i == 0)
{
// Wait the specified amount of time to allow the consumer of the callbacks to animate, halt or cancel the installation for a while after it's finished.
std::this_thread::sleep_for(endWaitTime);
}
}
return true;
}
+3 -2
View File
@@ -22,6 +22,7 @@ struct Journal
enum class Result
{
Success,
Cancelled,
VirtualFileSystemFailed,
DirectoryCreationFailed,
FileMissing,
@@ -75,10 +76,10 @@ struct Installer
static bool checkDLCInstall(const std::filesystem::path &baseDirectory, DLC dlc);
static bool checkAllDLC(const std::filesystem::path &baseDirectory);
static bool computeTotalSize(std::span<const FilePair> filePairs, const uint64_t *fileHashes, VirtualFileSystem &sourceVfs, Journal &journal, uint64_t &totalSize);
static bool copyFiles(std::span<const FilePair> filePairs, const uint64_t *fileHashes, VirtualFileSystem &sourceVfs, const std::filesystem::path &targetDirectory, const std::string &validationFile, bool skipHashChecks, Journal &journal, const std::function<void()> &progressCallback);
static bool copyFiles(std::span<const FilePair> filePairs, const uint64_t *fileHashes, VirtualFileSystem &sourceVfs, const std::filesystem::path &targetDirectory, const std::string &validationFile, bool skipHashChecks, Journal &journal, const std::function<bool()> &progressCallback);
static bool parseContent(const std::filesystem::path &sourcePath, std::unique_ptr<VirtualFileSystem> &targetVfs, Journal &journal);
static bool parseSources(const Input &input, Journal &journal, Sources &sources);
static bool install(const Sources &sources, const std::filesystem::path &targetDirectory, bool skipHashChecks, Journal &journal, const std::function<void()> &progressCallback);
static bool install(const Sources &sources, const std::filesystem::path &targetDirectory, bool skipHashChecks, Journal &journal, std::chrono::seconds endWaitTime, const std::function<bool()> &progressCallback);
static void rollback(Journal &journal);
// Convenience method for checking if the specified file contains the game. This should be used when the user selects the file.
+10 -3
View File
@@ -1,11 +1,12 @@
#include <stdafx.h>
#include "file_system.h"
#include <cpu/guest_thread.h>
#include <kernel/xam.h>
#include <kernel/xdm.h>
#include <kernel/function.h>
#include <cpu/guest_thread.h>
#include <os/logger.h>
#include <mod/mod_loader.h>
#include <os/logger.h>
#include <user/config.h>
#include <stdafx.h>
struct FileHandle : KernelObject
{
@@ -365,8 +366,14 @@ std::filesystem::path FileSystem::ResolvePath(const std::string_view& path, bool
if (checkForMods)
{
std::filesystem::path resolvedPath = ModLoader::ResolvePath(path);
if (!resolvedPath.empty())
{
if (ModLoader::s_isLogTypeConsole)
LOGF_IMPL(Utility, "Mod Loader", "Loading file: \"{}\"", reinterpret_cast<const char*>(resolvedPath.u8string().c_str()));
return resolvedPath;
}
}
thread_local std::string builtPath;
+12
View File
@@ -466,6 +466,18 @@ uint32_t XamInputGetState(uint32_t userIndex, uint32_t flags, XAMINPUT_STATE* st
state->Gamepad.wButtons &= ~hid::g_prohibitedButtons;
if (hid::g_isLeftStickProhibited)
{
state->Gamepad.sThumbLX = 0;
state->Gamepad.sThumbLY = 0;
}
if (hid::g_isRightStickProhibited)
{
state->Gamepad.sThumbRX = 0;
state->Gamepad.sThumbRY = 0;
}
ByteSwapInplace(state->Gamepad.wButtons);
ByteSwapInplace(state->Gamepad.sThumbLX);
ByteSwapInplace(state->Gamepad.sThumbLY);
+2 -2
View File
@@ -10,12 +10,12 @@ namespace xdbf
{
inline std::string& FixInvalidSequences(std::string& str)
{
static std::vector<std::string> invalidSequences =
static std::array<std::string_view, 1> invalidSequences =
{
"\xE2\x80\x99"
};
static std::vector<std::string> replaceSequences =
static std::array<std::string_view, 1> replaceSequences =
{
"'"
};
+633 -50
View File
@@ -15,6 +15,28 @@
360, which is one of the few consoles to have a port of Sonic
Unleashed.
- Ensure your locale is added in the correct order following the language enum.
Correct:
{
{ ELanguage::English, "Example" },
{ ELanguage::Japanese, "Example" },
{ ELanguage::German, "Example" },
{ ELanguage::French, "Example" },
{ ELanguage::Spanish, "Example" },
{ ELanguage::Italian, "Example" }
}
Incorrect:
{
{ ELanguage::English, "Example" },
{ ELanguage::French, "Example" },
{ ELanguage::Spanish, "Example" },
{ ELanguage::German, "Example" },
{ ELanguage::Italian, "Example" },
{ ELanguage::Japanese, "Example" }
}
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
*/
@@ -27,13 +49,14 @@
CONFIG_DEFINE_LOCALE(Language)
{
{ ELanguage::English, { "Language", "Change the language used for text and logos." } },
{ ELanguage::Japanese, { "言語", "[PLACEHOLDER]" } },
{ ELanguage::German, { "Sprache", "[PLACEHOLDER]" } },
{ ELanguage::French, { "Langue", "[PLACEHOLDER]" } },
{ ELanguage::Spanish, { "Idioma", "[PLACEHOLDER]" } },
{ ELanguage::Italian, { "Lingua", "[PLACEHOLDER]" } }
{ ELanguage::Japanese, { "言語", "" } },
{ ELanguage::German, { "Sprache", "" } },
{ ELanguage::French, { "Langue", "" } },
{ ELanguage::Spanish, { "Idioma", "" } },
{ ELanguage::Italian, { "Lingua", "" } }
};
// Notes: do not localise this.
CONFIG_DEFINE_ENUM_LOCALE(ELanguage)
{
{
@@ -97,22 +120,42 @@ CONFIG_DEFINE_ENUM_LOCALE(EVoiceLanguage)
CONFIG_DEFINE_LOCALE(Hints)
{
{ ELanguage::English, { "Hints", "Show hint rings in stages." } }
{ ELanguage::English, { "Hints", "Show hints during gameplay." } },
{ ELanguage::Japanese, { "", "" } },
{ ELanguage::German, { "", "" } },
{ ELanguage::French, { "", "" } },
{ ELanguage::Spanish, { "", "" } },
{ ELanguage::Italian, { "", "" } }
};
CONFIG_DEFINE_LOCALE(ControlTutorial)
{
{ ELanguage::English, { "Control Tutorial", "Show controller hints in stages." } }
{ ELanguage::English, { "Control Tutorial", "Show controller hints during gameplay." } },
{ ELanguage::Japanese, { "", "" } },
{ ELanguage::German, { "", "" } },
{ ELanguage::French, { "", "" } },
{ ELanguage::Spanish, { "", "" } },
{ ELanguage::Italian, { "", "" } }
};
CONFIG_DEFINE_LOCALE(AchievementNotifications)
{
{ ELanguage::English, { "Achievement Notifications", "Show notifications for unlocking achievements.\n\nAchievements will still be rewarded with notifications disabled." } }
{ ELanguage::English, { "Achievement Notifications", "Show notifications for unlocking achievements.\n\nAchievements will still be rewarded with notifications disabled." } },
{ ELanguage::Japanese, { "", "" } },
{ ELanguage::German, { "", "" } },
{ ELanguage::French, { "", "" } },
{ ELanguage::Spanish, { "", "" } },
{ ELanguage::Italian, { "", "" } }
};
CONFIG_DEFINE_LOCALE(TimeOfDayTransition)
{
{ ELanguage::English, { "Time of Day Transition", "Change how the loading screen appears when switching time of day in the hub areas." } }
{ ELanguage::English, { "Time of Day Transition", "Change how the loading screen appears when switching time of day in the hub areas." } },
{ ELanguage::Japanese, { "", "" } },
{ ELanguage::German, { "", "" } },
{ ELanguage::French, { "", "" } },
{ ELanguage::Spanish, { "", "" } },
{ ELanguage::Italian, { "", "" } }
};
CONFIG_DEFINE_ENUM_LOCALE(ETimeOfDayTransition)
@@ -123,12 +166,52 @@ CONFIG_DEFINE_ENUM_LOCALE(ETimeOfDayTransition)
{ ETimeOfDayTransition::Xbox, { "XBOX", "Xbox: the transformation cutscene will play with artificial loading times." } },
{ ETimeOfDayTransition::PlayStation, { "PLAYSTATION", "PlayStation: a spinning medal loading screen will be used instead." } }
}
},
{
ELanguage::Japanese,
{
{ ETimeOfDayTransition::Xbox, { "", "" } },
{ ETimeOfDayTransition::PlayStation, { "", "" } }
}
},
{
ELanguage::German,
{
{ ETimeOfDayTransition::Xbox, { "", "" } },
{ ETimeOfDayTransition::PlayStation, { "", "" } }
}
},
{
ELanguage::French,
{
{ ETimeOfDayTransition::Xbox, { "", "" } },
{ ETimeOfDayTransition::PlayStation, { "", "" } }
}
},
{
ELanguage::Spanish,
{
{ ETimeOfDayTransition::Xbox, { "", "" } },
{ ETimeOfDayTransition::PlayStation, { "", "" } }
}
},
{
ELanguage::Italian,
{
{ ETimeOfDayTransition::Xbox, { "", "" } },
{ ETimeOfDayTransition::PlayStation, { "", "" } }
}
}
};
CONFIG_DEFINE_LOCALE(ControllerIcons)
{
{ ELanguage::English, { "Controller Icons", "Change the icons to match your controller." } }
{ ELanguage::English, { "Controller Icons", "Change the icons to match your controller." } },
{ ELanguage::Japanese, { "", "" } },
{ ELanguage::German, { "", "" } },
{ ELanguage::French, { "", "" } },
{ ELanguage::Spanish, { "", "" } },
{ ELanguage::Italian, { "", "" } }
};
CONFIG_DEFINE_ENUM_LOCALE(EControllerIcons)
@@ -140,17 +223,67 @@ CONFIG_DEFINE_ENUM_LOCALE(EControllerIcons)
{ EControllerIcons::Xbox, { "XBOX", "" } },
{ EControllerIcons::PlayStation, { "PLAYSTATION", "" } }
}
},
{
ELanguage::Japanese,
{
{ EControllerIcons::Auto, { "", "" } },
{ EControllerIcons::Xbox, { "", "" } },
{ EControllerIcons::PlayStation, { "", "" } }
}
},
{
ELanguage::German,
{
{ EControllerIcons::Auto, { "", "" } },
{ EControllerIcons::Xbox, { "", "" } },
{ EControllerIcons::PlayStation, { "", "" } }
}
},
{
ELanguage::French,
{
{ EControllerIcons::Auto, { "", "" } },
{ EControllerIcons::Xbox, { "", "" } },
{ EControllerIcons::PlayStation, { "", "" } }
}
},
{
ELanguage::Spanish,
{
{ EControllerIcons::Auto, { "", "" } },
{ EControllerIcons::Xbox, { "", "" } },
{ EControllerIcons::PlayStation, { "", "" } }
}
},
{
ELanguage::Italian,
{
{ EControllerIcons::Auto, { "", "" } },
{ EControllerIcons::Xbox, { "", "" } },
{ EControllerIcons::PlayStation, { "", "" } }
}
}
};
CONFIG_DEFINE_LOCALE(HorizontalCamera)
{
{ ELanguage::English, { "Horizontal Camera", "Change how the camera moves left and right." } }
{ ELanguage::English, { "Horizontal Camera", "Change how the camera moves left and right." } },
{ ELanguage::Japanese, { "", "" } },
{ ELanguage::German, { "", "" } },
{ ELanguage::French, { "", "" } },
{ ELanguage::Spanish, { "", "" } },
{ ELanguage::Italian, { "", "" } }
};
CONFIG_DEFINE_LOCALE(VerticalCamera)
{
{ ELanguage::English, { "Vertical Camera", "Change how the camera moves up and down." } }
{ ELanguage::English, { "Vertical Camera", "Change how the camera moves up and down." } },
{ ELanguage::Japanese, { "", "" } },
{ ELanguage::German, { "", "" } },
{ ELanguage::French, { "", "" } },
{ ELanguage::Spanish, { "", "" } },
{ ELanguage::Italian, { "", "" } }
};
CONFIG_DEFINE_ENUM_LOCALE(ECameraRotationMode)
@@ -161,42 +294,112 @@ CONFIG_DEFINE_ENUM_LOCALE(ECameraRotationMode)
{ ECameraRotationMode::Normal, { "NORMAL", "" } },
{ ECameraRotationMode::Reverse, { "REVERSE", "" } }
}
},
{
ELanguage::Japanese,
{
{ ECameraRotationMode::Normal, { "", "" } },
{ ECameraRotationMode::Reverse, { "", "" } }
}
},
{
ELanguage::German,
{
{ ECameraRotationMode::Normal, { "", "" } },
{ ECameraRotationMode::Reverse, { "", "" } }
}
},
{
ELanguage::French,
{
{ ECameraRotationMode::Normal, { "", "" } },
{ ECameraRotationMode::Reverse, { "", "" } }
}
},
{
ELanguage::Spanish,
{
{ ECameraRotationMode::Normal, { "", "" } },
{ ECameraRotationMode::Reverse, { "", "" } }
}
},
{
ELanguage::Italian,
{
{ ECameraRotationMode::Normal, { "", "" } },
{ ECameraRotationMode::Reverse, { "", "" } }
}
}
};
CONFIG_DEFINE_LOCALE(Vibration)
{
{ ELanguage::English, { "Vibration", "Toggle controller vibration." } }
{ ELanguage::English, { "Vibration", "Toggle controller vibration." } },
{ ELanguage::Japanese, { "", "" } },
{ ELanguage::German, { "", "" } },
{ ELanguage::French, { "", "" } },
{ ELanguage::Spanish, { "", "" } },
{ ELanguage::Italian, { "", "" } }
};
CONFIG_DEFINE_LOCALE(AllowBackgroundInput)
{
{ ELanguage::English, { "Allow Background Input", "Allow controller input whilst the game window is unfocused." } }
{ ELanguage::English, { "Allow Background Input", "Allow controller input whilst the game window is unfocused." } },
{ ELanguage::Japanese, { "", "" } },
{ ELanguage::German, { "", "" } },
{ ELanguage::French, { "", "" } },
{ ELanguage::Spanish, { "", "" } },
{ ELanguage::Italian, { "", "" } }
};
CONFIG_DEFINE_LOCALE(MasterVolume)
{
{ ELanguage::English, { "Master Volume", "Adjust the overall volume." } }
{ ELanguage::English, { "Master Volume", "Adjust the overall volume." } },
{ ELanguage::Japanese, { "", "" } },
{ ELanguage::German, { "", "" } },
{ ELanguage::French, { "", "" } },
{ ELanguage::Spanish, { "", "" } },
{ ELanguage::Italian, { "", "" } }
};
CONFIG_DEFINE_LOCALE(MusicVolume)
{
{ ELanguage::English, { "Music Volume", "Adjust the volume for the music." } }
{ ELanguage::English, { "Music Volume", "Adjust the volume for the music." } },
{ ELanguage::Japanese, { "", "" } },
{ ELanguage::German, { "", "" } },
{ ELanguage::French, { "", "" } },
{ ELanguage::Spanish, { "", "" } },
{ ELanguage::Italian, { "", "" } }
};
CONFIG_DEFINE_LOCALE(EffectsVolume)
{
{ ELanguage::English, { "Effects Volume", "Adjust the volume for sound effects." } }
{ ELanguage::English, { "Effects Volume", "Adjust the volume for sound effects." } },
{ ELanguage::Japanese, { "", "" } },
{ ELanguage::German, { "", "" } },
{ ELanguage::French, { "", "" } },
{ ELanguage::Spanish, { "", "" } },
{ ELanguage::Italian, { "", "" } }
};
CONFIG_DEFINE_LOCALE(MusicAttenuation)
{
{ ELanguage::English, { "Music Attenuation", "Fade out the game's music when external media is playing." } }
{ ELanguage::English, { "Music Attenuation", "Fade out the game's music when external media is playing." } },
{ ELanguage::Japanese, { "", "" } },
{ ELanguage::German, { "", "" } },
{ ELanguage::French, { "", "" } },
{ ELanguage::Spanish, { "", "" } },
{ ELanguage::Italian, { "", "" } }
};
CONFIG_DEFINE_LOCALE(ChannelConfiguration)
{
{ ELanguage::English, { "Channel Configuration", "" } }
{ ELanguage::English, { "Channel Configuration", "Change the output mode for your audio device." } },
{ ELanguage::Japanese, { "", "" } },
{ ELanguage::German, { "", "" } },
{ ELanguage::French, { "", "" } },
{ ELanguage::Spanish, { "", "" } },
{ ELanguage::Italian, { "", "" } }
};
CONFIG_DEFINE_ENUM_LOCALE(EChannelConfiguration)
@@ -207,37 +410,102 @@ CONFIG_DEFINE_ENUM_LOCALE(EChannelConfiguration)
{ EChannelConfiguration::Stereo, { "STEREO", "" } },
{ EChannelConfiguration::Surround, { "SURROUND", "" } }
}
},
{
ELanguage::Japanese,
{
{ EChannelConfiguration::Stereo, { "", "" } },
{ EChannelConfiguration::Surround, { "", "" } }
}
},
{
ELanguage::German,
{
{ EChannelConfiguration::Stereo, { "", "" } },
{ EChannelConfiguration::Surround, { "", "" } }
}
},
{
ELanguage::French,
{
{ EChannelConfiguration::Stereo, { "", "" } },
{ EChannelConfiguration::Surround, { "", "" } }
}
},
{
ELanguage::Spanish,
{
{ EChannelConfiguration::Stereo, { "", "" } },
{ EChannelConfiguration::Surround, { "", "" } }
}
},
{
ELanguage::Italian,
{
{ EChannelConfiguration::Stereo, { "", "" } },
{ EChannelConfiguration::Surround, { "", "" } }
}
}
};
CONFIG_DEFINE_LOCALE(VoiceLanguage)
{
{ ELanguage::English, { "Voice Language", "Change the language used for character voices." } }
{ ELanguage::English, { "Voice Language", "Change the language used for character voices." } },
{ ELanguage::Japanese, { "", "" } },
{ ELanguage::German, { "", "" } },
{ ELanguage::French, { "", "" } },
{ ELanguage::Spanish, { "", "" } },
{ ELanguage::Italian, { "", "" } }
};
CONFIG_DEFINE_LOCALE(Subtitles)
{
{ ELanguage::English, { "Subtitles", "Show subtitles during dialogue." } }
{ ELanguage::English, { "Subtitles", "Show subtitles during dialogue." } },
{ ELanguage::Japanese, { "", "" } },
{ ELanguage::German, { "", "" } },
{ ELanguage::French, { "", "" } },
{ ELanguage::Spanish, { "", "" } },
{ ELanguage::Italian, { "", "" } }
};
CONFIG_DEFINE_LOCALE(BattleTheme)
{
{ ELanguage::English, { "Battle Theme", "Play the Werehog battle theme during combat.\n\nThis option will apply the next time you're in combat." } }
{ ELanguage::English, { "Battle Theme", "Play the Werehog battle theme during combat.\n\nThis option will apply the next time you're in combat.\n\nExorcism missions and miniboss themes will be unaffected." } },
{ ELanguage::Japanese, { "", "" } },
{ ELanguage::German, { "", "" } },
{ ELanguage::French, { "", "" } },
{ ELanguage::Spanish, { "", "" } },
{ ELanguage::Italian, { "", "" } }
};
CONFIG_DEFINE_LOCALE(WindowSize)
{
{ ELanguage::English, { "Window Size", "Adjust the size of the game window in windowed mode." } }
{ ELanguage::English, { "Window Size", "Adjust the size of the game window in windowed mode." } },
{ ELanguage::Japanese, { "", "" } },
{ ELanguage::German, { "", "" } },
{ ELanguage::French, { "", "" } },
{ ELanguage::Spanish, { "", "" } },
{ ELanguage::Italian, { "", "" } }
};
CONFIG_DEFINE_LOCALE(Monitor)
{
{ ELanguage::English, { "Monitor", "Change which monitor to display the game on." } }
{ ELanguage::English, { "Monitor", "Change which monitor to display the game on." } },
{ ELanguage::Japanese, { "", "" } },
{ ELanguage::German, { "", "" } },
{ ELanguage::French, { "", "" } },
{ ELanguage::Spanish, { "", "" } },
{ ELanguage::Italian, { "", "" } }
};
CONFIG_DEFINE_LOCALE(AspectRatio)
{
{ ELanguage::English, { "Aspect Ratio", "Change the aspect ratio." } }
{ ELanguage::English, { "Aspect Ratio", "Change the aspect ratio." } },
{ ELanguage::Japanese, { "", "" } },
{ ELanguage::German, { "", "" } },
{ ELanguage::French, { "", "" } },
{ ELanguage::Spanish, { "", "" } },
{ ELanguage::Italian, { "", "" } }
};
CONFIG_DEFINE_ENUM_LOCALE(EAspectRatio)
@@ -246,39 +514,116 @@ CONFIG_DEFINE_ENUM_LOCALE(EAspectRatio)
ELanguage::English,
{
{ EAspectRatio::Auto, { "AUTO", "Auto: the aspect ratio will dynamically adjust to the window size." } },
{ EAspectRatio::OriginalNarrow, { "ORIGINAL 4:3", "" } }
{ EAspectRatio::Wide, { "16:9", "16:9: locks the game to a widescreen aspect ratio." } },
{ EAspectRatio::Narrow, { "4:3", "4:3: locks the game to a narrow aspect ratio." } },
{ EAspectRatio::OriginalNarrow, { "ORIGINAL 4:3", "Original 4:3: locks the game to a narrow aspect ratio and retains parity with the game's original implementation." } }
}
},
{
ELanguage::Japanese,
{
{ EAspectRatio::Auto, { "", "" } },
{ EAspectRatio::Wide, { "", "" } },
{ EAspectRatio::Narrow, { "", "" } },
{ EAspectRatio::OriginalNarrow, { "", "" } }
}
},
{
ELanguage::German,
{
{ EAspectRatio::Auto, { "", "" } },
{ EAspectRatio::Wide, { "", "" } },
{ EAspectRatio::Narrow, { "", "" } },
{ EAspectRatio::OriginalNarrow, { "", "" } }
}
},
{
ELanguage::French,
{
{ EAspectRatio::Auto, { "", "" } },
{ EAspectRatio::Wide, { "", "" } },
{ EAspectRatio::Narrow, { "", "" } },
{ EAspectRatio::OriginalNarrow, { "", "" } }
}
},
{
ELanguage::Spanish,
{
{ EAspectRatio::Auto, { "", "" } },
{ EAspectRatio::Wide, { "", "" } },
{ EAspectRatio::Narrow, { "", "" } },
{ EAspectRatio::OriginalNarrow, { "", "" } }
}
},
{
ELanguage::Italian,
{
{ EAspectRatio::Auto, { "", "" } },
{ EAspectRatio::Wide, { "", "" } },
{ EAspectRatio::Narrow, { "", "" } },
{ EAspectRatio::OriginalNarrow, { "", "" } }
}
}
};
CONFIG_DEFINE_LOCALE(ResolutionScale)
{
{ ELanguage::English, { "Resolution Scale", "Adjust the internal resolution of the game.\n\n%dx%d" } }
{ ELanguage::English, { "Resolution Scale", "Adjust the internal resolution of the game.\n\n%dx%d" } },
{ ELanguage::Japanese, { "", "" } },
{ ELanguage::German, { "", "" } },
{ ELanguage::French, { "", "" } },
{ ELanguage::Spanish, { "", "" } },
{ ELanguage::Italian, { "", "" } }
};
CONFIG_DEFINE_LOCALE(Fullscreen)
{
{ ELanguage::English, { "Fullscreen", "Toggle between borderless fullscreen or windowed mode." } }
{ ELanguage::English, { "Fullscreen", "Toggle between borderless fullscreen or windowed mode." } },
{ ELanguage::Japanese, { "", "" } },
{ ELanguage::German, { "", "" } },
{ ELanguage::French, { "", "" } },
{ ELanguage::Spanish, { "", "" } },
{ ELanguage::Italian, { "", "" } }
};
CONFIG_DEFINE_LOCALE(VSync)
{
{ ELanguage::English, { "V-Sync", "Synchronize the game to the refresh rate of the display to prevent screen tearing." } }
{ ELanguage::English, { "V-Sync", "Synchronize the game to the refresh rate of the display to prevent screen tearing." } },
{ ELanguage::Japanese, { "", "" } },
{ ELanguage::German, { "", "" } },
{ ELanguage::French, { "", "" } },
{ ELanguage::Spanish, { "", "" } },
{ ELanguage::Italian, { "", "" } }
};
CONFIG_DEFINE_LOCALE(FPS)
{
{ ELanguage::English, { "FPS", "Set the max frame rate the game can run at.\n\nWARNING: this may introduce glitches at frame rates higher than 60 FPS." } }
{ ELanguage::English, { "FPS", "Set the max frame rate the game can run at.\n\nWARNING: this may introduce glitches at frame rates higher than 60 FPS." } },
{ ELanguage::Japanese, { "", "" } },
{ ELanguage::German, { "", "" } },
{ ELanguage::French, { "", "" } },
{ ELanguage::Spanish, { "", "" } },
{ ELanguage::Italian, { "", "" } }
};
CONFIG_DEFINE_LOCALE(Brightness)
{
{ ELanguage::English, { "Brightness", "Adjust the brightness level of the game." } }
{ ELanguage::English, { "Brightness", "Adjust the brightness level until the symbol on the left is barely visible." } },
{ ELanguage::Japanese, { "", "" } },
{ ELanguage::German, { "", "" } },
{ ELanguage::French, { "", "" } },
{ ELanguage::Spanish, { "", "" } },
{ ELanguage::Italian, { "", "" } }
};
CONFIG_DEFINE_LOCALE(AntiAliasing)
{
{ ELanguage::English, { "Anti-Aliasing", "Adjust the amount of smoothing applied to jagged edges." } }
{ ELanguage::English, { "Anti-Aliasing", "Adjust the amount of smoothing applied to jagged edges." } },
{ ELanguage::Japanese, { "", "" } },
{ ELanguage::German, { "", "" } },
{ ELanguage::French, { "", "" } },
{ ELanguage::Spanish, { "", "" } },
{ ELanguage::Italian, { "", "" } }
};
CONFIG_DEFINE_ENUM_LOCALE(EAntiAliasing)
@@ -286,19 +631,59 @@ CONFIG_DEFINE_ENUM_LOCALE(EAntiAliasing)
{
ELanguage::English,
{
{ EAntiAliasing::None, { "NONE", "" } },
{ EAntiAliasing::None, { "NONE", "" } }
}
},
{
ELanguage::Japanese,
{
{ EAntiAliasing::None, { "", "" } }
}
},
{
ELanguage::German,
{
{ EAntiAliasing::None, { "", "" } }
}
},
{
ELanguage::French,
{
{ EAntiAliasing::None, { "", "" } }
}
},
{
ELanguage::Spanish,
{
{ EAntiAliasing::None, { "", "" } }
}
},
{
ELanguage::Italian,
{
{ EAntiAliasing::None, { "", "" } }
}
}
};
CONFIG_DEFINE_LOCALE(TransparencyAntiAliasing)
{
{ ELanguage::English, { "Transparency Anti-Aliasing", "Apply anti-aliasing to alpha transparent textures." } }
{ ELanguage::English, { "Transparency Anti-Aliasing", "Apply anti-aliasing to alpha transparent textures." } },
{ ELanguage::Japanese, { "", "" } },
{ ELanguage::German, { "", "" } },
{ ELanguage::French, { "", "" } },
{ ELanguage::Spanish, { "", "" } },
{ ELanguage::Italian, { "", "" } }
};
CONFIG_DEFINE_LOCALE(ShadowResolution)
{
{ ELanguage::English, { "Shadow Resolution", "Set the resolution of real-time shadows." } }
{ ELanguage::English, { "Shadow Resolution", "Set the resolution of real-time shadows." } },
{ ELanguage::Japanese, { "", "" } },
{ ELanguage::German, { "", "" } },
{ ELanguage::French, { "", "" } },
{ ELanguage::Spanish, { "", "" } },
{ ELanguage::Italian, { "", "" } }
};
CONFIG_DEFINE_ENUM_LOCALE(EShadowResolution)
@@ -306,16 +691,49 @@ CONFIG_DEFINE_ENUM_LOCALE(EShadowResolution)
{
ELanguage::English,
{
{ EShadowResolution::Original, { "ORIGINAL", "Original: the game will automatically determine the resolution of the shadows." } },
{ EShadowResolution::x4096, { "4096", "High resolutions can degrade performance significantly on lower end hardware." } },
{ EShadowResolution::x8192, { "8192", "High resolutions can degrade performance significantly on lower end hardware." } },
{ EShadowResolution::Original, { "ORIGINAL", "Original: the game will automatically determine the resolution of the shadows." } }
}
},
{
ELanguage::Japanese,
{
{ EShadowResolution::Original, { "", "" } }
}
},
{
ELanguage::German,
{
{ EShadowResolution::Original, { "", "" } }
}
},
{
ELanguage::French,
{
{ EShadowResolution::Original, { "", "" } }
}
},
{
ELanguage::Spanish,
{
{ EShadowResolution::Original, { "", "" } }
}
},
{
ELanguage::Italian,
{
{ EShadowResolution::Original, { "", "" } }
}
}
};
CONFIG_DEFINE_LOCALE(GITextureFiltering)
{
{ ELanguage::English, { "GI Texture Filtering", "Change the quality of the filtering used for global illumination textures." } }
{ ELanguage::English, { "GI Texture Filtering", "Change the quality of the filtering used for global illumination textures." } },
{ ELanguage::Japanese, { "", "" } },
{ ELanguage::German, { "", "" } },
{ ELanguage::French, { "", "" } },
{ ELanguage::Spanish, { "", "" } },
{ ELanguage::Italian, { "", "" } }
};
CONFIG_DEFINE_ENUM_LOCALE(EGITextureFiltering)
@@ -324,14 +742,54 @@ CONFIG_DEFINE_ENUM_LOCALE(EGITextureFiltering)
ELanguage::English,
{
{ EGITextureFiltering::Bilinear, { "BILINEAR", "" } },
{ EGITextureFiltering::Bicubic, { "BICUBIC", "" } },
{ EGITextureFiltering::Bicubic, { "BICUBIC", "" } }
}
},
{
ELanguage::Japanese,
{
{ EGITextureFiltering::Bilinear, { "", "" } },
{ EGITextureFiltering::Bicubic, { "", "" } }
}
},
{
ELanguage::German,
{
{ EGITextureFiltering::Bilinear, { "", "" } },
{ EGITextureFiltering::Bicubic, { "", "" } }
}
},
{
ELanguage::French,
{
{ EGITextureFiltering::Bilinear, { "", "" } },
{ EGITextureFiltering::Bicubic, { "", "" } }
}
},
{
ELanguage::Spanish,
{
{ EGITextureFiltering::Bilinear, { "", "" } },
{ EGITextureFiltering::Bicubic, { "", "" } }
}
},
{
ELanguage::Italian,
{
{ EGITextureFiltering::Bilinear, { "", "" } },
{ EGITextureFiltering::Bicubic, { "", "" } }
}
}
};
CONFIG_DEFINE_LOCALE(MotionBlur)
{
{ ELanguage::English, { "Motion Blur", "Change the quality of the motion blur." } }
{ ELanguage::English, { "Motion Blur", "Change the quality of the motion blur." } },
{ ELanguage::Japanese, { "", "" } },
{ ELanguage::German, { "", "" } },
{ ELanguage::French, { "", "" } },
{ ELanguage::Spanish, { "", "" } },
{ ELanguage::Italian, { "", "" } }
};
CONFIG_DEFINE_ENUM_LOCALE(EMotionBlur)
@@ -343,17 +801,67 @@ CONFIG_DEFINE_ENUM_LOCALE(EMotionBlur)
{ EMotionBlur::Original, { "ORIGINAL", "" } },
{ EMotionBlur::Enhanced, { "ENHANCED", "Enhanced: uses more samples for smoother motion blur at the cost of performance." } }
}
},
{
ELanguage::Japanese,
{
{ EMotionBlur::Off, { "", "" } },
{ EMotionBlur::Original, { "", "" } },
{ EMotionBlur::Enhanced, { "", "" } }
}
},
{
ELanguage::German,
{
{ EMotionBlur::Off, { "", "" } },
{ EMotionBlur::Original, { "", "" } },
{ EMotionBlur::Enhanced, { "", "" } }
}
},
{
ELanguage::French,
{
{ EMotionBlur::Off, { "", "" } },
{ EMotionBlur::Original, { "", "" } },
{ EMotionBlur::Enhanced, { "", "" } }
}
},
{
ELanguage::Spanish,
{
{ EMotionBlur::Off, { "", "" } },
{ EMotionBlur::Original, { "", "" } },
{ EMotionBlur::Enhanced, { "", "" } }
}
},
{
ELanguage::Italian,
{
{ EMotionBlur::Off, { "", "" } },
{ EMotionBlur::Original, { "", "" } },
{ EMotionBlur::Enhanced, { "", "" } }
}
}
};
CONFIG_DEFINE_LOCALE(XboxColorCorrection)
{
{ ELanguage::English, { "Xbox Color Correction", "Use the warm tint from the Xbox version of the game." } }
{ ELanguage::English, { "Xbox Color Correction", "Use the warm tint from the Xbox version of the game." } },
{ ELanguage::Japanese, { "", "" } },
{ ELanguage::German, { "", "" } },
{ ELanguage::French, { "", "" } },
{ ELanguage::Spanish, { "", "" } },
{ ELanguage::Italian, { "", "" } }
};
CONFIG_DEFINE_LOCALE(CutsceneAspectRatio)
{
{ ELanguage::English, { "Cutscene Aspect Ratio", "" } }
{ ELanguage::English, { "Cutscene Aspect Ratio", "Change the aspect ratio of the real-time cutscenes." } },
{ ELanguage::Japanese, { "", "" } },
{ ELanguage::German, { "", "" } },
{ ELanguage::French, { "", "" } },
{ ELanguage::Spanish, { "", "" } },
{ ELanguage::Italian, { "", "" } }
};
CONFIG_DEFINE_ENUM_LOCALE(ECutsceneAspectRatio)
@@ -361,24 +869,99 @@ CONFIG_DEFINE_ENUM_LOCALE(ECutsceneAspectRatio)
{
ELanguage::English,
{
{ ECutsceneAspectRatio::Original, { "ORIGINAL", "" } },
{ ECutsceneAspectRatio::Unlocked, { "UNLOCKED", "" } },
{ ECutsceneAspectRatio::Original, { "ORIGINAL", "Original: locks cutscenes to their original 16:9 aspect ratio." } },
{ ECutsceneAspectRatio::Unlocked, { "UNLOCKED", "Unlocked: allows cutscenes to adjust their aspect ratio to the window size.\n\nWARNING: this will introduce visual oddities past the original 16:9 aspect ratio." } }
}
},
{
ELanguage::Japanese,
{
{ ECutsceneAspectRatio::Original, { "", "" } },
{ ECutsceneAspectRatio::Unlocked, { "", "" } }
}
},
{
ELanguage::German,
{
{ ECutsceneAspectRatio::Original, { "", "" } },
{ ECutsceneAspectRatio::Unlocked, { "", "" } }
}
},
{
ELanguage::French,
{
{ ECutsceneAspectRatio::Original, { "", "" } },
{ ECutsceneAspectRatio::Unlocked, { "", "" } }
}
},
{
ELanguage::Spanish,
{
{ ECutsceneAspectRatio::Original, { "", "" } },
{ ECutsceneAspectRatio::Unlocked, { "", "" } }
}
},
{
ELanguage::Italian,
{
{ ECutsceneAspectRatio::Original, { "", "" } },
{ ECutsceneAspectRatio::Unlocked, { "", "" } }
}
}
};
CONFIG_DEFINE_LOCALE(UIScaleMode)
CONFIG_DEFINE_LOCALE(UIAlignmentMode)
{
{ ELanguage::English, { "UI Scale Mode", "Change how the UI scales to the display." } }
{ ELanguage::English, { "UI Alignment Mode", "Change how the UI aligns with the display." } },
{ ELanguage::Japanese, { "", "" } },
{ ELanguage::German, { "", "" } },
{ ELanguage::French, { "", "" } },
{ ELanguage::Spanish, { "", "" } },
{ ELanguage::Italian, { "", "" } }
};
CONFIG_DEFINE_ENUM_LOCALE(EUIScaleMode)
CONFIG_DEFINE_ENUM_LOCALE(EUIAlignmentMode)
{
{
ELanguage::English,
{
{ EUIScaleMode::Edge, { "EDGE", "Edge: the UI will anchor to the edges of the display." } },
{ EUIScaleMode::Centre, { "CENTER", "Center: the UI will anchor to the center of the display." } },
{ EUIAlignmentMode::Edge, { "EDGE", "Edge: the UI will align with the edges of the display." } },
{ EUIAlignmentMode::Centre, { "CENTER", "Center: the UI will align with the center of the display." } }
}
},
{
ELanguage::Japanese,
{
{ EUIAlignmentMode::Edge, { "", "" } },
{ EUIAlignmentMode::Centre, { "", "" } }
}
},
{
ELanguage::German,
{
{ EUIAlignmentMode::Edge, { "", "" } },
{ EUIAlignmentMode::Centre, { "", "" } }
}
},
{
ELanguage::French,
{
{ EUIAlignmentMode::Edge, { "", "" } },
{ EUIAlignmentMode::Centre, { "", "" } }
}
},
{
ELanguage::Spanish,
{
{ EUIAlignmentMode::Edge, { "", "" } },
{ EUIAlignmentMode::Centre, { "", "" } }
}
},
{
ELanguage::Italian,
{
{ EUIAlignmentMode::Edge, { "", "" } },
{ EUIAlignmentMode::Centre, { "", "" } }
}
}
};
+419 -104
View File
@@ -16,6 +16,28 @@
360, which is one of the few consoles to have a port of Sonic
Unleashed.
- Ensure your locale is added in the correct order following the language enum.
Correct:
{
{ ELanguage::English, "Example" },
{ ELanguage::Japanese, "Example" },
{ ELanguage::German, "Example" },
{ ELanguage::French, "Example" },
{ ELanguage::Spanish, "Example" },
{ ELanguage::Italian, "Example" }
}
Incorrect:
{
{ ELanguage::English, "Example" },
{ ELanguage::French, "Example" },
{ ELanguage::Spanish, "Example" },
{ ELanguage::German, "Example" },
{ ELanguage::Italian, "Example" },
{ ELanguage::Japanese, "Example" }
}
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
*/
@@ -24,327 +46,550 @@ std::unordered_map<std::string, std::unordered_map<ELanguage, std::string>> g_lo
{
"Options_Header_Name",
{
{ ELanguage::English, "OPTIONS" },
{ ELanguage::English, "OPTIONS" },
{ ELanguage::Japanese, "OPTION" },
{ ELanguage::German, "OPTIONEN" },
{ ELanguage::French, "OPTIONS" },
{ ELanguage::Spanish, "OPCIONES" },
{ ELanguage::Italian, "OPZIONI" }
{ ELanguage::German, "OPTIONEN" },
{ ELanguage::French, "OPTIONS" },
{ ELanguage::Spanish, "OPCIONES" },
{ ELanguage::Italian, "OPZIONI" }
}
},
{
"Options_Category_System",
{
{ ELanguage::English, "SYSTEM" }
{ ELanguage::English, "SYSTEM" },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "" }
}
},
{
"Options_Category_Input",
{
{ ELanguage::English, "INPUT" }
{ ELanguage::English, "INPUT" },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "" }
}
},
{
"Options_Category_Audio",
{
{ ELanguage::English, "AUDIO" }
{ ELanguage::English, "AUDIO" },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "" }
}
},
{
"Options_Category_Video",
{
{ ELanguage::English, "VIDEO" }
{ ELanguage::English, "VIDEO" },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "" }
}
},
{
// Notes: integer values in the options menu (e.g. FPS) when at their maximum value.
"Options_Value_Max",
{
{ ELanguage::English, "MAX" }
{ ELanguage::English, "MAX" },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "" }
}
},
{
"Options_Name_WindowSize",
{
{ ELanguage::English, "Window Size" }
{ ELanguage::English, "Window Size" },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "" }
}
},
{
"Options_Desc_WindowSize",
{
{ ELanguage::English, "Adjust the size of the game window in windowed mode." }
{ ELanguage::English, "Adjust the size of the game window in windowed mode." },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "" }
}
},
{
// Notes: description for options that cannot be accessed anywhere but the title screen or world map (e.g. Language).
"Options_Desc_NotAvailable",
{
{ ELanguage::English, "This option is not available at this location." }
{ ELanguage::English, "This option is not available at this location." },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "" }
}
},
{
// Notes: currently the description for Window Size when in fullscreen.
"Options_Desc_NotAvailableFullscreen",
{
{ ELanguage::English, "This option is not available in fullscreen mode." }
{ ELanguage::English, "This option is not available in fullscreen mode." },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "" }
}
},
{
// Notes: currently the description for Monitor when in fullscreen.
"Options_Desc_NotAvailableWindowed",
{
{ ELanguage::English, "This option is not available in windowed mode." }
{ ELanguage::English, "This option is not available in windowed mode." },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "" }
}
},
{
// Notes: currently the description for Monitor when the user only has one display connected.
"Options_Desc_NotAvailableHardware",
{
{ ELanguage::English, "This option is not available with your current hardware configuration." }
{ ELanguage::English, "This option is not available with your current hardware configuration." },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "" }
}
},
{
// Notes: description for Transparency Anti-Aliasing when MSAA is disabled.
"Options_Desc_NotAvailableMSAA",
{
{ ELanguage::English, "This option is not available without MSAA." }
{ ELanguage::English, "This option is not available without MSAA." },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "" }
}
},
{
// Notes: description for Music Attenuation when the user is not running a supported OS.
"Options_Desc_OSNotSupported",
{
{ ELanguage::English, "This option is not supported by your operating system." }
{ ELanguage::English, "This option is not supported by your operating system." },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "" }
}
},
{
// Notes: message appears when changing the Language option and backing out of the options menu.
"Options_Message_Restart",
{
{ ELanguage::English, "The game will now restart." }
{ ELanguage::English, "The game will now restart." },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "" }
}
},
{
// Notes: used for the button guide at the pause menu.
"Achievements_Name",
{
{ ELanguage::English, "Achievements" }
{ ELanguage::English, "Achievements" },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "" }
}
},
{
// Notes: used for the header in the achievements menu.
"Achievements_Name_Uppercase",
{
{ ELanguage::English, "ACHIEVEMENTS" }
{ ELanguage::English, "ACHIEVEMENTS" },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "" }
}
},
{
"Achievements_Unlock",
{
{ ELanguage::English, "Achievement Unlocked!" }
{ ELanguage::English, "Achievement Unlocked!" },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "" }
}
},
{
"Installer_Header_Installer",
{
{ ELanguage::English, "INSTALLER" },
{ ELanguage::Spanish, "INSTALADOR" },
{ ELanguage::Italian, "INSTALLATORE" },
{ ELanguage::English, "INSTALLER" },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "INSTALADOR" },
{ ELanguage::Italian, "INSTALLATORE" },
},
},
{
"Installer_Header_Installing",
{
{ ELanguage::English, "INSTALLING" },
{ ELanguage::Spanish, "INSTALANDO" },
{ ELanguage::Italian, "INSTALLANDO" },
{ ELanguage::English, "INSTALLING" },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "INSTALANDO" },
{ ELanguage::Italian, "INSTALLANDO" },
}
},
{
"Installer_Page_SelectLanguage",
{
{ ELanguage::English, "Please select a language." },
{ ELanguage::Italian, "Seleziona una lingua." }
{ ELanguage::English, "Please select a language." },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "Seleziona una lingua." }
}
},
{
"Installer_Page_Introduction",
{
{ ELanguage::English, "Welcome to\nUnleashed Recompiled!\n\nYou'll need an Xbox 360 copy\nof Sonic Unleashed in order to proceed with the installation." },
{ ELanguage::Italian, "Benvenuto a\nUnleashed Recompiled!\n\nDovrai avere una copia di\nSonic Unleashed per la Xbox 360\nper proseguire con l'installazione." }
{ ELanguage::English, "Welcome to\nUnleashed Recompiled!\n\nYou'll need an Xbox 360 copy\nof Sonic Unleashed in order to proceed with the installation." },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "Benvenuto a\nUnleashed Recompiled!\n\nDovrai avere una copia di\nSonic Unleashed per la Xbox 360\nper proseguire con l'installazione." }
}
},
{
"Installer_Page_SelectGameAndUpdate",
{
{ ELanguage::English, "Add the sources for the game and its title update." }
{ ELanguage::English, "Add the sources for the game and its title update." },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "" }
}
},
{
"Installer_Page_SelectDLC",
{
{ ELanguage::English, "Add the sources for the DLC." }
{ ELanguage::English, "Add the sources for the DLC." },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "" }
}
},
{
"Installer_Page_CheckSpace",
{
{ ELanguage::English, "The content will be installed to the program's folder.\n\nPlease confirm that you have enough free space.\n\n" },
{ ELanguage::Italian, "Il contenuto verrà installato nella cartella di questo programma. Assicurati di avere abbastanza spazio libero sul tuo hard disk.\n\n" }
{ ELanguage::English, "The content will be installed to the program's folder.\n\n" },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "Il contenuto verrà installato nella cartella di questo programma.\n\n" }
}
},
{
"Installer_Page_Installing",
{
{ ELanguage::English, "Please wait while the content is being installed..." },
{ ELanguage::Italian, "Attendi mentre il contenuto viene installato... " }
{ ELanguage::English, "Please wait while the content is being installed..." },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "Attendi mentre il contenuto viene installato... " }
}
},
{
"Installer_Page_InstallSucceeded",
{
{ ELanguage::English, "Installation complete!\nThis project is brought to you by:" }
{ ELanguage::English, "Installation complete!\nThis project is brought to you by:" },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "" }
}
},
{
"Installer_Page_InstallFailed",
{
{ ELanguage::English, "Installation failed.\n\nError:\n\n" },
{ ELanguage::Italian, "Installazione fallita.\n\nErrore:\n\n" }
{ ELanguage::English, "Installation failed.\n\nError:\n\n" },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "Installazione fallita.\n\nErrore:\n\n" }
}
},
{
"Installer_Step_Game",
{
{ ELanguage::English, "GAME" },
{ ELanguage::Italian, "GIOCO" }
{ ELanguage::English, "GAME" },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "GIOCO" }
}
},
{
"Installer_Step_Update",
{
{ ELanguage::English, "UPDATE" },
{ ELanguage::Italian, "AGGIORNAMENTO" }
{ ELanguage::English, "UPDATE" },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "AGGIORNAMENTO" }
}
},
{
"Installer_Step_RequiredSpace",
{
{ ELanguage::English, "Required space: %2.2f GiB" },
{ ELanguage::Italian, "Spazio necessario: %2.2f GiB" }
{ ELanguage::English, "Required space: %2.2f GiB" },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "Spazio necessario: %2.2f GiB" }
}
},
{
"Installer_Step_AvailableSpace",
{
{ ELanguage::English, "Available space: %2.2f GiB" },
{ ELanguage::Italian, "Spazio disponibile: %2.2f GiB" }
{ ELanguage::English, "Available space: %2.2f GiB" },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "Spazio disponibile: %2.2f GiB" }
}
},
{
"Installer_Button_Next",
{
{ ELanguage::English, "NEXT" },
{ ELanguage::Spanish, "SIGUIENTE" },
{ ELanguage::German, "WEITER" },
{ ELanguage::Italian, "CONTINUA" }
{ ELanguage::English, "NEXT" },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "WEITER" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "SIGUIENTE" },
{ ELanguage::Italian, "CONTINUA" }
}
},
{
"Installer_Button_Skip",
{
{ ELanguage::English, "SKIP" },
{ ELanguage::Spanish, "SALTAR" },
{ ELanguage::German, "ÜBERSPRINGEN" },
{ ELanguage::Italian, "SALTA" }
{ ELanguage::English, "SKIP" },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "ÜBERSPRINGEN" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "SALTAR" },
{ ELanguage::Italian, "SALTA" }
}
},
{
"Installer_Button_Retry",
{
{ ELanguage::English, "RETRY" },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "" }
}
},
{
"Installer_Button_AddFiles",
{
{ ELanguage::English, "ADD FILES" },
{ ELanguage::Spanish, "AÑADIR ARCHIVOS" },
{ ELanguage::German, "DATEIEN HINZUFÜGEN" },
{ ELanguage::Italian, "AGGIUNGI FILE" }
{ ELanguage::English, "ADD FILES" },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "DATEIEN HINZUFÜGEN" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "AÑADIR ARCHIVOS" },
{ ELanguage::Italian, "AGGIUNGI FILE" }
}
},
{
"Installer_Button_AddFolder",
{
{ ELanguage::English, "ADD FOLDER" },
{ ELanguage::Spanish, "AÑADIR CARPETA" },
{ ELanguage::German, "ORDNER HINZUFÜGEN" },
{ ELanguage::Italian, "AGGIUNGI CARTELLA" }
{ ELanguage::English, "ADD FOLDER" },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "ORDNER HINZUFÜGEN" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "AÑADIR CARPETA" },
{ ELanguage::Italian, "AGGIUNGI CARTELLA" }
}
},
{
// Notes: message appears when using the "Add Files" option and choosing any file that is not an Xbox 360 game dump.
"Installer_Message_InvalidFilesList",
{
{ ELanguage::English, "The following selected files are invalid:" },
{ ELanguage::Italian, "I seguenti file non sono validi:" }
{ ELanguage::English, "The following selected files are invalid:" },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "I seguenti file non sono validi:" }
}
},
{
// Notes: message appears in the event there are some invalid files after adding the DLC and moving onto the next step.
"Installer_Message_InvalidFiles",
{
{ ELanguage::English, "Some of the files that have\nbeen provided are not valid.\n\nPlease make sure all the\nspecified files are correct\nand try again." },
{ ELanguage::Italian, "Alcuni dei file che sono stati\nselezionati non sono validi.\n\nAssicurati che tutti\ni file sono quelli corretti\ne riprova." }
{ ELanguage::English, "Some of the files that have\nbeen provided are not valid.\n\nPlease make sure all the\nspecified files are correct\nand try again." },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "Alcuni dei file che sono stati\nselezionati non sono validi.\n\nAssicurati che tutti\ni file sono quelli corretti\ne riprova." }
}
},
{
// Notes: message appears when clicking the "Add Files" option for the first time.
"Installer_Message_FilePickerTutorial",
{
{ ELanguage::English, "Select a digital dump.\n\nFor choosing a folder with\nextracted and unmodified\ngame files, use the\n\"Add Folder\" option instead." },
{ ELanguage::English, "Select a digital dump with\ncontent from the game.\n\nThese files can be obtained from\nyour Xbox 360 hard drive by\nfollowing the instructions on\nthe GitHub page.\n\nFor choosing a folder with extracted\nand unmodified game files, use\nthe \"Add Folder\" option instead." },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "" }
}
},
{
// Notes: message appears when clicking the "Add Folder" option for the first time.
"Installer_Message_FolderPickerTutorial",
{
{ ELanguage::English, "Select a folder that contains\nthe unmodified files that have\nbeen extracted from the game.\n\nFor choosing a digital dump,\nuse the\"Add Files\" option instead." },
{ ELanguage::English, "Select a folder that contains the\nunmodified files that have been\nextracted from the game.\n\nThese files can be obtained from\nyour Xbox 360 hard drive by\nfollowing the instructions on\nthe GitHub page.\n\nFor choosing a digital dump,\nuse the\"Add Files\" option instead." },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "" }
}
},
{
// Notes: message appears when providing a title update that does not match the region or version of the game dump.
"Installer_Message_IncompatibleGameData",
{
{ ELanguage::English, "The specified game and\ntitle update are incompatible.\n\nPlease ensure the files are\nfor the same version and\nregion and try again." },
{ ELanguage::Italian, "I file del gioco\ne dell'aggiornamento sono incompatibili.\n\nAssicurati che i file sono\ndella stessa versione\ne regione e riprova." }
{ ELanguage::English, "The specified game and\ntitle update are incompatible.\n\nPlease ensure the files are\nfor the same version and\nregion and try again." },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "I file del gioco\ne dell'aggiornamento sono incompatibili.\n\nAssicurati che i file sono\ndella stessa versione\ne regione e riprova." }
}
},
{
// Notes: message appears when clicking Skip at the DLC step.
"Installer_Message_DLCWarning",
{
{ ELanguage::English, "It is highly recommended\nthat you install all of the\nDLC, as it includes high\nquality lighting textures\nfor the base game.\n\nAre you sure you want to\nskip this step?" },
{ ELanguage::Italian, "Si consiglia di installare\ntutti i DLC, poichè includono\ntexture di illuminazione di qualità migliore.\n\nSei sicuro di voler saltare?" }
{ ELanguage::English, "It is highly recommended\nthat you install all of the\nDLC, as it includes high\nquality lighting textures\nfor the base game.\n\nAre you sure you want to\nskip this step?" },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "Si consiglia di installare\ntutti i DLC, poichè includono\ntexture di illuminazione di qualità migliore.\n\nSei sicuro di voler saltare?" }
}
},
{
// Notes: message appears when choosing the Install option at the title screen when the user is missing DLC content.
"Installer_Message_TitleMissingDLC",
{
{ ELanguage::English, "This will restart the game to\nallow you to install any DLC\nthat you are missing.\n\nInstalling DLC will improve the\nlighting quality across the game.\n\nWould you like to install missing\ncontent?" },
{ ELanguage::Italian, "Questa opzione riavviera il gioco\nper farti installare qualsiasi DLC\nche non hai installato.\n\nL'installazione dei DLC migliorerà la qualità\ndell'illuminazione del gioco.\n\nVuoi installare i DLC mancanti?" }
{ ELanguage::English, "This will restart the game to\nallow you to install any DLC\nthat you are missing.\n\nInstalling DLC will improve the\nlighting quality across the game.\n\nWould you like to install missing\ncontent?" },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "Questa opzione riavviera il gioco\nper farti installare qualsiasi DLC\nche non hai installato.\n\nL'installazione dei DLC migliorerà la qualità\ndell'illuminazione del gioco.\n\nVuoi installare i DLC mancanti?" }
}
},
{
// Notes: message appears when choosing the Install option at the title screen when the user is not missing any content.
"Installer_Message_Title",
{
{ ELanguage::English, "This restarts the game to\nallow you to install any DLC\nthat you may be missing.\n\nYou are not currently\nmissing any DLC.\n\nWould you like to proceed\nanyway?" },
{ ELanguage::Italian, "Questa opzione riavviera il gioco\nper farti installare qualsiasi DLC\nche non hai installato.\n\nHai già installato tutti i DLC.\n\nVuoi procedere comunque?" }
{ ELanguage::English, "This restarts the game to\nallow you to install any DLC\nthat you may be missing.\n\nYou are not currently\nmissing any DLC.\n\nWould you like to proceed\nanyway?" },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "Questa opzione riavviera il gioco\nper farti installare qualsiasi DLC\nche non hai installato.\n\nHai già installato tutti i DLC.\n\nVuoi procedere comunque?" }
}
},
{
// Notes: message appears when user chooses "Quit" on the first available installation screen.
"Installer_Message_Quit",
{
{ ELanguage::English, "Are you sure you want to quit?" },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "" }
}
},
{
// Notes: message appears when user chooses "Cancel" during installation.
"Installer_Message_Cancel",
{
{ ELanguage::English, "Are you sure you want to cancel the installation?" },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "" }
}
},
{
// Notes: message appears when pressing B at the title screen.
"Title_Message_Quit",
{
{ ELanguage::English, "Are you sure you want to quit?" },
{ ELanguage::Italian, "Sei sicuro di voler uscire?" }
{ ELanguage::English, "Are you sure you want to quit?" },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "Sei sicuro di voler uscire?" }
}
},
{
@@ -353,12 +598,12 @@ std::unordered_map<std::string, std::unordered_map<ELanguage, std::string>> g_lo
// Do not localise this unless absolutely necessary, these strings are from the XEX.
"Title_Message_SaveDataCorrupt",
{
{ ELanguage::English, "The save file appears to be\ncorrupted and cannot be loaded." },
{ ELanguage::English, "The save file appears to be\ncorrupted and cannot be loaded." },
{ ELanguage::Japanese, "ゲームデータの読み込みに失敗しました。\nこのまま続けるとゲームデータをセーブすることはできません" },
{ ELanguage::German, "Diese Speicherdatei ist beschädigt\nund kann nicht geladen werden." },
{ ELanguage::French, "Le fichier de sauvegarde semble être\nendommagé et ne peut être chargé." },
{ ELanguage::Spanish, "El archivo parece estar dañado\ny no se puede cargar." },
{ ELanguage::Italian, "I file di salvataggio sembrano danneggiati\ne non possono essere caricati." }
{ ELanguage::German, "Diese Speicherdatei ist beschädigt\nund kann nicht geladen werden." },
{ ELanguage::French, "Le fichier de sauvegarde semble être\nendommagé et ne peut être chargé." },
{ ELanguage::Spanish, "El archivo parece estar dañado\ny no se puede cargar." },
{ ELanguage::Italian, "I file di salvataggio sembrano danneggiati\ne non possono essere caricati." }
}
},
{
@@ -366,7 +611,12 @@ std::unordered_map<std::string, std::unordered_map<ELanguage, std::string>> g_lo
// To make this occur, open the file in any editor and just remove a large chunk of data.
"Title_Message_AchievementDataCorrupt",
{
{ ELanguage::English, "The achievement data appears to be\ncorrupted and cannot be loaded.\n\nProceeding from this point will\nclear your achievement data." }
{ ELanguage::English, "The achievement data appears to be\ncorrupted and cannot be loaded.\n\nProceeding from this point will\nclear your achievement data." },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "" }
}
},
{
@@ -374,19 +624,34 @@ std::unordered_map<std::string, std::unordered_map<ELanguage, std::string>> g_lo
// To make this occur, lock the ACH-DATA file using an external program so that it cannot be accessed by the game.
"Title_Message_AchievementDataIOError",
{
{ ELanguage::English, "The achievement data could not be loaded.\nYour achievements will not be saved." }
{ ELanguage::English, "The achievement data could not be loaded.\nYour achievements will not be saved." },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "" }
}
},
{
"Title_Message_UpdateAvailable",
{
{ ELanguage::English, "An update is available!\n\nWould you like to visit the\nreleases page to download it?" }
{ ELanguage::English, "An update is available!\n\nWould you like to visit the\nreleases page to download it?" },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "" }
}
},
{
"Video_BackendError",
{
{ ELanguage::English, "Unable to create a D3D12 (Windows) or Vulkan backend.\n\nPlease make sure that:\n\n- Your system meets the minimum requirements.\n- Your GPU drivers are up to date.\n- Your operating system is on the latest version available." }
{ ELanguage::English, "Unable to create a D3D12 (Windows) or Vulkan backend.\n\nPlease make sure that:\n\n- Your system meets the minimum requirements.\n- Your GPU drivers are up to date.\n- Your operating system is on the latest version available." },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "" }
}
},
{
@@ -414,50 +679,100 @@ std::unordered_map<std::string, std::unordered_map<ELanguage, std::string>> g_lo
{
"Common_Yes",
{
{ ELanguage::English, "Yes" },
{ ELanguage::Italian, "" }
{ ELanguage::English, "Yes" },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "" }
}
},
{
"Common_No",
{
{ ELanguage::English, "No" },
{ ELanguage::Italian, "No" }
{ ELanguage::English, "No" },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "No" }
}
},
{
"Common_Next",
{
{ ELanguage::English, "Next" },
{ ELanguage::Italian, "Continua" }
{ ELanguage::English, "Next" },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "Continua" }
}
},
{
"Common_Select",
{
{ ELanguage::English, "Select" },
{ ELanguage::Italian, "Seleziona" }
{ ELanguage::English, "Select" },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "Seleziona" }
}
},
{
"Common_Back",
{
{ ELanguage::English, "Back" },
{ ELanguage::Italian, "Indietro" }
{ ELanguage::English, "Back" },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "Indietro" }
}
},
{
"Common_Quit",
{
{ ELanguage::English, "Quit" },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "" }
}
},
{
"Common_Cancel",
{
{ ELanguage::English, "Cancel" },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "" }
}
},
{
"Common_Reset",
{
{ ELanguage::English, "Reset" },
{ ELanguage::Italian, "Ripristina" }
{ ELanguage::English, "Reset" },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "Ripristina" }
}
},
{
"Common_Switch",
{
{ ELanguage::English, "Switch" },
{ ELanguage::Italian, "Cambia" }
{ ELanguage::English, "Switch" },
{ ELanguage::Japanese, "" },
{ ELanguage::German, "" },
{ ELanguage::French, "" },
{ ELanguage::Spanish, "" },
{ ELanguage::Italian, "Cambia" }
}
}
};
+3 -3
View File
@@ -203,12 +203,12 @@ int main(int argc, char *argv[])
if (!Video::CreateHostDevice(sdlVideoDriver))
{
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, GameWindow::GetTitle(), Localise("Video_BackendError").c_str(), GameWindow::s_pWindow);
return 1;
std::_Exit(1);
}
if (!InstallerWizard::Run(GAME_INSTALL_DIRECTORY, isGameInstalled && forceDLCInstaller))
{
return 1;
std::_Exit(0);
}
}
@@ -223,7 +223,7 @@ int main(int argc, char *argv[])
if (!Video::CreateHostDevice(sdlVideoDriver))
{
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, GameWindow::GetTitle(), Localise("Video_BackendError").c_str(), GameWindow::s_pWindow);
return 1;
std::_Exit(1);
}
}
+47
View File
@@ -5,7 +5,9 @@
#include <cpu/guest_stack_var.h>
#include <kernel/function.h>
#include <kernel/heap.h>
#include <user/config.h>
#include <user/paths.h>
#include <os/logger.h>
#include <os/process.h>
#include <xxHashMap.h>
@@ -117,7 +119,10 @@ void ModLoader::Init()
}
if (configIni.getString("CPKREDIR", "LogType", std::string()) == "console")
{
os::process::ShowConsole();
s_isLogTypeConsole = true;
}
std::string modsDbIniFilePathU8 = configIni.getString("CPKREDIR", "ModsDbIni", "");
if (modsDbIniFilePathU8.empty())
@@ -208,6 +213,42 @@ void ModLoader::Init()
if (!mod.includeDirs.empty())
g_mods.emplace_back(std::move(mod));
}
auto codeCount = modsDbIni.get<size_t>("Codes", "CodeCount", 0);
if (codeCount)
{
std::vector<std::string> codes{};
for (size_t i = 0; i < codeCount; i++)
{
auto name = modsDbIni.getString("Codes", fmt::format("Code{}", i), "");
if (name.empty())
continue;
codes.push_back(name);
}
for (auto& def : g_configDefinitions)
{
if (!def->IsHidden() || def->GetSection() != "Codes")
continue;
/* NOTE: this is inefficient, but it happens
once on boot for a handful of codes at release
and is temporary until we support real code mods. */
for (size_t i = 0; i < codes.size(); i++)
{
if (def->GetName() == codes[i])
{
LOGF_IMPL(Utility, "Mod Loader", "Loading code: \"{}\"", codes[i]);
*(bool*)def->GetValue() = true;
break;
}
}
}
}
}
static constexpr uint32_t LZX_SIGNATURE = 0xFF512EE;
@@ -337,6 +378,9 @@ PPC_FUNC(sub_82E0D3E8)
std::ifstream stream(filePath, std::ios::binary);
if (stream.good())
{
if (ModLoader::s_isLogTypeConsole)
LOGF_IMPL(Utility, "Mod Loader", "Loading file: \"{}\"", reinterpret_cast<const char*>(filePath.u8string().c_str()));
be<uint32_t> signature{};
stream.read(reinterpret_cast<char*>(&signature), sizeof(signature));
@@ -572,6 +616,9 @@ PPC_FUNC(sub_82E0B500)
std::ifstream stream(arFilePath, std::ios::binary);
if (stream.good())
{
if (ModLoader::s_isLogTypeConsole)
LOGF_IMPL(Utility, "Mod Loader", "Loading file: \"{}\"", reinterpret_cast<const char*>(arFilePath.u8string().c_str()));
stream.seekg(0, std::ios::end);
size_t arFileSize = stream.tellg();
+2
View File
@@ -2,6 +2,8 @@
struct ModLoader
{
static inline bool s_isLogTypeConsole;
static inline std::filesystem::path s_saveFilePath;
static std::filesystem::path ResolvePath(std::string_view path);
+10 -1
View File
@@ -24,6 +24,9 @@ void CHudPauseAddOptionsItemMidAsmHook(PPCRegister& pThis)
bool InjectMenuBehaviour(uint32_t pThis, uint32_t count)
{
if (App::s_isLoading)
return true;
auto pHudPause = (SWA::CHudPause*)g_memory.Translate(pThis);
auto cursorIndex = *(be<uint32_t>*)g_memory.Translate(4 * (*(be<uint32_t>*)g_memory.Translate(pThis + 0x19C) + 0x68) + pThis);
@@ -116,6 +119,12 @@ bool CHudPauseMiscInjectOptionsMidAsmHook(PPCRegister& pThis)
PPC_FUNC_IMPL(__imp__sub_824B0930);
PPC_FUNC(sub_824B0930)
{
if (App::s_isLoading)
{
__imp__sub_824B0930(ctx, base);
return;
}
auto pHudPause = (SWA::CHudPause*)g_memory.Translate(ctx.r3.u32);
auto pInputState = SWA::CInputState::GetInstance();
@@ -163,7 +172,7 @@ PPC_FUNC(sub_824B0930)
if (*SWA::SGlobals::ms_IsRenderHud && pHudPause->m_IsShown && !pHudPause->m_Submenu && pHudPause->m_Transition == SWA::eTransitionType_Undefined)
{
ButtonGuide::Open(Button(Localise("Achievements_Name"), EButtonIcon::Back, EButtonAlignment::Left, EFontQuality::Low));
ButtonGuide::Open(Button("Achievements_Name", EButtonIcon::Back, EButtonAlignment::Left, EFontQuality::Low));
g_isClosed = false;
}
else if (!g_isClosed)
@@ -148,9 +148,10 @@ void PressStartSaveLoadThreadMidAsmHook()
PPC_FUNC_IMPL(__imp__sub_82587E50);
PPC_FUNC(sub_82587E50)
{
auto isAutoSaveWarningShown = *(bool*)g_memory.Translate(0x83367BC1);
auto pTitleStateIntro = (SWA::CTitleStateIntro*)g_memory.Translate(ctx.r3.u32);
auto pTime = (be<float>*)((uint8_t*)pTitleStateIntro->GetContextBase() + 0x10C);
if (isAutoSaveWarningShown)
if (*SWA::SGlobals::ms_IsAutoSaveWarningShown)
{
__imp__sub_82587E50(ctx, base);
}
@@ -158,7 +159,7 @@ PPC_FUNC(sub_82587E50)
{
if (auto pInputState = SWA::CInputState::GetInstance())
{
if (pInputState->GetPadState().IsTapped(SWA::eKeyState_B))
if (pInputState->GetPadState().IsTapped(SWA::eKeyState_B) && *pTime > 0.5f)
g_quitMessageOpen = true;
}
@@ -1,9 +1,12 @@
#include <api/SWA.h>
#include <cpu/guest_stack_var.h>
#include <locale/locale.h>
#include <os/logger.h>
#include <ui/button_guide.h>
#include <ui/fader.h>
#include <ui/message_window.h>
#include <ui/options_menu.h>
#include <user/achievement_manager.h>
#include <user/paths.h>
#include <app.h>
#include <exports.h>
@@ -49,13 +52,15 @@ static bool ProcessInstallMessage()
PPC_FUNC_IMPL(__imp__sub_825882B8);
PPC_FUNC(sub_825882B8)
{
auto pTitleState = (SWA::CTitleStateBase*)g_memory.Translate(ctx.r3.u32);
auto pTitleStateMenu = (SWA::CTitleStateMenu*)g_memory.Translate(ctx.r3.u32);
auto pGameDocument = SWA::CGameDocument::GetInstance();
auto pInputState = SWA::CInputState::GetInstance();
auto& pPadState = pInputState->GetPadState();
auto isAccepted = pPadState.IsTapped(SWA::eKeyState_A) || pPadState.IsTapped(SWA::eKeyState_Start);
auto pContext = pTitleState->GetContextBase<SWA::CTitleStateBase::CTitleStateContext>();
auto pContext = pTitleStateMenu->GetContextBase<SWA::CTitleStateMenu::CTitleStateMenuContext>();
auto isNewGameIndex = pContext->m_pTitleMenu->m_CursorIndex == 0;
auto isOptionsIndex = pContext->m_pTitleMenu->m_CursorIndex == 2;
auto isInstallIndex = pContext->m_pTitleMenu->m_CursorIndex == 3;
@@ -63,7 +68,17 @@ PPC_FUNC(sub_825882B8)
if (App::s_isSaveDataCorrupt && pContext->m_pTitleMenu->m_CursorIndex == 1)
pContext->m_pTitleMenu->m_CursorIndex = 0;
if (!OptionsMenu::s_isVisible && isOptionsIndex)
if (isNewGameIndex && isAccepted)
{
if (pContext->m_pTitleMenu->m_IsDeleteCheckMessageOpen &&
pGameDocument->m_pMember->m_pGeneralWindow->m_SelectedIndex == 1)
{
LOGN("Resetting achievements...");
AchievementManager::Reset();
}
}
else if (!OptionsMenu::s_isVisible && isOptionsIndex)
{
if (OptionsMenu::s_isRestartRequired)
{
@@ -76,7 +91,6 @@ PPC_FUNC(sub_825882B8)
{
Game_PlaySound("sys_worldmap_window");
Game_PlaySound("sys_worldmap_decide");
OptionsMenu::Open();
}
}
@@ -93,7 +107,6 @@ PPC_FUNC(sub_825882B8)
if (OptionsMenu::CanClose() && pPadState.IsTapped(SWA::eKeyState_B))
{
Game_PlaySound("sys_worldmap_cansel");
OptionsMenu::Close();
}
}
+255 -47
View File
@@ -2,10 +2,13 @@
#include <api/SWA.h>
#include <app.h>
#include <ui/game_window.h>
#include <ui/black_bar.h>
#include <gpu/video.h>
#include <xxHashMap.h>
#include "aspect_ratio_patches.h"
#include "camera_patches.h"
#include "inspire_patches.h"
// These are here for now to not recompile basically all of the project.
namespace Chao::CSD
@@ -271,7 +274,7 @@ PPC_FUNC(sub_8258B558)
ctx.f1.f64 = offsetX + g_aspectRatioNarrowScale * 140.0f;
ctx.f2.f64 = offsetY;
if (Config::UIScaleMode == EUIScaleMode::Edge && g_aspectRatioNarrowScale >= 1.0f)
if (Config::UIAlignmentMode == EUIAlignmentMode::Edge && g_aspectRatioNarrowScale >= 1.0f)
ctx.f1.f64 += g_aspectRatioOffsetX / g_aspectRatioScale;
sub_830BB3D0(ctx, base);
@@ -293,7 +296,7 @@ PPC_FUNC(sub_8258B558)
if (textBox != NULL)
{
float value = 708.0f + g_aspectRatioNarrowScale * 140.0f;
if (Config::UIScaleMode == EUIScaleMode::Edge && g_aspectRatioNarrowScale >= 1.0f)
if (Config::UIAlignmentMode == EUIAlignmentMode::Edge && g_aspectRatioNarrowScale >= 1.0f)
value += g_aspectRatioOffsetX / g_aspectRatioScale;
PPC_STORE_U32(textBox + 0x38, reinterpret_cast<uint32_t&>(value));
@@ -339,6 +342,19 @@ enum
REPEAT_LEFT = 1 << 15,
TORNADO_DEFENSE = 1 << 16,
LOADING_BLACK_BAR_MIN = 1 << 17,
LOADING_BLACK_BAR_MAX = 1 << 18,
UNSTRETCH_HORIZONTAL = 1 << 19,
CORNER_EXTRACT = 1 << 20,
SKIP_INSPIRE = 1 << 21,
CONTROL_TUTORIAL = 1 << 22,
LOADING_ARROW = 1 << 23,
};
struct CsdModifier
@@ -348,17 +364,21 @@ struct CsdModifier
uint32_t cornerIndex{};
};
static const ankerl::unordered_dense::map<XXH64_hash_t, CsdModifier> g_modifiers =
static const xxHashMap<CsdModifier> g_modifiers =
{
// ui_balloon
{ HashStr("ui_balloon/window/bg"), { STRETCH } },
{ HashStr("ui_balloon/window/footer"), { ALIGN_BOTTOM } },
// ui_boss_gauge
{ HashStr("ui_boss_gauge/gauge_bg"), { ALIGN_TOP_RIGHT | SCALE } },
{ HashStr("ui_boss_gauge/gauge_2"), { ALIGN_TOP_RIGHT | SCALE } },
{ HashStr("ui_boss_gauge/gauge_1"), { ALIGN_TOP_RIGHT | SCALE } },
{ HashStr("ui_boss_gauge/gauge_breakpoint"), { ALIGN_TOP_RIGHT | SCALE } },
{ HashStr("ui_boss_gauge/gauge_bg"), { ALIGN_TOP_RIGHT | SCALE | SKIP_INSPIRE} },
{ HashStr("ui_boss_gauge/gauge_2"), { ALIGN_TOP_RIGHT | SCALE | SKIP_INSPIRE} },
{ HashStr("ui_boss_gauge/gauge_1"), { ALIGN_TOP_RIGHT | SCALE | SKIP_INSPIRE} },
{ HashStr("ui_boss_gauge/gauge_breakpoint"), { ALIGN_TOP_RIGHT | SCALE | SKIP_INSPIRE} },
// ui_boss_name
{ HashStr("ui_boss_name/name_so/bg"), { UNSTRETCH_HORIZONTAL } },
{ HashStr("ui_boss_name/name_so/pale"), { UNSTRETCH_HORIZONTAL } },
// ui_exstage
{ HashStr("ui_exstage/shield/L_gauge"), { ALIGN_BOTTOM_LEFT | SCALE } },
@@ -367,10 +387,10 @@ static const ankerl::unordered_dense::map<XXH64_hash_t, CsdModifier> g_modifiers
{ HashStr("ui_exstage/energy/R_gauge"), { ALIGN_BOTTOM_RIGHT | SCALE } },
{ HashStr("ui_exstage/energy/R_gauge_effect"), { ALIGN_BOTTOM_RIGHT | SCALE } },
{ HashStr("ui_exstage/energy/R_gauge_effect_2"), { ALIGN_BOTTOM_RIGHT | SCALE } },
{ HashStr("ui_exstage/hit/hit_counter_bg"), { ALIGN_RIGHT | SCALE } },
{ HashStr("ui_exstage/hit/hit_counter_bg"), { ALIGN_RIGHT | SCALE | OFFSET_SCALE_RIGHT, 986.0f } },
{ HashStr("ui_exstage/hit/hit_counter_bg/C"), { ALIGN_RIGHT | SCALE | EXTEND_RIGHT } },
{ HashStr("ui_exstage/hit/hit_counter_bg/C/L"), { ALIGN_RIGHT | SCALE } },
{ HashStr("ui_exstage/hit/hit_counter_bg/C/R"), { SKIP } },
{ HashStr("ui_exstage/hit/hit_counter_bg/C/L"), { SKIP } }, // L/R are mixed up
{ HashStr("ui_exstage/hit/hit_counter_bg/C/R"), { ALIGN_RIGHT | SCALE | STORE_LEFT_CORNER } },
{ HashStr("ui_exstage/hit/hit_counter_num"), { ALIGN_RIGHT | SCALE } },
// ui_gate
@@ -387,21 +407,29 @@ static const ankerl::unordered_dense::map<XXH64_hash_t, CsdModifier> g_modifiers
// ui_itemresult
{ HashStr("ui_itemresult/footer/result_footer"), { ALIGN_BOTTOM } },
{ HashStr("ui_itemresult/main/iresult_title"), { ALIGN_TOP } },
{ HashStr("ui_itemresult/main/iresult_title"), { ALIGN_TOP | OFFSET_SCALE_LEFT, 688.0f } },
{ HashStr("ui_itemresult/main/iresult_title/title_bg/center"), { ALIGN_TOP | EXTEND_LEFT } },
{ HashStr("ui_itemresult/main/iresult_title/title_bg/center/h_light"), { ALIGN_TOP | EXTEND_LEFT } },
{ HashStr("ui_itemresult/main/iresult_title/title_bg/right"), { ALIGN_TOP | STORE_RIGHT_CORNER } },
{ HashStr("ui_itemresult/main/iresult_title/title_brilliance1"), { ALIGN_TOP | STORE_RIGHT_CORNER | OFFSET_SCALE_LEFT, 632.25775f, 1 } },
{ HashStr("ui_itemresult/main/iresult_title/title_brilliance2"), { ALIGN_TOP | STORE_RIGHT_CORNER | OFFSET_SCALE_LEFT, 830.0f, 2 } },
{ HashStr("ui_itemresult/main/iresult_title/title_brilliance3"), { ALIGN_TOP | STORE_RIGHT_CORNER | OFFSET_SCALE_LEFT, 640.0f, 3 } },
// ui_loading
{ HashStr("ui_loading/bg_1"), { STRETCH } },
{ HashStr("ui_loading/bg_1/arrow"), { STRETCH | LOADING_ARROW } },
{ HashStr("ui_loading/bg_2"), { STRETCH } },
{ HashStr("ui_loading/bg_2/arrow"), { STRETCH | LOADING_ARROW } },
{ HashStr("ui_loading/n_2_d/bg/sky"), { STRETCH } },
{ HashStr("ui_loading/n_2_d/bg/under"), { STRETCH } },
{ HashStr("ui_loading/n_2_d/letterbox/letterbox_under"), { STRETCH } },
{ HashStr("ui_loading/n_2_d/letterbox/letterbox_top"), { STRETCH } },
{ HashStr("ui_loading/n_2_d/letterbox/black_l"), { EXTEND_LEFT | STRETCH_VERTICAL } },
{ HashStr("ui_loading/n_2_d/letterbox/black_r"), { EXTEND_RIGHT | STRETCH_VERTICAL } },
{ HashStr("ui_loading/event_viewer/black/black_top"), { LOADING_BLACK_BAR_MIN } },
{ HashStr("ui_loading/event_viewer/black/black_under"), { LOADING_BLACK_BAR_MAX } },
{ HashStr("ui_loading/pda/pda_frame/L"), { LOADING_BLACK_BAR_MIN } },
{ HashStr("ui_loading/pda/pda_frame/R"), { LOADING_BLACK_BAR_MAX } },
// ui_mediaroom
{ HashStr("ui_mediaroom/header/bg/img_1"), { EXTEND_LEFT } },
@@ -412,10 +440,40 @@ static const ankerl::unordered_dense::map<XXH64_hash_t, CsdModifier> g_modifiers
// ui_missionscreen
{ HashStr("ui_missionscreen/player_count"), { ALIGN_TOP_LEFT | SCALE } },
{ HashStr("ui_missionscreen/time_count"), { ALIGN_TOP_LEFT | SCALE } },
{ HashStr("ui_missionscreen/score_count"), { ALIGN_TOP_LEFT | SCALE } },
{ HashStr("ui_missionscreen/item_count"), { ALIGN_TOP_LEFT | SCALE } },
{ HashStr("ui_missionscreen/time_count/position_S/bg_1"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_missionscreen/time_count/position_S/bg_1/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_missionscreen/time_count/position_S/bg_2"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_missionscreen/time_count/position_S/bg_2/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_missionscreen/time_count/position_L/bg_1"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_missionscreen/time_count/position_L/bg_1/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_missionscreen/time_count/position_L/bg_2"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_missionscreen/time_count/position_L/bg_2/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_missionscreen/laptime_count"), { ALIGN_TOP_LEFT | SCALE } },
{ HashStr("ui_missionscreen/laptime_count/position_S/bg_1"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_missionscreen/laptime_count/position_S/bg_1/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_missionscreen/laptime_count/position_S/bg_2"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_missionscreen/laptime_count/position_S/bg_2/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_missionscreen/laptime_count/position_L/bg_1"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_missionscreen/laptime_count/position_L/bg_1/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_missionscreen/laptime_count/position_L/bg_2"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_missionscreen/laptime_count/position_L/bg_2/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_missionscreen/score_count"), { ALIGN_TOP_LEFT | SCALE } },
{ HashStr("ui_missionscreen/score_count/position_S/bg_1"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_missionscreen/score_count/position_S/bg_1/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_missionscreen/score_count/position_S/bg_2"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_missionscreen/score_count/position_S/bg_2/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_missionscreen/score_count/position_L/bg_1"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_missionscreen/score_count/position_L/bg_1/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_missionscreen/score_count/position_L/bg_2"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_missionscreen/score_count/position_L/bg_2/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_missionscreen/item_count"), { ALIGN_TOP_LEFT | SCALE } },
{ HashStr("ui_missionscreen/item_count/position_L/bg_1"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_missionscreen/item_count/position_L/bg_1/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_missionscreen/item_count/position_L/bg_2"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_missionscreen/item_count/position_L/bg_2/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_missionscreen/lap_count"), { ALIGN_TOP_RIGHT | SCALE } },
{ HashStr("ui_missionscreen/lap_count/position/bar"), { ALIGN_RIGHT | SCALE | EXTEND_RIGHT } },
{ HashStr("ui_missionscreen/lap_count/position/bar/R"), { SKIP } },
// ui_misson
{ HashStr("ui_misson/bg"), { STRETCH } },
@@ -536,9 +594,11 @@ static const ankerl::unordered_dense::map<XXH64_hash_t, CsdModifier> g_modifiers
{ HashStr("ui_playscreen_ev_hit/chance_attack"), { ALIGN_RIGHT | SCALE } },
// ui_playscreen_su
{ HashStr("ui_playscreen_su/su_sonic_gauge"), { ALIGN_BOTTOM_LEFT | SCALE } },
{ HashStr("ui_playscreen_su/gaia_gauge"), { ALIGN_BOTTOM_LEFT | SCALE } },
{ HashStr("ui_playscreen_su/footer"), { ALIGN_BOTTOM_RIGHT | SCALE } },
{ HashStr("ui_playscreen_su/su_sonic_gauge"), { ALIGN_BOTTOM_LEFT | SCALE | OFFSET_SCALE_LEFT, 632.0f } },
{ HashStr("ui_playscreen_su/su_sonic_gauge/position/C/R"), { ALIGN_BOTTOM_LEFT | SCALE | STORE_RIGHT_CORNER } },
{ HashStr("ui_playscreen_su/gaia_gauge"), { ALIGN_BOTTOM_LEFT | SCALE | OFFSET_SCALE_LEFT, 632.0f } },
{ HashStr("ui_playscreen_su/gaia_gauge/position/C/R"), { ALIGN_BOTTOM_LEFT | SCALE | STORE_RIGHT_CORNER } },
{ HashStr("ui_playscreen_su/footer"), { ALIGN_BOTTOM_RIGHT | SCALE | CONTROL_TUTORIAL } },
// ui_prov_playscreen
{ HashStr("ui_prov_playscreen/so_speed_gauge"), { ALIGN_BOTTOM_LEFT | SCALE | TORNADO_DEFENSE } },
@@ -562,37 +622,43 @@ static const ankerl::unordered_dense::map<XXH64_hash_t, CsdModifier> g_modifiers
{ HashStr("ui_result/main/result_title/title_bg/center"), { ALIGN_TOP | EXTEND_LEFT } },
{ HashStr("ui_result/main/result_title/title_bg/center/h_light"), { ALIGN_TOP | EXTEND_LEFT} },
{ HashStr("ui_result/main/result_title/title_bg/right"), { ALIGN_TOP | STORE_RIGHT_CORNER } },
{ HashStr("ui_result/main/result_num_1"), { OFFSET_SCALE_RIGHT, 669.0f } },
{ HashStr("ui_result/main/result_num_1"), { CORNER_EXTRACT } },
{ HashStr("ui_result/main/result_num_1/num_bg"), { OFFSET_SCALE_RIGHT, 669.0f } },
{ HashStr("ui_result/main/result_num_1/num_bg/position_1/center_1"), { EXTEND_RIGHT } },
{ HashStr("ui_result/main/result_num_1/num_bg/position_1/center_1/h_light"), { EXTEND_RIGHT } },
{ HashStr("ui_result/main/result_num_1/num_bg/position_1/center_1/left"), { STORE_LEFT_CORNER } },
{ HashStr("ui_result/main/result_num_1/num_bg/position_1/center_1/right"), { SKIP } },
{ HashStr("ui_result/main/result_num_1/num_bg/position_1/center_1/right/h_light"), { SKIP } },
{ HashStr("ui_result/main/result_num_2"), { OFFSET_SCALE_RIGHT, 669.0f } },
{ HashStr("ui_result/main/result_num_2"), { CORNER_EXTRACT } },
{ HashStr("ui_result/main/result_num_2/num_bg"), { OFFSET_SCALE_RIGHT, 669.0f } },
{ HashStr("ui_result/main/result_num_2/num_bg/position_2/center_1"), { EXTEND_RIGHT } },
{ HashStr("ui_result/main/result_num_2/num_bg/position_2/center_1/h_light"), { EXTEND_RIGHT } },
{ HashStr("ui_result/main/result_num_2/num_bg/position_2/center_1/left"), { STORE_LEFT_CORNER } },
{ HashStr("ui_result/main/result_num_2/num_bg/position_2/center_1/right"), { SKIP } },
{ HashStr("ui_result/main/result_num_2/num_bg/position_2/center_1/right/h_light"), { SKIP } },
{ HashStr("ui_result/main/result_num_3"), { OFFSET_SCALE_RIGHT, 669.0f } },
{ HashStr("ui_result/main/result_num_3"), { CORNER_EXTRACT } },
{ HashStr("ui_result/main/result_num_3/num_bg"), { OFFSET_SCALE_RIGHT, 669.0f } },
{ HashStr("ui_result/main/result_num_3/num_bg/position_3/center_1"), { EXTEND_RIGHT } },
{ HashStr("ui_result/main/result_num_3/num_bg/position_3/center_1/h_light"), { EXTEND_RIGHT } },
{ HashStr("ui_result/main/result_num_3/num_bg/position_3/center_1/left"), { STORE_LEFT_CORNER } },
{ HashStr("ui_result/main/result_num_3/num_bg/position_3/center_1/right"), { SKIP } },
{ HashStr("ui_result/main/result_num_3/num_bg/position_3/center_1/right/h_light"), { SKIP } },
{ HashStr("ui_result/main/result_num_4"), { OFFSET_SCALE_RIGHT, 669.0f } },
{ HashStr("ui_result/main/result_num_4"), { CORNER_EXTRACT } },
{ HashStr("ui_result/main/result_num_4/num_bg"), { OFFSET_SCALE_RIGHT, 669.0f } },
{ HashStr("ui_result/main/result_num_4/num_bg/position_4/center_1"), { EXTEND_RIGHT } },
{ HashStr("ui_result/main/result_num_4/num_bg/position_4/center_1/h_light"), { EXTEND_RIGHT } },
{ HashStr("ui_result/main/result_num_4/num_bg/position_4/center_1/left"), { STORE_LEFT_CORNER } },
{ HashStr("ui_result/main/result_num_4/num_bg/position_4/center_1/right"), { SKIP } },
{ HashStr("ui_result/main/result_num_4/num_bg/position_4/center_1/right/h_light"), { SKIP } },
{ HashStr("ui_result/main/result_num_5"), { OFFSET_SCALE_RIGHT, 669.0f } },
{ HashStr("ui_result/main/result_num_5"), { CORNER_EXTRACT } },
{ HashStr("ui_result/main/result_num_5/num_bg"), { OFFSET_SCALE_RIGHT, 669.0f } },
{ HashStr("ui_result/main/result_num_5/num_bg/position_5/center_1"), { EXTEND_RIGHT } },
{ HashStr("ui_result/main/result_num_5/num_bg/position_5/center_1/h_light"), { EXTEND_RIGHT } },
{ HashStr("ui_result/main/result_num_5/num_bg/position_5/center_1/left"), { STORE_LEFT_CORNER } },
{ HashStr("ui_result/main/result_num_5/num_bg/position_5/center_1/right"), { SKIP } },
{ HashStr("ui_result/main/result_num_5/num_bg/position_5/center_1/right/h_light"), { SKIP } },
{ HashStr("ui_result/main/result_num_6"), { OFFSET_SCALE_LEFT, 1094.0f } },
{ HashStr("ui_result/main/result_num_6"), { CORNER_EXTRACT } },
{ HashStr("ui_result/main/result_num_6/num_bg"), { OFFSET_SCALE_LEFT, 1094.0f } },
{ HashStr("ui_result/main/result_num_6/num_bg/position_6/center"), { EXTEND_LEFT } },
{ HashStr("ui_result/main/result_num_6/num_bg/position_6/center/h_light"), { EXTEND_LEFT } },
{ HashStr("ui_result/main/result_num_6/num_bg/position_6/center/right"), { STORE_RIGHT_CORNER } },
@@ -689,7 +755,7 @@ static const ankerl::unordered_dense::map<XXH64_hash_t, CsdModifier> g_modifiers
{ HashStr("ui_townscreen/time_effect"), { ALIGN_TOP_RIGHT | SCALE } },
{ HashStr("ui_townscreen/info"), { ALIGN_TOP_LEFT | SCALE } },
{ HashStr("ui_townscreen/cam"), { ALIGN_TOP_RIGHT | SCALE } },
{ HashStr("ui_townscreen/footer"), { ALIGN_BOTTOM } },
{ HashStr("ui_townscreen/footer"), { ALIGN_BOTTOM_RIGHT | SCALE } },
// ui_worldmap
{ HashStr("ui_worldmap/contents/choices/cts_choices_bg"), { STRETCH } },
@@ -789,6 +855,11 @@ PPC_FUNC(sub_830C6A00)
if (g_sceneModifier.has_value())
{
if (!Config::ControlTutorial && (g_sceneModifier->flags & CONTROL_TUTORIAL) != 0)
{
return;
}
// Tornado Defense bugs out when applying gameplay UI scaling.
// This seems consistent with base game behavior, because the UI
// is normally squashed, which was probably done to work around this.
@@ -798,7 +869,7 @@ PPC_FUNC(sub_830C6A00)
g_scenePositionY = 0.0f;
}
if ((g_sceneModifier->flags & (OFFSET_SCALE_LEFT | OFFSET_SCALE_RIGHT)) != 0)
if (g_aspectRatio > WIDE_ASPECT_RATIO && (g_sceneModifier->flags & (OFFSET_SCALE_LEFT | OFFSET_SCALE_RIGHT | CORNER_EXTRACT)) != 0)
{
auto r3 = ctx.r3;
auto r4 = ctx.r4;
@@ -867,6 +938,12 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str
return;
}
// That goddamn boss gauge doesn't disappear in the cutscene where Dark Gaia and Chip hug each other
if ((modifier.flags & SKIP_INSPIRE) != 0 && !InspirePatches::s_sceneName.empty() && *reinterpret_cast<be<float>*>(base + ctx.r4.u32) >= 1280.0f)
{
return;
}
if (g_cornerExtract)
{
if ((modifier.flags & (STORE_LEFT_CORNER | STORE_RIGHT_CORNER)) != 0)
@@ -878,7 +955,7 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str
return;
}
if (Config::UIScaleMode == EUIScaleMode::Centre)
if (Config::UIAlignmentMode == EUIAlignmentMode::Centre)
{
if (g_aspectRatio >= WIDE_ASPECT_RATIO)
modifier.flags &= ~(ALIGN_LEFT | ALIGN_RIGHT);
@@ -898,6 +975,22 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str
return reinterpret_cast<be<float>*>(stack + index * stride);
};
// Subtract half a pixel from loading arrows to prevent transparent pixels surrounding them from leaking into the texture filtering.
if (Video::s_viewportHeight > 720 && (modifier.flags & LOADING_ARROW) != 0 && stride == 0x14)
{
for (size_t i = 0; i < 4; i++)
{
auto texCoord = getPosition(i) + 4;
constexpr float OFFSET = 0.5f / 720.0f;
if (i == 0 || i == 2) // Top
*texCoord = (*texCoord + OFFSET);
else // Bottom
*texCoord = (*texCoord - OFFSET);
}
}
float offsetX = 0.0f;
float offsetY = 0.0f;
float pivotX = 0.0f;
@@ -905,7 +998,9 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str
float scaleX = 1.0f;
float scaleY = 1.0f;
if (squash || ((modifier.flags & STRETCH_HORIZONTAL) != 0 && g_aspectRatio >= WIDE_ASPECT_RATIO))
bool needsStretch = g_aspectRatio >= WIDE_ASPECT_RATIO;
if (squash || (needsStretch && (modifier.flags & STRETCH_HORIZONTAL) != 0))
{
scaleX = Video::s_viewportWidth / 1280.0f;
}
@@ -913,28 +1008,36 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str
{
scaleX = g_aspectRatioScale;
if ((modifier.flags & ALIGN_RIGHT) != 0)
offsetX = g_aspectRatioOffsetX * 2.0f;
else if ((modifier.flags & ALIGN_LEFT) == 0)
offsetX = g_aspectRatioOffsetX;
if ((modifier.flags & SCALE) != 0)
if (needsStretch && (modifier.flags & UNSTRETCH_HORIZONTAL) != 0)
{
scaleX *= g_aspectRatioGameplayScale;
pivotX = g_scenePositionX;
if ((modifier.flags & ALIGN_RIGHT) != 0)
offsetX += 1280.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale;
else if ((modifier.flags & ALIGN_LEFT) == 0)
offsetX += 640.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale;
offsetX += pivotX * g_aspectRatioScale;
pivotX = *getPosition(0);
offsetX = pivotX * Video::s_viewportWidth / 1280.0f;
}
if ((modifier.flags & WORLD_MAP) != 0)
else
{
if ((modifier.flags & ALIGN_LEFT) != 0)
offsetX += (1.0f - g_aspectRatioNarrowScale) * g_aspectRatioScale * -20.0f;
if ((modifier.flags & ALIGN_RIGHT) != 0)
offsetX = g_aspectRatioOffsetX * 2.0f;
else if ((modifier.flags & ALIGN_LEFT) == 0)
offsetX = g_aspectRatioOffsetX;
if ((modifier.flags & SCALE) != 0)
{
scaleX *= g_aspectRatioGameplayScale;
pivotX = g_scenePositionX;
if ((modifier.flags & ALIGN_RIGHT) != 0)
offsetX += 1280.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale;
else if ((modifier.flags & ALIGN_LEFT) == 0)
offsetX += 640.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale;
offsetX += pivotX * g_aspectRatioScale;
}
if ((modifier.flags & WORLD_MAP) != 0)
{
if ((modifier.flags & ALIGN_LEFT) != 0)
offsetX += (1.0f - g_aspectRatioNarrowScale) * g_aspectRatioScale * -20.0f;
}
}
}
@@ -1021,6 +1124,18 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str
position[1] = round(y);
}
if ((modifier.flags & LOADING_BLACK_BAR_MIN) != 0)
{
auto position = getPosition(0);
BlackBar::g_loadingBlackBarMin = ImVec2{ position[0], position[1] };
BlackBar::g_loadingBlackBarAlpha = *(base + ctx.r1.u32 + 0xB);
}
else if ((modifier.flags & LOADING_BLACK_BAR_MAX) != 0)
{
auto position = getPosition(3);
BlackBar::g_loadingBlackBarMax = ImVec2{ position[0], position[1] };
}
if ((modifier.flags & REPEAT_LEFT) != 0)
{
float width = *getPosition(2) - *getPosition(0);
@@ -1223,12 +1338,12 @@ static double ComputeObjGetItemX(uint32_t type)
double scaleOffset = (1280.0 * (1.0 - g_aspectRatioGameplayScale)) * g_aspectRatioScale;
if (Config::UIScaleMode == EUIScaleMode::Edge)
if (Config::UIAlignmentMode == EUIAlignmentMode::Edge)
{
if (type != 47) // Medal
x += g_aspectRatioOffsetX * 2.0 + scaleOffset;
}
else if (Config::UIScaleMode == EUIScaleMode::Centre)
else if (Config::UIAlignmentMode == EUIAlignmentMode::Centre)
{
x += g_aspectRatioOffsetX + scaleOffset;
}
@@ -1362,6 +1477,12 @@ PPC_FUNC(sub_82B8AA40)
// Restore the original letterbox value.
PPC_STORE_U8(r3.u32, letterbox);
if (letterbox)
{
// Would be nice to also push this as a 2D primitive but I really cannot be bothered right now...
BlackBar::g_inspirePillarbox = Config::CutsceneAspectRatio != ECutsceneAspectRatio::Unlocked && g_aspectRatio > WIDE_ASPECT_RATIO;
}
}
void InspireLetterboxTopMidAsmHook(PPCRegister& r3)
@@ -1444,3 +1565,90 @@ void YggdrasillRenderQuadMidAsmHook(PPCRegister& r3, PPCRegister& r6)
}
}
}
// Explicit CSD set position calls don't seem to care about the
// viewport size. This causes them to appear shifted by 1.5x,
// as the backbuffer resolution is 640x480 at 4:3. We need to account
// for this manually to make the positioning match with the original game.
static constexpr uint32_t EVIL_HUD_GUIDE_BYTE_SIZE = 0x154;
void EvilHudGuideAllocMidAsmHook(PPCRegister& r3)
{
r3.u32 += sizeof(float);
}
// SWA::Player::CEvilHudGuide::CEvilHudGuide
PPC_FUNC_IMPL(__imp__sub_82448CF0);
PPC_FUNC(sub_82448CF0)
{
*reinterpret_cast<float*>(base + ctx.r3.u32 + EVIL_HUD_GUIDE_BYTE_SIZE) = 0.0f;
__imp__sub_82448CF0(ctx, base);
}
void EvilHudGuideUpdateMidAsmHook(PPCRegister& r30, PPCRegister& f30)
{
*reinterpret_cast<float*>(g_memory.base + r30.u32 + EVIL_HUD_GUIDE_BYTE_SIZE) = f30.f64;
}
// SWA::Player::CEvilHudGuide::Update
PPC_FUNC_IMPL(__imp__sub_82449088);
PPC_FUNC(sub_82449088)
{
auto r3 = ctx.r3;
__imp__sub_82449088(ctx, base);
float positionX = *reinterpret_cast<float*>(base + r3.u32 + EVIL_HUD_GUIDE_BYTE_SIZE);
constexpr uint32_t OFFSETS[] = { 312, 320 };
for (const auto offset : OFFSETS)
{
uint32_t scene = PPC_LOAD_U32(r3.u32 + offset + 0x4);
if (scene != NULL)
{
scene = PPC_LOAD_U32(scene + 0x4);
if (scene != NULL)
{
ctx.r3.u32 = scene;
ctx.f1.f64 = (1.5 - 0.5 * g_aspectRatioNarrowScale) * positionX;
ctx.f2.f64 = 0.0;
sub_830BB3D0(ctx, base);
}
}
}
}
// The shadow offseting is buggy for FCO text just like Werehog button guide,
// making them appear thicker than they actually are.
PPC_FUNC_IMPL(__imp__sub_82E54950);
PPC_FUNC(sub_82E54950)
{
if (Config::AspectRatio == EAspectRatio::OriginalNarrow)
{
// Luckily, they have shadow offset scale values that are only used in this function.
uint32_t x = PPC_LOAD_U32(0x8332B7B8);
uint32_t y = PPC_LOAD_U32(0x8332B7BC);
PPCRegister scaled;
// X
scaled.u32 = x;
scaled.f32 *= 1.5f;
PPC_STORE_U32(0x8332B7B8, scaled.u32);
// Y
scaled.u32 = y;
scaled.f32 *= 1.5f;
PPC_STORE_U32(0x8332B7BC, scaled.u32);
__imp__sub_82E54950(ctx, base);
// Restore old values.
PPC_STORE_U32(0x8332B7B8, x);
PPC_STORE_U32(0x8332B7BC, y);
}
else
{
__imp__sub_82E54950(ctx, base);
}
}
+71 -4
View File
@@ -28,6 +28,17 @@ void HighFrameRateDeltaTimeFixMidAsmHook(PPCRegister& f1)
f1.f64 = threshold;
}
// This hook expects the vector register to store delta time at the first index.
void HighFrameRateDeltaTimeFixVectorMidAsmHook(PPCVRegister& v62)
{
// Having 60 FPS threshold ensures we still retain
// the original game behavior when locked to 30/60 FPS.
constexpr double threshold = 1.0 / 60.0;
if (v62.f32[3] < threshold)
v62.f32[3] = threshold;
}
void CameraDeltaTimeFixMidAsmHook(PPCRegister& dest, PPCRegister& src)
{
dest.f64 = src.f64 / 30.0;
@@ -115,10 +126,7 @@ PPC_FUNC(sub_8312DBF8)
constexpr auto INTERVAL = 1000000000ns / 60;
auto next = now + (INTERVAL - now.time_since_epoch() % INTERVAL);
std::this_thread::sleep_for(std::chrono::floor<std::chrono::milliseconds>(next - now - 1ms));
while (std::chrono::steady_clock::now() < next)
std::this_thread::yield();
std::this_thread::sleep_until(next);
}
void WaitVsyncMidAsmHook()
@@ -128,3 +136,62 @@ void WaitVsyncMidAsmHook()
void ApplicationFrameLimiterMidAsmHook()
{
}
// Tornado Defense boss increments timers without respecting delta time.
// We run the update function with a 30 FPS time step to ensure all timers update at the correct rate.
static constexpr size_t EX_STAGE_BOSS_STATE_BATTLE_SIZE = 0x70;
void CExStageBossCStateBattleAllocMidAsmHook(PPCRegister& r3)
{
r3.u32 += sizeof(float);
}
void CExStageBossCStateBattleCtorMidAsmHook(PPCRegister& r3)
{
new (g_memory.base + r3.u32 + EX_STAGE_BOSS_STATE_BATTLE_SIZE) float(0);
}
// SWA::CExStageBoss::CStateBattle::Update
PPC_FUNC_IMPL(__imp__sub_82B00D00);
PPC_FUNC(sub_82B00D00)
{
constexpr auto referenceDeltaTime = 1.0f / 30.0f;
constexpr auto deltaTimeTolerance = 0.0001f;
auto pElapsedTime = (float*)(base + ctx.r3.u32 + EX_STAGE_BOSS_STATE_BATTLE_SIZE);
*pElapsedTime += std::min(App::s_deltaTime, 1.0 / 15.0);
if ((*pElapsedTime + deltaTimeTolerance) > referenceDeltaTime)
{
__imp__sub_82B00D00(ctx, base);
*pElapsedTime -= referenceDeltaTime;
}
*pElapsedTime = std::max(*pElapsedTime, 0.0f);
}
// Fix for Egg Dragoon's drill missile attack rotating 90 degrees at HFR.
void BossEggDragoonDrillMissileCMissileSetRotationMidAsmHook(PPCRegister& r4)
{
auto pRotation = (Hedgehog::Math::CQuaternion*)g_memory.Translate(r4.u32);
auto magnitude = std::sqrt(pRotation->X * pRotation->X + pRotation->Y * pRotation->Y + pRotation->Z * pRotation->Z + pRotation->W * pRotation->W);
if (magnitude < 0.0f)
return;
auto magnitudeNrm = 1.0f / magnitude;
pRotation->X = pRotation->X * magnitudeNrm;
pRotation->Y = pRotation->Y * magnitudeNrm;
pRotation->Z = pRotation->Z * magnitudeNrm;
pRotation->W = pRotation->W * magnitudeNrm;
}
bool SparkleLocusMidAsmHook()
{
// There is an epsilon check in sparkle locus particle code that seems to never pass at high frame rates, which causes vertex corruption.
// Checking for equality doesn't fix it either, so we can fix it by forcing it to always execute instead.
// This has the side effect of the locus particle eventually snapping to the rest position during pause, but it's better than vertices exploding.
return App::s_deltaTime < (1.0 / 60.0);
}
@@ -9,10 +9,10 @@ static class FrontendListener : public SDLEventListener
bool m_isF8KeyDown = false;
public:
void OnSDLEvent(SDL_Event* event) override
bool OnSDLEvent(SDL_Event* event) override
{
if (!Config::HUDToggleHotkey || OptionsMenu::s_isVisible)
return;
if (!Config::HUDToggleKey || OptionsMenu::s_isVisible)
return false;
switch (event->type)
{
@@ -34,6 +34,8 @@ public:
m_isF8KeyDown = event->key.keysym.sym != SDLK_F8;
break;
}
return false;
}
}
g_frontendListener;
+4 -2
View File
@@ -121,10 +121,10 @@ public:
}
}
void OnSDLEvent(SDL_Event* event) override
bool OnSDLEvent(SDL_Event* event) override
{
if (!hid::IsInputAllowed())
return;
return false;
switch (event->type)
{
@@ -202,6 +202,8 @@ public:
ms_touchpadFingerCount--;
break;
}
return false;
}
}
g_sdlEventListenerForInputPatches;
+39 -7
View File
@@ -5,8 +5,9 @@
#include <app.h>
#include <sdl_events.h>
std::string InspirePatches::s_sceneName;
static SWA::Inspire::CScene* g_pScene;
static std::string g_sceneName;
static bool g_isFirstFrameChecked;
static uint32_t g_eventDispatchCount;
@@ -31,6 +32,11 @@ static std::unordered_map<std::string_view, std::pair<float, float>> g_evilSonic
{ "evrt_m8_04", { 0, 2314 } } // Dark Gaia Appears
};
// Sonic's mouth EXPLODES for a single frame in Temple Entrance cutscene.
// Looks very nasty. Let's hide morph models inbetween certain frames to solve it.
static bool g_loadedMouthExplosionAnimation;
static bool g_hideMorphModels;
// SWA::Inspire::CScene
PPC_FUNC_IMPL(__imp__sub_82B98D80);
PPC_FUNC(sub_82B98D80)
@@ -49,9 +55,12 @@ PPC_FUNC(sub_82B98D30)
__imp__sub_82B98D30(ctx, base);
g_pScene = nullptr;
g_sceneName.clear();
InspirePatches::s_sceneName.clear();
SDL_User_EvilSonic(App::s_isWerehog);
g_loadedMouthExplosionAnimation = false;
g_hideMorphModels = false;
}
PPC_FUNC_IMPL(__imp__sub_82B9BA98);
@@ -59,11 +68,31 @@ PPC_FUNC(sub_82B9BA98)
{
auto sceneName = (Hedgehog::Base::CSharedString*)g_memory.Translate(ctx.r5.u32);
g_sceneName = sceneName->c_str();
InspirePatches::s_sceneName = sceneName->c_str();
__imp__sub_82B9BA98(ctx, base);
}
void AnimationDataMakeMidAsmHook(PPCRegister& r31, PPCRegister& r29, PPCRegister& r28)
{
uint8_t* base = g_memory.base;
if (r28.u32 == 0x222E0 &&
strcmp(reinterpret_cast<const char*>(base + PPC_LOAD_U32(r31.u32)), "t0_04_SN") == 0 &&
XXH3_64bits(base + r29.u32, r28.u32) == 0xEC634F0F379F478A)
{
g_loadedMouthExplosionAnimation = true;
}
}
// Hedgehog::Mirage::CSingleMorphElement::Render
PPC_FUNC_IMPL(__imp__sub_82E32048);
PPC_FUNC(sub_82E32048)
{
if (!g_hideMorphModels)
__imp__sub_82E32048(ctx, base);
}
void InspirePatches::DrawDebug()
{
if (!g_pScene)
@@ -72,7 +101,7 @@ void InspirePatches::DrawDebug()
return;
}
ImGui::Text("Name: %s", g_sceneName.c_str());
ImGui::Text("Name: %s", InspirePatches::s_sceneName.c_str());
ImGui::Text("Frame: %f", g_pScene->m_pData->Frame.get());
ImGui::Text("Cut: %d", g_pScene->m_pData->Cut.get());
@@ -97,17 +126,20 @@ void InspirePatches::DrawDebug()
void InspirePatches::Update()
{
if (!g_pScene || !g_sceneName.size())
if (!g_pScene || !InspirePatches::s_sceneName.size())
return;
if (!g_isFirstFrameChecked && std::find(g_alwaysEvilSonic.begin(), g_alwaysEvilSonic.end(), g_sceneName) != g_alwaysEvilSonic.end())
g_hideMorphModels = g_loadedMouthExplosionAnimation && g_pScene->m_pData->Frame >= 185.0f &&
g_pScene->m_pData->Frame < 195.0f && InspirePatches::s_sceneName == "evrt_t0_04";
if (!g_isFirstFrameChecked && std::find(g_alwaysEvilSonic.begin(), g_alwaysEvilSonic.end(), InspirePatches::s_sceneName) != g_alwaysEvilSonic.end())
{
SDL_User_EvilSonic(true);
g_isFirstFrameChecked = true;
return;
}
auto findResult = g_evilSonicTimings.find(g_sceneName);
auto findResult = g_evilSonicTimings.find(InspirePatches::s_sceneName);
if (findResult != g_evilSonicTimings.end())
{
@@ -3,6 +3,8 @@
class InspirePatches
{
public:
static std::string s_sceneName;
static void DrawDebug();
static void Update();
};
+11 -7
View File
@@ -13,6 +13,17 @@ bool DisableHintsMidAsmHook()
return !Config::Hints;
}
// Disable Perfect Dark Gaia hints.
PPC_FUNC_IMPL(__imp__sub_82AC36E0);
PPC_FUNC(sub_82AC36E0)
{
auto pPerfectDarkGaiaChipHintName = (xpointer<char>*)g_memory.Translate(0x8338EF10);
strcpy(pPerfectDarkGaiaChipHintName->get(), Config::Hints ? "V_CHP_067\0" : "end\0");
__imp__sub_82AC36E0(ctx, base);
}
bool DisableControlTutorialMidAsmHook()
{
return !Config::ControlTutorial;
@@ -32,13 +43,6 @@ bool DisableDLCIconMidAsmHook()
return Config::DisableDLCIcon;
}
void ToggleSubtitlesMidAsmHook(PPCRegister& r27)
{
auto pApplicationDocument = (SWA::CApplicationDocument*)g_memory.Translate(r27.u32);
pApplicationDocument->m_InspireSubtitles = Config::Subtitles;
}
void WerehogBattleMusicMidAsmHook(PPCRegister& r11)
{
if (Config::BattleTheme)
+4 -3
View File
@@ -1,8 +1,9 @@
#include <api/SWA.h>
#include <user/config.h>
#include <SWA/CharacterUtility/CharacterProxy.h>
#include <hid/hid.h>
#include <app.h>
// CObjFlame::CObjFlame
// SWA::CObjFlame::CObjFlame
// A field is not zero initialized,
// causing collisions to constantly get created
// and slow down the game.
@@ -28,7 +29,7 @@ void ObjBigBarrelAllocMidAsmHook(PPCRegister& r3)
r3.u32 += sizeof(ObjBigBarrelEx);
}
// CObjBigBarrel::CObjBigBarrel
// SWA::CObjBigBarrel::CObjBigBarrel
PPC_FUNC_IMPL(__imp__sub_8271AC08);
PPC_FUNC(sub_8271AC08)
{
+1 -1
View File
@@ -97,7 +97,7 @@ void PostUnleashMidAsmHook(PPCRegister& r30)
void SetXButtonHomingMidAsmHook(PPCRegister& r30)
{
r30.u32 = Config::HomingAttackOnBoost;
r30.u32 = !Config::HomingAttackOnJump;
}
// SWA::Player::CEvilSonicContext
+16 -10
View File
@@ -15,25 +15,31 @@ PPC_FUNC(sub_824DCF38)
App::s_isLoading = true;
if (Config::TimeOfDayTransition == ETimeOfDayTransition::PlayStation)
if (ctx.r4.u32 == SWA::eLoadingDisplayType_WerehogMovie)
{
if (ctx.r4.u32 == SWA::eLoadingDisplayType_WerehogMovie)
if (Config::TimeOfDayTransition == ETimeOfDayTransition::PlayStation)
{
ctx.r4.u32 = SWA::eLoadingDisplayType_ChangeTimeOfDay;
pLoading->m_IsNightToDay = App::s_isWerehog;
}
if (Config::UseArrowsForTimeOfDayTransition)
ctx.r4.u32 = SWA::eLoadingDisplayType_Arrows;
}
if (auto pGameDocument = SWA::CGameDocument::GetInstance())
if (Config::FixEggmanlandUsingEventGalleryTransition)
{
auto stageName = pGameDocument->m_pMember->m_StageName.c_str();
if (stageName && strlen(stageName))
if (auto pGameDocument = SWA::CGameDocument::GetInstance())
{
/* Fix restarting Eggmanland as the Werehog
erroneously using the Event Gallery transition. */
if (ctx.r4.u32 == SWA::eLoadingDisplayType_EventGallery && !strcmp(stageName, "Act_EggmanLand"))
ctx.r4.u32 = SWA::eLoadingDisplayType_NowLoading;
auto stageName = pGameDocument->m_pMember->m_StageName.c_str();
if (stageName && strlen(stageName))
{
/* Fix restarting Eggmanland as the Werehog
erroneously using the Event Gallery transition. */
if (ctx.r4.u32 == SWA::eLoadingDisplayType_EventGallery && !strcmp(stageName, "Act_EggmanLand"))
ctx.r4.u32 = SWA::eLoadingDisplayType_NowLoading;
}
}
}
+30 -2
View File
@@ -76,8 +76,12 @@ PPC_FUNC(sub_830D25D8)
auto device = reinterpret_cast<GuestDevice*>(base + PPC_LOAD_U32(ctx.r4.u32));
// Set first sampler to use linear filtering.
device->samplerStates[0].data[3] = (device->samplerStates[0].data[3].get() & ~0x1f80000) | 0x1280000;
device->dirtyFlags[3] = device->dirtyFlags[3].get() | 0x80000000ull;
// NOTE: We only check for height here since all 2D primitives get centered.
if (Video::s_viewportHeight > 720)
{
device->samplerStates[0].data[3] = (device->samplerStates[0].data[3].get() & ~0x1f80000) | 0x1280000;
device->dirtyFlags[3] = device->dirtyFlags[3].get() | 0x80000000ull;
}
__imp__sub_830D25D8(ctx, base);
}
@@ -113,3 +117,27 @@ PPC_FUNC(sub_8260BBF8)
SetDefaultMaterialParameters(reinterpret_cast<GuestDevice*>(base + PPC_LOAD_U32(PPC_LOAD_U32(ctx.r4.u32))));
__imp__sub_8260BBF8(ctx, base);
}
// The pedestal in Gaia Temple is placed on an opaque mesh slot, despite using additive blending.
// This somehow works. Except when the delta time is too stable, it flashes black for one frame.
// We can fix it by detecting the asset runtime, and swapping the mesh slots to transparent by hand.
// Hedgehog::Mirage::CModelData::Make
PPC_FUNC_IMPL(__imp__sub_82E38650);
PPC_FUNC(sub_82E38650)
{
if (ctx.r5.u32 == 0xBB90 && XXH3_64bits(base + ctx.r4.u32, ctx.r5.u32) == 0xB524C8C3B80C3F54)
{
// Mesh Count
std::swap(
*reinterpret_cast<uint32_t*>(base + ctx.r4.u32 + 0x18),
*reinterpret_cast<uint32_t*>(base + ctx.r4.u32 + 0x20));
// Mesh Offset
std::swap(
*reinterpret_cast<uint32_t*>(base + ctx.r4.u32 + 0x1C),
*reinterpret_cast<uint32_t*>(base + ctx.r4.u32 + 0x24));
}
__imp__sub_82E38650(ctx, base);
}
+5 -6
View File
@@ -1,20 +1,19 @@
#pragma once
inline const char* g_credits[] =
inline std::array<const char*, 14> g_credits =
{
"Skyth",
"Hyper",
"Darío",
"Sajid",
"DeaThProj",
"RadiantDerg",
"PTKay",
"DeaThProj",
"SuperSonic16",
"NextinHKRY",
"M&M",
"saguinee",
"LadyLunanova",
"LJSTAR"
"LJSTAR",
"Goalringmod27",
"M&M"
};
inline size_t g_creditsSize = 12;
+1 -1
View File
@@ -1,4 +1,4 @@
VERSION_MILESTONE="Beta 1"
VERSION_MILESTONE="Beta 3"
VERSION_MAJOR=1
VERSION_MINOR=0
VERSION_REVISION=0
+2 -2
View File
@@ -4,7 +4,7 @@ class ISDLEventListener
{
public:
virtual ~ISDLEventListener() = default;
virtual void OnSDLEvent(SDL_Event* event) = 0;
virtual bool OnSDLEvent(SDL_Event* event) = 0;
};
extern std::vector<ISDLEventListener*>& GetEventListeners();
@@ -17,5 +17,5 @@ public:
GetEventListeners().emplace_back(this);
}
void OnSDLEvent(SDL_Event* event) override {}
bool OnSDLEvent(SDL_Event* event) override { return false; }
};
+49 -64
View File
@@ -48,13 +48,11 @@ static std::unique_ptr<GuestTexture> g_upTrophyIcon;
static int g_firstVisibleRowIndex;
static int g_selectedRowIndex;
static double g_rowSelectionTime;
static double g_lastTappedTime;
static double g_lastIncrementTime;
static bool g_upWasHeld;
static bool g_downWasHeld;
static bool g_leftWasHeld;
static bool g_rightWasHeld;
static bool g_upRSWasHeld;
static bool g_downRSWasHeld;
static void ResetSelection()
{
@@ -67,7 +65,7 @@ static void ResetSelection()
static void DrawContainer(ImVec2 min, ImVec2 max, ImU32 gradientTop, ImU32 gradientBottom, float alpha = 1, float cornerRadius = 25)
{
auto drawList = ImGui::GetForegroundDrawList();
auto drawList = ImGui::GetBackgroundDrawList();
DrawPauseContainer(min, max, alpha);
@@ -76,8 +74,9 @@ static void DrawContainer(ImVec2 min, ImVec2 max, ImU32 gradientTop, ImU32 gradi
static void DrawHeaderContainer(const char* text)
{
auto drawList = ImGui::GetForegroundDrawList();
auto drawList = ImGui::GetBackgroundDrawList();
auto fontSize = Scale(24);
auto minTextSize = Scale(294.575989);
auto textSize = g_fntNewRodinUB->CalcTextSizeA(fontSize, FLT_MAX, 0, text);
auto cornerRadius = 23;
auto textMarginX = Scale(16) + (Scale(cornerRadius) / 2);
@@ -101,13 +100,12 @@ static void DrawHeaderContainer(const char* text)
: Lerp(0, 1, colourMotion);
ImVec2 min = { g_aspectRatioOffsetX + Scale(containerMarginX), g_aspectRatioOffsetY + Scale(136) };
ImVec2 max = { min.x + textMarginX * 2 + textSize.x + Scale(5), g_aspectRatioOffsetY + Scale(196) };
ImVec2 max = { std::max(min.x + minTextSize, min.x + textMarginX * 2 + textSize.x + Scale(5)), g_aspectRatioOffsetY + Scale(196) };
DrawPauseHeaderContainer(min, max, alpha);
SetTextSkew((min.y + max.y) / 2.0f, Scale(3.0f));
// TODO: Apply bevel.
DrawTextWithOutline
(
g_fntNewRodinUB,
@@ -124,7 +122,7 @@ static void DrawHeaderContainer(const char* text)
static void DrawAchievement(int rowIndex, float yOffset, Achievement& achievement, bool isUnlocked)
{
auto drawList = ImGui::GetForegroundDrawList();
auto drawList = ImGui::GetBackgroundDrawList();
auto clipRectMin = drawList->GetClipRectMin();
auto clipRectMax = drawList->GetClipRectMax();
@@ -339,7 +337,7 @@ static void DrawAchievement(int rowIndex, float yOffset, Achievement& achievemen
static void DrawTrophySparkles(ImVec2 min, ImVec2 max, int recordCount, int trophyFrameIndex)
{
auto drawList = ImGui::GetForegroundDrawList();
auto drawList = ImGui::GetBackgroundDrawList();
constexpr auto recordsHalfTotal = ACH_RECORDS / 2;
@@ -427,7 +425,7 @@ static void DrawTrophySparkles(ImVec2 min, ImVec2 max, int recordCount, int trop
static void DrawAchievementTotal(ImVec2 min, ImVec2 max)
{
auto drawList = ImGui::GetForegroundDrawList();
auto drawList = ImGui::GetBackgroundDrawList();
// Transparency fade animation.
auto alpha = Cubic(0, 1, ComputeMotion(g_appearTime, COUNTER_INTRO_FADE_START, COUNTER_INTRO_FADE_END));
@@ -511,7 +509,7 @@ static void DrawAchievementTotal(ImVec2 min, ImVec2 max)
static void DrawContentContainer()
{
auto drawList = ImGui::GetForegroundDrawList();
auto drawList = ImGui::GetBackgroundDrawList();
// Expand/retract animation.
auto motion = g_isClosing
@@ -566,15 +564,12 @@ static void DrawContentContainer()
// Draw separators.
for (int i = 1; i <= 3; i++)
{
auto lineMarginLeft = Scale(35);
auto lineMarginRight = Scale(55);
auto lineMarginY = Scale(2);
ImVec2 lineMin = { clipRectMin.x + Scale(35), clipRectMin.y + itemHeight * i + Scale(2) };
ImVec2 lineMax = { clipRectMax.x - Scale(55), lineMin.y + Scale(1.3f) };
ImVec2 lineMin = { clipRectMin.x + lineMarginLeft, clipRectMin.y + itemHeight * i + lineMarginY };
ImVec2 lineMax = { clipRectMax.x - lineMarginRight, clipRectMin.y + itemHeight * i + lineMarginY };
drawList->AddLine(lineMin, lineMax, IM_COL32(163, 163, 163, 255));
drawList->AddLine({ lineMin.x, lineMin.y + Scale(1) }, { lineMax.x, lineMax.y + Scale(1) }, IM_COL32(143, 148, 143, 255));
SetAdditive(true);
drawList->AddRectFilled(lineMin, lineMax, IM_COL32(160, 160, 160, 60));
SetAdditive(false);
}
for (auto& tpl : g_achievements)
@@ -601,26 +596,41 @@ static void DrawContentContainer()
bool downIsHeld = inputState->GetPadState().IsDown(SWA::eKeyState_DpadDown) ||
inputState->GetPadState().LeftStickVertical < -0.5f;
bool leftIsHeld = inputState->GetPadState().IsDown(SWA::eKeyState_DpadLeft) ||
inputState->GetPadState().LeftStickHorizontal < -0.5f;
bool rightIsHeld = inputState->GetPadState().IsDown(SWA::eKeyState_DpadRight) ||
inputState->GetPadState().LeftStickHorizontal > 0.5f;
bool upRSIsHeld = inputState->GetPadState().RightStickVertical > 0.5f;
bool downRSIsHeld = inputState->GetPadState().RightStickVertical < -0.5f;
bool isReachedTop = g_selectedRowIndex == 0;
bool isReachedBottom = g_selectedRowIndex == rowCount - 1;
bool scrollUp = !g_upWasHeld && upIsHeld;
bool scrollDown = !g_downWasHeld && downIsHeld;
bool scrollPageUp = !g_leftWasHeld && leftIsHeld && !isReachedTop;
bool scrollPageDown = !g_rightWasHeld && rightIsHeld && !isReachedBottom;
bool jumpToTop = !g_upRSWasHeld && upRSIsHeld && !isReachedTop;
bool jumpToBottom = !g_downRSWasHeld && downRSIsHeld && !isReachedBottom;
int prevSelectedRowIndex = g_selectedRowIndex;
auto time = ImGui::GetTime();
auto fastScroll = (time - g_lastTappedTime) > 0.6;
auto fastScrollSpeed = 1.0 / 3.5;
static auto fastScrollSpeedUp = false;
if (scrollUp || scrollDown)
g_lastTappedTime = time;
if (!upIsHeld && !downIsHeld)
fastScrollSpeedUp = false;
if (fastScrollSpeedUp)
fastScrollSpeed /= 2;
if (fastScroll)
{
if ((time - g_lastIncrementTime) < fastScrollSpeed)
{
fastScroll = false;
}
else
{
g_lastIncrementTime = time;
scrollUp = upIsHeld;
scrollDown = downIsHeld;
fastScrollSpeedUp = true;
}
}
if (scrollUp)
{
@@ -634,40 +644,15 @@ static void DrawContentContainer()
if (g_selectedRowIndex >= rowCount)
g_selectedRowIndex = 0;
}
else if (scrollPageUp)
{
g_selectedRowIndex -= 3;
if (g_selectedRowIndex < 0)
g_selectedRowIndex = 0;
}
else if (scrollPageDown)
{
g_selectedRowIndex += 3;
if (g_selectedRowIndex >= rowCount)
g_selectedRowIndex = rowCount - 1;
}
else if (jumpToTop)
{
g_selectedRowIndex = 0;
}
else if (jumpToBottom)
{
g_selectedRowIndex = rowCount - 1;
}
// lol
if (scrollUp || scrollDown || scrollPageUp || scrollPageDown || jumpToTop || jumpToBottom)
if (scrollUp || scrollDown)
{
g_rowSelectionTime = ImGui::GetTime();
g_rowSelectionTime = time;
Game_PlaySound("sys_actstg_pausecursor");
}
g_upWasHeld = upIsHeld;
g_downWasHeld = downIsHeld;
g_leftWasHeld = leftIsHeld;
g_rightWasHeld = rightIsHeld;
g_upRSWasHeld = upRSIsHeld;
g_downRSWasHeld = downRSIsHeld;
int visibleRowCount = int(floor((clipRectMax.y - clipRectMin.y) / itemHeight));
@@ -778,12 +763,12 @@ void AchievementMenu::Open()
return std::get<1>(a) > std::get<1>(b);
});
ButtonGuide::Open(Button(Localise("Common_Back"), EButtonIcon::B));
ButtonGuide::Open(Button("Common_Back", EButtonIcon::B));
ResetSelection();
Game_PlaySound("sys_actstg_pausewinopen");
hid::SetProhibitedButtons(XAMINPUT_GAMEPAD_START);
hid::SetProhibitedInputs(XAMINPUT_GAMEPAD_START);
}
void AchievementMenu::Close()
@@ -793,7 +778,7 @@ void AchievementMenu::Close()
g_appearTime = ImGui::GetTime();
g_isClosing = true;
hid::SetProhibitedButtons(0);
hid::SetProhibitedInputs();
}
ButtonGuide::Close();
+58 -22
View File
@@ -10,6 +10,7 @@
#include <app.h>
#include <exports.h>
#include <decompressor.h>
#include <patches/inspire_patches.h>
constexpr double OVERLAY_CONTAINER_COMMON_MOTION_START = 0;
constexpr double OVERLAY_CONTAINER_COMMON_MOTION_END = 11;
@@ -30,7 +31,7 @@ static ImFont* g_fntSeurat;
static bool DrawContainer(ImVec2 min, ImVec2 max, float cornerRadius = 25)
{
auto drawList = ImGui::GetForegroundDrawList();
auto drawList = ImGui::GetBackgroundDrawList();
// Expand/retract animation.
auto containerMotion = ComputeMotion(g_appearTime, OVERLAY_CONTAINER_COMMON_MOTION_START, OVERLAY_CONTAINER_COMMON_MOTION_END);
@@ -80,15 +81,68 @@ void AchievementOverlay::Init()
g_fntSeurat = ImFontAtlasSnapshot::GetFont("FOT-SeuratPro-M.otf");
}
// Dequeue achievements only when we can actually play sounds.
// Loading thread does not update this object.
static bool g_soundAdministratorUpdated;
PPC_FUNC_IMPL(__imp__sub_82B43480);
PPC_FUNC(sub_82B43480)
{
g_soundAdministratorUpdated = true;
__imp__sub_82B43480(ctx, base);
}
// Dequeue achievements only in the main thread. This is also extra thread safety.
static std::thread::id g_mainThreadId = std::this_thread::get_id();
static bool CanDequeueAchievement()
{
if (g_soundAdministratorUpdated && std::this_thread::get_id() == g_mainThreadId && !AchievementOverlay::s_queue.empty())
{
// Check if we can actually play any audio right now. If not, we'll wait until we can.
uint32_t audioCenter = *reinterpret_cast<be<uint32_t>*>(g_memory.Translate(0x83362FFC));
if (audioCenter != NULL)
{
uint32_t member = *reinterpret_cast<be<uint32_t>*>(g_memory.Translate(audioCenter + 0x4));
uint32_t category = !InspirePatches::s_sceneName.empty() ? 10 : 7; // EVENT category is used during Inspire cutscenes.
// Check if the volume is non zero.
return *reinterpret_cast<uint32_t*>(g_memory.Translate(member + 0x7C + category * 0x10 + 0x8)) != 0;
}
}
return false;
}
void AchievementOverlay::Draw()
{
if (!AchievementOverlay::s_isVisible && CanDequeueAchievement())
{
s_isVisible = true;
g_isClosing = false;
g_appearTime = ImGui::GetTime();
g_achievement = g_xdbfWrapper.GetAchievement((EXDBFLanguage)Config::Language.Value, s_queue.front());
s_queue.pop();
if (Config::Language == ELanguage::English)
g_achievement.Name = xdbf::FixInvalidSequences(g_achievement.Name);
Game_PlaySound("obj_navi_appear");
}
if (!s_isVisible)
{
g_soundAdministratorUpdated = false;
return;
}
if (ImGui::GetTime() - g_appearTime >= OVERLAY_DURATION)
AchievementOverlay::Close();
auto drawList = ImGui::GetForegroundDrawList();
// Close function can use this bool so reset it after.
g_soundAdministratorUpdated = false;
auto drawList = ImGui::GetBackgroundDrawList();
auto& res = ImGui::GetIO().DisplaySize;
auto strAchievementUnlocked = Localise("Achievements_Unlock").c_str();
@@ -167,21 +221,7 @@ void AchievementOverlay::Draw()
void AchievementOverlay::Open(int id)
{
if (s_isVisible)
{
s_queue.emplace(id);
return;
}
s_isVisible = true;
g_isClosing = false;
g_appearTime = ImGui::GetTime();
g_achievement = g_xdbfWrapper.GetAchievement((EXDBFLanguage)Config::Language.Value, id);
if (Config::Language == ELanguage::English)
g_achievement.Name = xdbf::FixInvalidSequences(g_achievement.Name);
Game_PlaySound("obj_navi_appear");
s_queue.push(id);
}
void AchievementOverlay::Close()
@@ -192,10 +232,6 @@ void AchievementOverlay::Close()
g_isClosing = true;
}
if (s_queue.size())
{
if (CanDequeueAchievement())
s_isVisible = false;
AchievementOverlay::Open(s_queue.front());
s_queue.pop();
}
}
+58
View File
@@ -0,0 +1,58 @@
#include "black_bar.h"
#include <patches/aspect_ratio_patches.h>
void BlackBar::Draw()
{
if (g_inspirePillarbox)
{
auto drawList = ImGui::GetBackgroundDrawList();
auto& res = ImGui::GetIO().DisplaySize;
float width = (res.x - (res.y * 16.0f / 9.0f)) / 2.0f;
drawList->AddRectFilled(
{ 0.0f, 0.0f },
{ width, res.y },
IM_COL32_BLACK);
drawList->AddRectFilled(
{ res.x - width, 0.0f },
res,
IM_COL32_BLACK);
g_inspirePillarbox = false;
}
if (g_loadingBlackBarAlpha != 0)
{
auto drawList = ImGui::GetBackgroundDrawList();
auto& res = ImGui::GetIO().DisplaySize;
if (g_aspectRatio > WIDE_ASPECT_RATIO)
{
drawList->AddRectFilled(
{ 0.0f, 0.0f },
{ g_loadingBlackBarMin.x, g_loadingBlackBarMax.y },
IM_COL32(0, 0, 0, g_loadingBlackBarAlpha));
drawList->AddRectFilled(
{ g_loadingBlackBarMax.x, g_loadingBlackBarMin.y },
res,
IM_COL32(0, 0, 0, g_loadingBlackBarAlpha));
}
else if (g_aspectRatio < NARROW_ASPECT_RATIO)
{
drawList->AddRectFilled(
{ 0.0f, 0.0f },
{ g_loadingBlackBarMax.x, g_loadingBlackBarMin.y },
IM_COL32(0, 0, 0, g_loadingBlackBarAlpha));
drawList->AddRectFilled(
{ g_loadingBlackBarMin.x, g_loadingBlackBarMax.y },
res,
IM_COL32(0, 0, 0, g_loadingBlackBarAlpha));
}
g_loadingBlackBarAlpha = 0;
}
}
+12
View File
@@ -0,0 +1,12 @@
#pragma once
struct BlackBar
{
static inline bool g_inspirePillarbox;
static inline ImVec2 g_loadingBlackBarMin;
static inline ImVec2 g_loadingBlackBarMax;
static inline uint8_t g_loadingBlackBarAlpha;
static void Draw();
};
+19 -10
View File
@@ -38,7 +38,8 @@ std::unordered_map<EButtonIcon, float> g_iconWidths =
{ EButtonIcon::Start, 40 },
{ EButtonIcon::Back, 40 },
{ EButtonIcon::LMB, 40 },
{ EButtonIcon::Enter, 40 }
{ EButtonIcon::Enter, 40 },
{ EButtonIcon::Escape, 40 },
};
std::unordered_map<EButtonIcon, float> g_iconHeights =
@@ -56,7 +57,8 @@ std::unordered_map<EButtonIcon, float> g_iconHeights =
{ EButtonIcon::Start, 40 },
{ EButtonIcon::Back, 40 },
{ EButtonIcon::LMB, 40 },
{ EButtonIcon::Enter, 40 }
{ EButtonIcon::Enter, 40 },
{ EButtonIcon::Escape, 40 },
};
std::tuple<std::tuple<ImVec2, ImVec2>, GuestTexture*> GetButtonIcon(EButtonIcon icon)
@@ -124,12 +126,17 @@ std::tuple<std::tuple<ImVec2, ImVec2>, GuestTexture*> GetButtonIcon(EButtonIcon
break;
case EButtonIcon::LMB:
btn = PIXELS_TO_UV_COORDS(256, 128, 0, 0, 128, 128);
btn = PIXELS_TO_UV_COORDS(384, 128, 0, 0, 128, 128);
texture = g_upKBMIcons.get();
break;
case EButtonIcon::Enter:
btn = PIXELS_TO_UV_COORDS(256, 128, 128, 0, 128, 128);
btn = PIXELS_TO_UV_COORDS(384, 128, 128, 0, 128, 128);
texture = g_upKBMIcons.get();
break;
case EButtonIcon::Escape:
btn = PIXELS_TO_UV_COORDS(384, 128, 256, 0, 128, 128);
texture = g_upKBMIcons.get();
break;
}
@@ -148,7 +155,7 @@ static void DrawGuide(float* offset, ImVec2 regionMin, ImVec2 regionMax, EButton
EButtonAlignment alignment, ImVec2 iconMin, ImVec2 iconMax, EFontQuality fontQuality,
ImVec2 textSize, float fontSize, const char* text)
{
auto drawList = ImGui::GetForegroundDrawList();
auto drawList = ImGui::GetBackgroundDrawList();
auto _icon = icon;
auto iconWidth = Scale(g_iconWidths[icon]);
auto dualIconMarginX = Scale(25);
@@ -228,7 +235,7 @@ void ButtonGuide::Draw()
if (!s_isVisible)
return;
auto drawList = ImGui::GetForegroundDrawList();
auto drawList = ImGui::GetBackgroundDrawList();
auto& res = ImGui::GetIO().DisplaySize;
ImVec2 regionMin = { g_aspectRatioOffsetX + Scale(g_sideMargins), g_aspectRatioOffsetY * 2.0f + Scale(720.0f - 102.0f) };
@@ -253,9 +260,10 @@ void ButtonGuide::Draw()
if (btn.Visibility && !*btn.Visibility)
continue;
auto str = Localise(btn.Name.c_str()).c_str();
auto iconWidth = Scale(g_iconWidths[btn.Icon]);
auto iconHeight = Scale(g_iconHeights[btn.Icon]);
auto textSize = g_fntNewRodin->CalcTextSizeA(fontSize, FLT_MAX, 0, btn.Name.c_str());
auto textSize = g_fntNewRodin->CalcTextSizeA(fontSize, FLT_MAX, 0, str);
if (i > 0)
offsetLeft += textSize.x + iconWidth + textMarginX;
@@ -263,7 +271,7 @@ void ButtonGuide::Draw()
ImVec2 iconMin = { regionMin.x + offsetLeft - iconWidth - iconMarginX, regionMin.y };
ImVec2 iconMax = { regionMin.x + offsetLeft - iconMarginX, regionMin.y + iconHeight };
DrawGuide(&offsetLeft, regionMin, regionMax, btn.Icon, btn.Alignment, iconMin, iconMax, btn.FontQuality, textSize, fontSize, btn.Name.c_str());
DrawGuide(&offsetLeft, regionMin, regionMax, btn.Icon, btn.Alignment, iconMin, iconMax, btn.FontQuality, textSize, fontSize, str);
}
// Draw right aligned icons.
@@ -277,9 +285,10 @@ void ButtonGuide::Draw()
if (btn.Visibility && !*btn.Visibility)
continue;
auto str = Localise(btn.Name.c_str()).c_str();
auto iconWidth = Scale(g_iconWidths[btn.Icon]);
auto iconHeight = Scale(g_iconHeights[btn.Icon]);
auto textSize = g_fntNewRodin->CalcTextSizeA(fontSize, FLT_MAX, 0, btn.Name.c_str());
auto textSize = g_fntNewRodin->CalcTextSizeA(fontSize, FLT_MAX, 0, str);
if (i < g_buttons.size() - 1)
offsetRight += textSize.x + iconWidth + textMarginX;
@@ -287,7 +296,7 @@ void ButtonGuide::Draw()
ImVec2 iconMin = { regionMax.x - offsetRight - iconWidth - iconMarginX, regionMin.y };
ImVec2 iconMax = { regionMax.x - offsetRight - iconMarginX, regionMin.y + iconHeight };
DrawGuide(&offsetRight, regionMin, regionMax, btn.Icon, btn.Alignment, iconMin, iconMax, btn.FontQuality, textSize, fontSize, btn.Name.c_str());
DrawGuide(&offsetRight, regionMin, regionMax, btn.Icon, btn.Alignment, iconMin, iconMax, btn.FontQuality, textSize, fontSize, str);
}
}
+2 -1
View File
@@ -20,7 +20,8 @@ enum class EButtonIcon
// Keyboard + Mouse (temporary)
LMB,
Enter
Enter,
Escape
};
enum class EButtonAlignment
+1 -1
View File
@@ -45,7 +45,7 @@ void Fader::Draw()
auto colour = IM_COL32(g_colour & 0xFF, (g_colour >> 8) & 0xFF, (g_colour >> 16) & 0xFF, 255 * alpha);
ImGui::GetForegroundDrawList()->AddRectFilled({ 0, 0 }, ImGui::GetIO().DisplaySize, colour);
ImGui::GetBackgroundDrawList()->AddRectFilled({ 0, 0 }, ImGui::GetIO().DisplaySize, colour);
}
static void DoFade(bool isFadeIn, float duration, std::function<void()> endCallback, float endCallbackDelay)
+12 -39
View File
@@ -9,7 +9,7 @@
#if _WIN32
#include <dwmapi.h>
#pragma comment(lib, "dwmapi.lib")
#include <shellscalingapi.h>
#endif
#include <res/images/game_icon.bmp.h>
@@ -24,7 +24,12 @@ int Window_OnSDLEvent(void*, SDL_Event* event)
ImGui_ImplSDL2_ProcessEvent(event);
for (auto listener : GetEventListeners())
listener->OnSDLEvent(event);
{
if (listener->OnSDLEvent(event))
{
return 0;
}
}
switch (event->type)
{
@@ -141,20 +146,6 @@ int Window_OnSDLEvent(void*, SDL_Event* event)
break;
}
if (!GameWindow::IsFullscreen())
{
if (event->type == SDL_CONTROLLERBUTTONDOWN || event->type == SDL_CONTROLLERBUTTONUP || event->type == SDL_CONTROLLERAXISMOTION)
{
// Hide mouse cursor when controller input is detected.
SDL_ShowCursor(SDL_DISABLE);
}
else if (event->type == SDL_MOUSEMOTION)
{
// Restore mouse cursor when mouse input is detected.
SDL_ShowCursor(SDL_ENABLE);
}
}
return 0;
}
@@ -179,30 +170,9 @@ void GameWindow::Init(const char* sdlVideoDriver)
SDL_AddEventWatch(Window_OnSDLEvent, s_pWindow);
#ifdef _WIN32
SetProcessDPIAware();
SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
#endif
Config::WindowSize.LockCallback = [](ConfigDef<int32_t>* def)
{
// Try matching the current window size with a known configuration.
if (def->Value < 0)
def->Value = FindNearestDisplayMode();
};
Config::WindowSize.ApplyCallback = [](ConfigDef<int32_t>* def)
{
auto displayModes = GetDisplayModes();
// Use largest supported resolution if overflowed.
if (def->Value >= displayModes.size())
def->Value = displayModes.size() - 1;
auto& mode = displayModes[def->Value];
auto centre = SDL_WINDOWPOS_CENTERED_DISPLAY(GetDisplay());
SetDimensions(mode.w, mode.h, centre, centre);
};
s_x = Config::WindowX;
s_y = Config::WindowY;
s_width = Config::WindowWidth;
@@ -437,7 +407,7 @@ void GameWindow::ResetDimensions()
uint32_t GameWindow::GetWindowFlags()
{
uint32_t flags = SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE;
uint32_t flags = SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI;
if (Config::WindowState == EWindowState::Maximised)
flags |= SDL_WINDOW_MAXIMIZED;
@@ -475,6 +445,9 @@ void GameWindow::SetDisplay(int displayIndex)
if (!IsFullscreen())
return;
if (GetDisplay() == displayIndex)
return;
s_isChangingDisplay = true;
SDL_Rect bounds;
+78 -68
View File
@@ -6,20 +6,17 @@
#include <res/images/common/general_window.dds.h>
#include <res/images/common/light.dds.h>
#include <res/images/common/select_fade.dds.h>
#include <res/images/common/select_fill.dds.h>
#include <res/images/common/select.dds.h>
std::unique_ptr<GuestTexture> g_texGeneralWindow;
std::unique_ptr<GuestTexture> g_texLight;
std::unique_ptr<GuestTexture> g_texSelectFade;
std::unique_ptr<GuestTexture> g_texSelectFill;
std::unique_ptr<GuestTexture> g_texSelect;
void InitImGuiUtils()
{
g_texGeneralWindow = LOAD_ZSTD_TEXTURE(g_general_window);
g_texLight = LOAD_ZSTD_TEXTURE(g_light);
g_texSelectFade = LOAD_ZSTD_TEXTURE(g_select_fade);
g_texSelectFill = LOAD_ZSTD_TEXTURE(g_select_fill);
g_texSelect = LOAD_ZSTD_TEXTURE(g_select);
}
void SetGradient(const ImVec2& min, const ImVec2& max, ImU32 top, ImU32 bottom)
@@ -80,7 +77,7 @@ void ResetTextSkew()
SetScale({ 1.0f, 1.0f });
}
void SetHorizontalMarqueeFade(ImVec2 min, ImVec2 max, float fadeScale)
void SetHorizontalMarqueeFade(ImVec2 min, ImVec2 max, float fadeScaleLeft, float fadeScaleRight)
{
auto callbackData = AddImGuiCallback(ImGuiCallback::SetMarqueeFade);
callbackData->setMarqueeFade.boundsMin[0] = min.x;
@@ -89,10 +86,15 @@ void SetHorizontalMarqueeFade(ImVec2 min, ImVec2 max, float fadeScale)
callbackData->setMarqueeFade.boundsMax[1] = max.y;
SetShaderModifier(IMGUI_SHADER_MODIFIER_HORIZONTAL_MARQUEE_FADE);
SetScale({ fadeScale, 1.0f });
SetScale({ fadeScaleLeft, fadeScaleRight });
}
void SetVerticalMarqueeFade(ImVec2 min, ImVec2 max, float fadeScale)
void SetHorizontalMarqueeFade(ImVec2 min, ImVec2 max, float fadeScale)
{
SetHorizontalMarqueeFade(min, max, fadeScale, fadeScale);
}
void SetVerticalMarqueeFade(ImVec2 min, ImVec2 max, float fadeScaleTop, float fadeScaleBottom)
{
auto callbackData = AddImGuiCallback(ImGuiCallback::SetMarqueeFade);
callbackData->setMarqueeFade.boundsMin[0] = min.x;
@@ -101,7 +103,12 @@ void SetVerticalMarqueeFade(ImVec2 min, ImVec2 max, float fadeScale)
callbackData->setMarqueeFade.boundsMax[1] = max.y;
SetShaderModifier(IMGUI_SHADER_MODIFIER_VERTICAL_MARQUEE_FADE);
SetScale({ 1.0f, fadeScale });
SetScale({ fadeScaleTop, fadeScaleBottom });
}
void SetVerticalMarqueeFade(ImVec2 min, ImVec2 max, float fadeScale)
{
SetVerticalMarqueeFade(min, max, fadeScale, fadeScale);
}
void ResetMarqueeFade()
@@ -162,7 +169,7 @@ double ComputeMotion(double duration, double offset, double total)
void DrawPauseContainer(ImVec2 min, ImVec2 max, float alpha)
{
auto drawList = ImGui::GetForegroundDrawList();
auto drawList = ImGui::GetBackgroundDrawList();
auto commonWidth = Scale(35);
auto commonHeight = Scale(35);
@@ -193,7 +200,7 @@ void DrawPauseContainer(ImVec2 min, ImVec2 max, float alpha)
void DrawPauseHeaderContainer(ImVec2 min, ImVec2 max, float alpha)
{
auto drawList = ImGui::GetForegroundDrawList();
auto drawList = ImGui::GetBackgroundDrawList();
auto commonWidth = Scale(35);
@@ -210,14 +217,14 @@ void DrawPauseHeaderContainer(ImVec2 min, ImVec2 max, float alpha)
void DrawTextBasic(const ImFont* font, float fontSize, const ImVec2& pos, ImU32 colour, const char* text)
{
auto drawList = ImGui::GetForegroundDrawList();
auto drawList = ImGui::GetBackgroundDrawList();
drawList->AddText(font, fontSize, pos, colour, text, nullptr);
}
void DrawTextWithMarquee(const ImFont* font, float fontSize, const ImVec2& position, const ImVec2& min, const ImVec2& max, ImU32 color, const char* text, double time, double delay, double speed)
{
auto drawList = ImGui::GetForegroundDrawList();
auto drawList = ImGui::GetBackgroundDrawList();
auto rectWidth = max.x - min.x;
auto textSize = font->CalcTextSizeA(fontSize, FLT_MAX, 0, text);
auto textX = position.x - fmodf(std::max(0.0, ImGui::GetTime() - (time + delay)) * speed, textSize.x + rectWidth);
@@ -271,7 +278,7 @@ void DrawTextWithMarquee(const ImFont* font, float fontSize, const ImVec2& posit
void DrawTextWithMarqueeShadow(const ImFont* font, float fontSize, const ImVec2& pos, const ImVec2& min, const ImVec2& max, ImU32 colour, const char* text, double time, double delay, double speed, float offset, float radius, ImU32 shadowColour)
{
auto drawList = ImGui::GetForegroundDrawList();
auto drawList = ImGui::GetBackgroundDrawList();
auto rectWidth = max.x - min.x;
auto textSize = font->CalcTextSizeA(fontSize, FLT_MAX, 0, text);
auto textX = pos.x - fmodf(std::max(0.0, ImGui::GetTime() - (time + delay)) * speed, textSize.x + rectWidth);
@@ -289,7 +296,7 @@ void DrawTextWithMarqueeShadow(const ImFont* font, float fontSize, const ImVec2&
void DrawTextWithOutline(const ImFont* font, float fontSize, const ImVec2& pos, ImU32 color, const char* text, float outlineSize, ImU32 outlineColor, uint32_t shaderModifier)
{
auto drawList = ImGui::GetForegroundDrawList();
auto drawList = ImGui::GetBackgroundDrawList();
SetOutline(outlineSize);
drawList->AddText(font, fontSize, pos, outlineColor, text);
@@ -306,10 +313,16 @@ void DrawTextWithOutline(const ImFont* font, float fontSize, const ImVec2& pos,
void DrawTextWithShadow(const ImFont* font, float fontSize, const ImVec2& pos, ImU32 colour, const char* text, float offset, float radius, ImU32 shadowColour)
{
auto drawList = ImGui::GetForegroundDrawList();
auto drawList = ImGui::GetBackgroundDrawList();
offset = Scale(offset);
// Original 4:3 has thicker text shadows.
if (Config::AspectRatio == EAspectRatio::OriginalNarrow)
{
radius *= 1.5f;
}
SetOutline(radius);
drawList->AddText(font, fontSize, { pos.x + offset, pos.y + offset }, shadowColour, text);
ResetOutline();
@@ -353,7 +366,8 @@ std::string Truncate(const std::string& input, size_t maxLength, bool useEllipsi
return input;
}
std::pair<std::string, std::map<std::string, std::string>> RemoveRubyAnnotations(const char* input) {
std::pair<std::string, std::map<std::string, std::string>> RemoveRubyAnnotations(const char* input)
{
std::string output;
std::map<std::string, std::string> rubyMap;
std::string currentMain, currentRuby;
@@ -400,7 +414,8 @@ std::pair<std::string, std::map<std::string, std::string>> RemoveRubyAnnotations
return { output, rubyMap };
}
std::string ReAddRubyAnnotations(const std::string_view& wrappedText, const std::map<std::string, std::string>& rubyMap) {
std::string ReAddRubyAnnotations(const std::string_view& wrappedText, const std::map<std::string, std::string>& rubyMap)
{
std::string annotatedText;
size_t idx = 0;
size_t length = wrappedText.length();
@@ -431,7 +446,7 @@ std::string ReAddRubyAnnotations(const std::string_view& wrappedText, const std:
return annotatedText;
}
std::vector<std::string> Split(const char* strStart, const ImFont *font, float fontSize, float maxWidth)
std::vector<std::string> Split(const char* strStart, const ImFont* font, float fontSize, float maxWidth)
{
if (!strStart)
return {};
@@ -445,6 +460,7 @@ std::vector<std::string> Split(const char* strStart, const ImFont *font, float f
const char *lineStart = strStart;
const bool wordWrapEnabled = (maxWidth > 0.0f);
const char *wordWrapEOL = nullptr;
while (*str != 0)
{
if (wordWrapEnabled)
@@ -572,10 +588,9 @@ std::vector<std::string> RemoveAnnotationFromParagraph(const std::vector<std::st
for (auto& annotatedLine : paragraph.lines)
{
std::string annotationRemovedLine = "";
for (const auto& segment : annotatedLine)
{
annotationRemovedLine += segment.text;
}
result.push_back(annotationRemovedLine);
}
@@ -588,9 +603,7 @@ std::string RemoveAnnotationFromParagraphLine(const std::vector<TextSegment>& an
std::string result = "";
for (auto& segment : annotatedLine)
{
result += segment.text;
}
return result;
}
@@ -603,25 +616,19 @@ ImVec2 MeasureCentredParagraph(const ImFont* font, float fontSize, float lineMar
const auto paragraph = CalculateAnnotatedParagraph(lines);
std::vector<std::string> annotationRemovedLines;
for (const auto& line : paragraph.lines)
{
annotationRemovedLines.emplace_back(RemoveAnnotationFromParagraphLine(line));
}
for (size_t i = 0; i < annotationRemovedLines.size(); i++)
{
auto& line = annotationRemovedLines[i];
auto textSize = font->CalcTextSizeA(fontSize, FLT_MAX, 0, line.c_str());
auto annotationSize = font->CalcTextSizeA(fontSize * ANNOTATION_FONT_SIZE_MODIFIER, FLT_MAX, 0, "");
auto textSize = font->CalcTextSizeA(fontSize, FLT_MAX, 0, annotationRemovedLines[i].c_str());
x = std::max(x, textSize.x);
y += textSize.y + Scale(lineMargin);
if (paragraph.annotated)
{
y += annotationSize.y;
}
if (paragraph.annotated && i != (annotationRemovedLines.size() - 1))
y += fontSize * ANNOTATION_FONT_SIZE_MODIFIER;
}
return { x, y };
@@ -629,23 +636,27 @@ ImVec2 MeasureCentredParagraph(const ImFont* font, float fontSize, float lineMar
ImVec2 MeasureCentredParagraph(const ImFont* font, float fontSize, float maxWidth, float lineMargin, const char* text)
{
return MeasureCentredParagraph(font, fontSize, lineMargin, Split(text, font, fontSize, maxWidth));
const auto input = RemoveRubyAnnotations(text);
auto lines = Split(input.first.c_str(), font, fontSize, maxWidth);
for (auto& line : lines)
line = ReAddRubyAnnotations(line, input.second);
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)
{
float annotationFontSize = fontSize * ANNOTATION_FONT_SIZE_MODIFIER;
auto annotationFontSize = fontSize * ANNOTATION_FONT_SIZE_MODIFIER;
const auto input = RemoveRubyAnnotations(text);
auto lines = Split(input.first.c_str(), font, fontSize, maxWidth);
for (auto& line : lines)
{
line = ReAddRubyAnnotations(line, input.second);
}
auto paragraphSize = MeasureCentredParagraph(font, fontSize, lineMargin, lines);
float offsetY = 0.0f;
auto offsetY = 0.0f;
const auto paragraph = CalculateAnnotatedParagraph(lines);
@@ -655,9 +666,8 @@ void DrawRubyAnnotatedText(const ImFont* font, float fontSize, float maxWidth, c
auto textSize = font->CalcTextSizeA(fontSize, FLT_MAX, 0, annotationRemovedLine.c_str());
auto annotationSize = font->CalcTextSizeA(annotationFontSize, FLT_MAX, 0, "");
float textX = pos.x;
float textY = pos.y + offsetY;
auto textX = pos.x;
auto textY = pos.y + offsetY;
if (isCentred)
{
@@ -681,14 +691,14 @@ void DrawRubyAnnotatedText(const ImFont* font, float fontSize, float maxWidth, c
}
drawMethod(segment.text.c_str(), { textX, textY });
textX += textSize.x;
}
offsetY += textSize.y + Scale(lineMargin);
if (paragraph.annotated)
{
offsetY += annotationSize.y;
}
}
}
@@ -728,7 +738,7 @@ ImU32 ColourLerp(ImU32 c0, ImU32 c1, float t)
void DrawVersionString(const ImFont* font, const ImU32 col)
{
auto drawList = ImGui::GetForegroundDrawList();
auto drawList = ImGui::GetBackgroundDrawList();
auto& res = ImGui::GetIO().DisplaySize;
auto fontSize = Scale(12);
auto textMargin = Scale(2);
@@ -739,7 +749,7 @@ void DrawVersionString(const ImFont* font, const ImU32 col)
void DrawSelectionContainer(ImVec2 min, ImVec2 max, bool fadeTop)
{
auto drawList = ImGui::GetForegroundDrawList();
auto drawList = ImGui::GetBackgroundDrawList();
static auto breatheStart = ImGui::GetTime();
auto alpha = BREATHE_MOTION(1.0f, 0.55f, breatheStart, 0.92f);
@@ -753,37 +763,37 @@ void DrawSelectionContainer(ImVec2 min, ImVec2 max, bool fadeTop)
auto centre = PIXELS_TO_UV_COORDS(64, 64, 11, 0, 8, 50);
auto right = PIXELS_TO_UV_COORDS(64, 64, 19, 0, 11, 50);
drawList->AddImage(g_texSelectFade.get(), min, { min.x + commonWidth, max.y }, GET_UV_COORDS(left), colour);
drawList->AddImage(g_texSelectFade.get(), { min.x + commonWidth, min.y }, { max.x - commonWidth, max.y }, GET_UV_COORDS(centre), colour);
drawList->AddImage(g_texSelectFade.get(), { max.x - commonWidth, min.y }, max, GET_UV_COORDS(right), colour);
drawList->AddImage(g_texSelect.get(), min, { min.x + commonWidth, max.y }, GET_UV_COORDS(left), colour);
drawList->AddImage(g_texSelect.get(), { min.x + commonWidth, min.y }, { max.x - commonWidth, max.y }, GET_UV_COORDS(centre), colour);
drawList->AddImage(g_texSelect.get(), { max.x - commonWidth, min.y }, max, GET_UV_COORDS(right), colour);
return;
}
auto tl = PIXELS_TO_UV_COORDS(64, 64, 0, 0, 11, 24);
auto tc = PIXELS_TO_UV_COORDS(64, 64, 11, 0, 8, 24);
auto tr = PIXELS_TO_UV_COORDS(64, 64, 19, 0, 11, 24);
auto cl = PIXELS_TO_UV_COORDS(64, 64, 0, 24, 11, 2);
auto cc = PIXELS_TO_UV_COORDS(64, 64, 11, 24, 8, 2);
auto cr = PIXELS_TO_UV_COORDS(64, 64, 19, 24, 11, 2);
auto bl = PIXELS_TO_UV_COORDS(64, 64, 0, 26, 11, 24);
auto bc = PIXELS_TO_UV_COORDS(64, 64, 11, 26, 8, 24);
auto br = PIXELS_TO_UV_COORDS(64, 64, 19, 26, 11, 24);
auto tl = PIXELS_TO_UV_COORDS(64, 64, 34, 0, 11, 24);
auto tc = PIXELS_TO_UV_COORDS(64, 64, 45, 0, 8, 24);
auto tr = PIXELS_TO_UV_COORDS(64, 64, 53, 0, 11, 24);
auto cl = PIXELS_TO_UV_COORDS(64, 64, 34, 24, 11, 2);
auto cc = PIXELS_TO_UV_COORDS(64, 64, 45, 24, 8, 2);
auto cr = PIXELS_TO_UV_COORDS(64, 64, 53, 24, 11, 2);
auto bl = PIXELS_TO_UV_COORDS(64, 64, 34, 26, 11, 24);
auto bc = PIXELS_TO_UV_COORDS(64, 64, 45, 26, 8, 24);
auto br = PIXELS_TO_UV_COORDS(64, 64, 53, 26, 11, 24);
drawList->AddImage(g_texSelectFill.get(), min, { min.x + commonWidth, min.y + commonHeight }, GET_UV_COORDS(tl), colour);
drawList->AddImage(g_texSelectFill.get(), { min.x + commonWidth, min.y }, { max.x - commonWidth, min.y + commonHeight }, GET_UV_COORDS(tc), colour);
drawList->AddImage(g_texSelectFill.get(), { max.x - commonWidth, min.y }, { max.x, min.y + commonHeight }, GET_UV_COORDS(tr), colour);
drawList->AddImage(g_texSelectFill.get(), { min.x, min.y + commonHeight }, { min.x + commonWidth, max.y - commonHeight }, GET_UV_COORDS(cl), colour);
drawList->AddImage(g_texSelectFill.get(), { min.x + commonWidth, min.y + commonHeight }, { max.x - commonWidth, max.y - commonHeight }, GET_UV_COORDS(cc), colour);
drawList->AddImage(g_texSelectFill.get(), { max.x - commonWidth, min.y + commonHeight }, { max.x, max.y - commonHeight }, GET_UV_COORDS(cr), colour);
drawList->AddImage(g_texSelectFill.get(), { min.x, max.y - commonHeight }, { min.x + commonWidth, max.y }, GET_UV_COORDS(bl), colour);
drawList->AddImage(g_texSelectFill.get(), { min.x + commonWidth, max.y - commonHeight }, { max.x - commonWidth, max.y }, GET_UV_COORDS(bc), colour);
drawList->AddImage(g_texSelectFill.get(), { max.x - commonWidth, max.y - commonHeight }, { max.x, max.y }, GET_UV_COORDS(br), colour);
drawList->AddImage(g_texSelect.get(), min, { min.x + commonWidth, min.y + commonHeight }, GET_UV_COORDS(tl), colour);
drawList->AddImage(g_texSelect.get(), { min.x + commonWidth, min.y }, { max.x - commonWidth, min.y + commonHeight }, GET_UV_COORDS(tc), colour);
drawList->AddImage(g_texSelect.get(), { max.x - commonWidth, min.y }, { max.x, min.y + commonHeight }, GET_UV_COORDS(tr), colour);
drawList->AddImage(g_texSelect.get(), { min.x, min.y + commonHeight }, { min.x + commonWidth, max.y - commonHeight }, GET_UV_COORDS(cl), colour);
drawList->AddImage(g_texSelect.get(), { min.x + commonWidth, min.y + commonHeight }, { max.x - commonWidth, max.y - commonHeight }, GET_UV_COORDS(cc), colour);
drawList->AddImage(g_texSelect.get(), { max.x - commonWidth, min.y + commonHeight }, { max.x, max.y - commonHeight }, GET_UV_COORDS(cr), colour);
drawList->AddImage(g_texSelect.get(), { min.x, max.y - commonHeight }, { min.x + commonWidth, max.y }, GET_UV_COORDS(bl), colour);
drawList->AddImage(g_texSelect.get(), { min.x + commonWidth, max.y - commonHeight }, { max.x - commonWidth, max.y }, GET_UV_COORDS(bc), colour);
drawList->AddImage(g_texSelect.get(), { max.x - commonWidth, max.y - commonHeight }, { max.x, max.y }, GET_UV_COORDS(br), colour);
}
void DrawToggleLight(ImVec2 pos, bool isEnabled, float alpha)
{
auto drawList = ImGui::GetForegroundDrawList();
auto drawList = ImGui::GetBackgroundDrawList();
auto lightSize = Scale(14);
auto lightCol = IM_COL32(255, 255, 255, 255 * alpha);
+3 -2
View File
@@ -18,8 +18,7 @@ constexpr float ANNOTATION_FONT_SIZE_MODIFIER = 0.6f;
extern std::unique_ptr<GuestTexture> g_texGeneralWindow;
extern std::unique_ptr<GuestTexture> g_texLight;
extern std::unique_ptr<GuestTexture> g_texSelectFade;
extern std::unique_ptr<GuestTexture> g_texSelectFill;
extern std::unique_ptr<GuestTexture> g_texSelect;
struct TextSegment {
bool annotated;
@@ -42,7 +41,9 @@ void SetOrigin(ImVec2 origin);
void SetScale(ImVec2 scale);
void SetTextSkew(float yCenter, float skewScale);
void ResetTextSkew();
void SetHorizontalMarqueeFade(ImVec2 min, ImVec2 max, float fadeScaleLeft, float fadeScaleRight);
void SetHorizontalMarqueeFade(ImVec2 min, ImVec2 max, float fadeScale);
void SetVerticalMarqueeFade(ImVec2 min, ImVec2 max, float fadeScaleTop, float fadeScaleBottom);
void SetVerticalMarqueeFade(ImVec2 min, ImVec2 max, float fadeScale);
void ResetMarqueeFade();
void SetOutline(float outline);
+327 -146
View File
@@ -54,6 +54,7 @@ static constexpr double CONTAINER_INNER_TIME = SCANLINES_ANIMATION_DURATION + CO
static constexpr double CONTAINER_INNER_DURATION = 15.0;
static constexpr double ALL_ANIMATIONS_FULL_DURATION = CONTAINER_INNER_TIME + CONTAINER_INNER_DURATION;
static constexpr double QUITTING_EXTRA_DURATION = 60.0;
static constexpr double INSTALL_ICONS_FADE_IN_ANIMATION_TIME = 0.0;
static constexpr double INSTALL_ICONS_FADE_IN_ANIMATION_DURATION = 15.0;
@@ -77,7 +78,7 @@ constexpr float CONTAINER_HEIGHT = 246.0f;
constexpr float SIDE_CONTAINER_WIDTH = CONTAINER_WIDTH / 2.0f;
constexpr float BOTTOM_X_GAP = 4.0f;
constexpr float BOTTOM_Y_GAP = 6.0f;
constexpr float BOTTOM_Y_GAP = 5.0f;
constexpr float CONTAINER_BUTTON_WIDTH = 250.0f;
constexpr float CONTAINER_BUTTON_GAP = 9.0f;
constexpr float BUTTON_HEIGHT = 22.0f;
@@ -98,6 +99,7 @@ static double g_arrowCircleCurrentRotation = 0.0;
static double g_appearTime = 0.0;
static double g_disappearTime = DBL_MAX;
static bool g_isDisappearing = false;
static bool g_isQuitting = false;
static std::filesystem::path g_installPath;
static std::filesystem::path g_gameSourcePath;
@@ -118,6 +120,8 @@ static double g_installerEndTime = DBL_MAX;
static float g_installerProgressRatioCurrent = 0.0f;
static std::atomic<float> g_installerProgressRatioTarget = 0.0f;
static std::atomic<bool> g_installerFinished = false;
static std::atomic<bool> g_installerHalted = false;
static std::atomic<bool> g_installerCancelled = false;
static bool g_installerFailed = false;
static std::string g_installerErrorMessage;
@@ -133,9 +137,17 @@ enum class WizardPage
InstallFailed,
};
enum class MessagePromptSource
{
Unknown,
Next,
Back
};
static WizardPage g_firstPage = WizardPage::SelectLanguage;
static WizardPage g_currentPage = g_firstPage;
static std::string g_currentMessagePrompt = "";
static MessagePromptSource g_currentMessagePromptSource = MessagePromptSource::Unknown;
static bool g_currentMessagePromptConfirmation = false;
static std::list<std::filesystem::path> g_currentPickerResults;
static std::atomic<bool> g_currentPickerResultsReady = false;
@@ -151,16 +163,32 @@ static ImVec2 g_joypadAxis = {};
static int g_currentCursorIndex = -1;
static int g_currentCursorDefault = 0;
static bool g_currentCursorAccepted = false;
static bool g_currentCursorBack = false;
static std::vector<std::pair<ImVec2, ImVec2>> g_currentCursorRects;
static std::string g_creditsStr;
class SDLEventListenerForInstaller : public SDLEventListener
{
public:
void OnSDLEvent(SDL_Event *event) override
bool OnSDLEvent(SDL_Event *event) override
{
if (!InstallerWizard::s_isVisible || !g_currentMessagePrompt.empty() || g_currentPickerVisible || !hid::IsInputAllowed())
return;
if (!InstallerWizard::s_isVisible)
return false;
bool noModals = g_currentMessagePrompt.empty() && !g_currentPickerVisible;
if (event->type == SDL_QUIT && g_currentPage == WizardPage::Installing)
{
// Pretend the back button was pressed if the user tried quitting during installation.
// This condition is above the rest of the event processing as we want to block the exit
// button while there's confirmation message is open as well.
if (noModals)
g_currentCursorBack = true;
return true;
}
if (!noModals || !hid::IsInputAllowed())
return false;
constexpr float AxisValueRange = 32767.0f;
constexpr float AxisTapRange = 0.5f;
@@ -185,6 +213,9 @@ public:
case SDL_SCANCODE_KP_ENTER:
g_currentCursorAccepted = (g_currentCursorIndex >= 0);
break;
case SDL_SCANCODE_ESCAPE:
g_currentCursorBack = true;
break;
}
break;
@@ -209,6 +240,9 @@ public:
case SDL_CONTROLLER_BUTTON_A:
g_currentCursorAccepted = (g_currentCursorIndex >= 0);
break;
case SDL_CONTROLLER_BUTTON_B:
g_currentCursorBack = true;
break;
}
break;
@@ -307,6 +341,8 @@ public:
g_currentCursorIndex = newCursorIndex;
}
return false;
}
}
g_sdlEventListenerForInstaller;
@@ -428,7 +464,7 @@ static void ResetCursorRects()
static void DrawBackground()
{
auto &res = ImGui::GetIO().DisplaySize;
auto drawList = ImGui::GetForegroundDrawList();
auto drawList = ImGui::GetBackgroundDrawList();
drawList->AddRectFilled({ 0.0, 0.0 }, res, IM_COL32_BLACK);
}
@@ -447,7 +483,7 @@ static void DrawLeftImage()
int a = std::lround(255.0 * imageAlpha);
GuestTexture *guestTexture = g_installTextures[installTextureIndex % g_installTextures.size()].get();
auto &res = ImGui::GetIO().DisplaySize;
auto drawList = ImGui::GetForegroundDrawList();
auto drawList = ImGui::GetBackgroundDrawList();
ImVec2 min = { g_aspectRatioOffsetX + Scale(IMAGE_X), g_aspectRatioOffsetY + Scale(IMAGE_Y) };
ImVec2 max = { min.x + Scale(IMAGE_WIDTH), min.y + Scale(IMAGE_HEIGHT) };
drawList->AddImage(guestTexture, min, max, ImVec2(0, 0), ImVec2(1, 1), IM_COL32(255, 255, 255, a));
@@ -455,7 +491,7 @@ static void DrawLeftImage()
static void DrawHeaderIconsForInstallPhase(double iconsPosX, double iconsPosY, double iconsScale)
{
auto drawList = ImGui::GetForegroundDrawList();
auto drawList = ImGui::GetBackgroundDrawList();
// Arrow Circle Icon
ImVec2 arrowCircleMin = { g_aspectRatioOffsetX + Scale(iconsPosX - iconsScale / 2), Scale(iconsPosY - iconsScale / 2) };
@@ -510,7 +546,7 @@ static void DrawHeaderIconsForInstallPhase(double iconsPosX, double iconsPosY, d
static void DrawHeaderIcons()
{
auto drawList = ImGui::GetForegroundDrawList();
auto drawList = ImGui::GetBackgroundDrawList();
float iconsPosX = 256.0f;
float iconsPosY = 80.0f;
@@ -546,7 +582,7 @@ static void DrawScanlineBars()
}
auto &res = ImGui::GetIO().DisplaySize;
auto drawList = ImGui::GetForegroundDrawList();
auto drawList = ImGui::GetBackgroundDrawList();
SetShaderModifier(IMGUI_SHADER_MODIFIER_SCANLINE);
@@ -644,7 +680,7 @@ static void DrawScanlineBars()
static void DrawContainer(ImVec2 min, ImVec2 max, bool isTextArea)
{
auto &res = ImGui::GetIO().DisplaySize;
auto drawList = ImGui::GetForegroundDrawList();
auto drawList = ImGui::GetBackgroundDrawList();
double gridAlpha = ComputeMotionInstaller(g_appearTime, g_disappearTime,
isTextArea ? CONTAINER_INNER_TIME : CONTAINER_OUTER_TIME,
@@ -675,7 +711,7 @@ static void DrawContainer(ImVec2 min, ImVec2 max, bool isTextArea)
static void DrawDescriptionContainer()
{
auto &res = ImGui::GetIO().DisplaySize;
auto drawList = ImGui::GetForegroundDrawList();
auto drawList = ImGui::GetBackgroundDrawList();
auto fontSize = Scale(28.0f);
auto annotationFontSize = fontSize * ANNOTATION_FONT_SIZE_MODIFIER;
@@ -756,24 +792,41 @@ static void DrawDescriptionContainer()
);
drawList->PopClipRect();
drawList->PopClipRect();
if (g_currentPage == WizardPage::InstallSucceeded)
{
auto descTextSize = MeasureCentredParagraph(g_seuratFont, fontSize, lineWidth, lineMargin, descriptionText);
auto hedgeDevStr = "hedge-dev";
auto hedgeDevTextSize = g_seuratFont->CalcTextSizeA(fontSize, FLT_MAX, 0, hedgeDevStr);
auto hedgeDevTextMarginX = Scale(15);
auto imageScale = hedgeDevTextSize.x / 3;
auto imageMarginY = Scale(15);
auto colWhite = IM_COL32(255, 255, 255, 255 * textAlpha);
auto containerLeft = g_aspectRatioOffsetX + Scale(CONTAINER_X);
auto containerTop = g_aspectRatioOffsetY + Scale(CONTAINER_Y);
auto containerRight = containerLeft + Scale(CONTAINER_WIDTH);
auto containerBottom = containerTop + Scale(CONTAINER_HEIGHT);
auto marqueeTextSize = g_seuratFont->CalcTextSizeA(fontSize, FLT_MAX, 0, g_creditsStr.c_str());
auto marqueeTextMarginX = Scale(5);
auto marqueeTextMarginY = Scale(15);
ImVec2 marqueeTextPos = { descriptionMax.x, containerBottom - marqueeTextSize.y - marqueeTextMarginY };
ImVec2 marqueeTextMin = { containerLeft, marqueeTextPos.y };
ImVec2 marqueeTextMax = { containerRight, containerBottom };
auto imageScale = hedgeDevTextSize.x / 3;
auto imageMarginY = Scale(2);
ImVec2 imageRegionMin = { containerLeft, textY + descTextSize.y };
ImVec2 imageRegionMax = { containerRight, containerBottom - (marqueeTextMax.y - marqueeTextMin.y) };
ImVec2 imageMin =
{
/* X */ g_aspectRatioOffsetX + Scale(CONTAINER_X) + (Scale(CONTAINER_WIDTH) / 2) - (imageScale / 2) - (hedgeDevTextSize.x / 2) - hedgeDevTextMarginX,
/* Y */ g_aspectRatioOffsetY + Scale(CONTAINER_Y) + (Scale(CONTAINER_HEIGHT) / 2) - (imageScale / 2) + imageMarginY
/* X */ imageRegionMin.x + ((imageRegionMax.x - imageRegionMin.x) / 2) - (imageScale / 2) - (hedgeDevTextSize.x / 2) - hedgeDevTextMarginX,
/* Y */ imageRegionMin.y + ((imageRegionMax.y - imageRegionMin.y) / 2) - (imageScale / 2) - imageMarginY
};
ImVec2 imageMax = { imageMin.x + imageScale, imageMin.y + imageScale };
@@ -789,16 +842,8 @@ static void DrawDescriptionContainer()
hedgeDevStr
);
auto marqueeTextSize = g_seuratFont->CalcTextSizeA(fontSize, FLT_MAX, 0, g_creditsStr.c_str());
auto marqueeTextMarginX = Scale(5);
auto marqueeTextMarginY = Scale(15);
ImVec2 textPos = { descriptionMax.x, g_aspectRatioOffsetY + Scale(CONTAINER_Y) + Scale(CONTAINER_HEIGHT) - marqueeTextSize.y - marqueeTextMarginY };
ImVec2 textMin = { g_aspectRatioOffsetX + Scale(CONTAINER_X), textPos.y };
ImVec2 textMax = { g_aspectRatioOffsetX + Scale(CONTAINER_X) + Scale(CONTAINER_WIDTH), g_aspectRatioOffsetY + Scale(CONTAINER_Y) + Scale(CONTAINER_HEIGHT) };
SetHorizontalMarqueeFade(textMin, textMax, Scale(32));
DrawTextWithMarquee(g_seuratFont, fontSize, textPos, textMin, textMax, colWhite, g_creditsStr.c_str(), g_installerEndTime, 0.9, Scale(200));
SetHorizontalMarqueeFade(marqueeTextMin, marqueeTextMax, Scale(32));
DrawTextWithMarquee(g_seuratFont, fontSize, marqueeTextPos, marqueeTextMin, marqueeTextMax, colWhite, g_creditsStr.c_str(), g_installerEndTime, 0.9, Scale(200));
ResetMarqueeFade();
}
@@ -807,15 +852,47 @@ static void DrawDescriptionContainer()
DrawContainer(sideMin, sideMax, false);
drawList->PopClipRect();
if (g_currentPage != WizardPage::Installing && textAlpha >= 1.0)
EButtonIcon backIcon;
EButtonIcon selectIcon;
if (hid::IsInputDeviceController())
{
auto icon = hid::IsInputDeviceController()
? EButtonIcon::A
: hid::g_inputDevice == hid::EInputDevice::Keyboard
? EButtonIcon::Enter
: EButtonIcon::LMB;
backIcon = EButtonIcon::B;
selectIcon = EButtonIcon::A;
}
else if (hid::g_inputDevice == hid::EInputDevice::Keyboard)
{
backIcon = EButtonIcon::Escape;
selectIcon = EButtonIcon::Enter;
}
else
{
backIcon = EButtonIcon::Escape;
selectIcon = EButtonIcon::LMB;
}
ButtonGuide::Open(Button(Localise("Common_Select"), icon));
if (g_currentPage == WizardPage::InstallSucceeded && textAlpha >= 1.0)
{
ButtonGuide::Open(Button("Common_Select", selectIcon));
}
else if (g_currentPage != WizardPage::Installing && textAlpha >= 1.0)
{
const char *backKey = "Common_Back";
if ((g_currentPage == g_firstPage) || (g_currentPage == WizardPage::InstallFailed))
{
backKey = "Common_Quit";
}
std::array<Button, 2> buttons =
{
Button("Common_Select", selectIcon),
Button(backKey, backIcon)
};
ButtonGuide::Open(buttons);
}
else if (g_currentPage == WizardPage::Installing)
{
ButtonGuide::Open(Button("Common_Cancel", backIcon));
}
else
{
@@ -828,7 +905,7 @@ static void DrawDescriptionContainer()
static void DrawButtonContainer(ImVec2 min, ImVec2 max, int baser, int baseg, float alpha)
{
auto &res = ImGui::GetIO().DisplaySize;
auto drawList = ImGui::GetForegroundDrawList();
auto drawList = ImGui::GetBackgroundDrawList();
SetShaderModifier(IMGUI_SHADER_MODIFIER_SCANLINE_BUTTON);
drawList->AddRectFilledMultiColor(min, max, IM_COL32(baser, baseg + 130, 0, 223 * alpha), IM_COL32(baser, baseg + 130, 0, 178 * alpha), IM_COL32(baser, baseg + 130, 0, 223 * alpha), IM_COL32(baser, baseg + 130, 0, 178 * alpha));
drawList->AddRectFilledMultiColor(min, max, IM_COL32(baser, baseg, 0, 13 * alpha), IM_COL32(baser, baseg, 0, 0), IM_COL32(baser, baseg, 0, 55 * alpha), IM_COL32(baser, baseg, 0, 6 * alpha));
@@ -856,7 +933,7 @@ static void DrawButton(ImVec2 min, ImVec2 max, const char *buttonText, bool sour
buttonPressed = false;
auto &res = ImGui::GetIO().DisplaySize;
auto drawList = ImGui::GetForegroundDrawList();
auto drawList = ImGui::GetBackgroundDrawList();
float alpha = ComputeMotionInstaller(g_appearTime, g_disappearTime, CONTAINER_INNER_TIME, CONTAINER_INNER_DURATION);
if (!buttonEnabled)
{
@@ -952,22 +1029,23 @@ static void DrawSourceButton(ButtonColumn buttonColumn, float yRatio, const char
ImVec2 min = { minX, g_aspectRatioOffsetY + Scale(CONTAINER_Y + CONTAINER_HEIGHT - CONTAINER_BUTTON_GAP - BUTTON_HEIGHT - minusY) };
ImVec2 max = { maxX, g_aspectRatioOffsetY + Scale(CONTAINER_Y + CONTAINER_HEIGHT - CONTAINER_BUTTON_GAP - minusY) };
auto alphaMotion = ComputeMotionInstaller(g_appearTime, g_disappearTime, CONTAINER_INNER_TIME, CONTAINER_INNER_DURATION);
auto lightSize = Scale(14);
DrawButton(min, max, sourceText, true, sourceSet, buttonPressed, (max.x - min.x) - lightSize * 10);
DrawToggleLight({ min.x + lightSize, min.y + ((max.y - min.y) - lightSize) / 2 + Scale(1) }, sourceSet, sourceSet ? 1.0f : 0.5f);
DrawButton(min, max, sourceText, true, sourceSet, buttonPressed, ((max.x - min.x) * 0.7) / g_aspectRatioScale);
DrawToggleLight({ min.x + lightSize, min.y + ((max.y - min.y) - lightSize) / 2 + Scale(1) }, sourceSet, (sourceSet ? 1.0f : 0.5f) * alphaMotion);
}
static void DrawProgressBar(float progressRatio)
{
auto &res = ImGui::GetIO().DisplaySize;
auto drawList = ImGui::GetForegroundDrawList();
auto drawList = ImGui::GetBackgroundDrawList();
float alpha = 1.0;
const uint32_t innerColor0 = IM_COL32(0, 65, 0, 255 * alpha);
const uint32_t innerColor1 = IM_COL32(0, 32, 0, 255 * alpha);
float xPadding = Scale(6.0f);
float yPadding = Scale(3.0f);
ImVec2 min = { g_aspectRatioOffsetX + Scale(CONTAINER_X) + BOTTOM_X_GAP, g_aspectRatioOffsetY + Scale(CONTAINER_Y + CONTAINER_HEIGHT + BOTTOM_Y_GAP) };
float xPadding = Scale(4);
float yPadding = Scale(3);
ImVec2 min = { g_aspectRatioOffsetX + Scale(CONTAINER_X) + BOTTOM_X_GAP + Scale(1), g_aspectRatioOffsetY + Scale(CONTAINER_Y + CONTAINER_HEIGHT + BOTTOM_Y_GAP)};
ImVec2 max = { g_aspectRatioOffsetX + Scale(CONTAINER_X + CONTAINER_WIDTH - BOTTOM_X_GAP), g_aspectRatioOffsetY + Scale(CONTAINER_Y + CONTAINER_HEIGHT + BOTTOM_Y_GAP + BUTTON_HEIGHT) };
DrawButtonContainer(min, max, 0, 0, alpha);
@@ -984,8 +1062,8 @@ static void DrawProgressBar(float progressRatio)
const uint32_t sliderColor0 = IM_COL32(57, 241, 0, 255 * alpha);
const uint32_t sliderColor1 = IM_COL32(2, 106, 0, 255 * alpha);
xPadding += Scale(1.0f);
yPadding += Scale(1.0f);
xPadding += Scale(1.5f);
yPadding += Scale(1.5f);
ImVec2 sliderMin = { min.x + xPadding, min.y + yPadding };
ImVec2 sliderMax = { max.x - xPadding, max.y - yPadding };
@@ -1177,27 +1255,23 @@ static void DrawSourcePickers()
std::list<std::filesystem::path> paths;
if (g_currentPage == WizardPage::SelectGameAndUpdate || g_currentPage == WizardPage::SelectDLC)
{
constexpr float ADD_BUTTON_MAX_TEXT_WIDTH = 160.0f;
constexpr float ADD_BUTTON_MAX_TEXT_WIDTH = 168.0f;
const std::string &addFilesText = Localise("Installer_Button_AddFiles");
float squashRatio;
ImVec2 textSize = ComputeTextSize(g_dfsogeistdFont, addFilesText.c_str(), 20.0f, squashRatio, ADD_BUTTON_MAX_TEXT_WIDTH);
textSize.x += BUTTON_TEXT_GAP;
ImVec2 min = { g_aspectRatioOffsetX + Scale(CONTAINER_X + BOTTOM_X_GAP), g_aspectRatioOffsetY + Scale(CONTAINER_Y + CONTAINER_HEIGHT + BOTTOM_Y_GAP) };
ImVec2 max = { g_aspectRatioOffsetX + Scale(CONTAINER_X + BOTTOM_X_GAP + textSize.x * squashRatio), g_aspectRatioOffsetY + Scale(CONTAINER_Y + CONTAINER_HEIGHT + BOTTOM_Y_GAP + BUTTON_HEIGHT) };
ImVec2 max = { g_aspectRatioOffsetX + Scale(CONTAINER_X + BOTTOM_X_GAP + textSize.x * squashRatio + BUTTON_TEXT_GAP), g_aspectRatioOffsetY + Scale(CONTAINER_Y + CONTAINER_HEIGHT + BOTTOM_Y_GAP + BUTTON_HEIGHT) };
DrawButton(min, max, addFilesText.c_str(), false, true, buttonPressed, ADD_BUTTON_MAX_TEXT_WIDTH);
if (buttonPressed)
{
PickerShow(false);
}
min.x += Scale(BOTTOM_X_GAP + textSize.x * squashRatio);
min.x += Scale(BOTTOM_X_GAP + textSize.x * squashRatio + BUTTON_TEXT_GAP);
const std::string &addFolderText = Localise("Installer_Button_AddFolder");
textSize = ComputeTextSize(g_dfsogeistdFont, addFolderText.c_str(), 20.0f, squashRatio, ADD_BUTTON_MAX_TEXT_WIDTH);
textSize.x += BUTTON_TEXT_GAP;
max.x = min.x + Scale(textSize.x * squashRatio);
max.x = min.x + Scale(textSize.x * squashRatio + BUTTON_TEXT_GAP);
DrawButton(min, max, addFolderText.c_str(), false, true, buttonPressed, ADD_BUTTON_MAX_TEXT_WIDTH);
if (buttonPressed)
{
@@ -1227,8 +1301,9 @@ static void DrawInstallingProgress()
{
if (g_currentPage == WizardPage::Installing)
{
constexpr float ProgressSpeed = 0.1f;
float ratioTarget = g_installerProgressRatioTarget.load();
g_installerProgressRatioCurrent += (4.0f * ImGui::GetIO().DeltaTime * (ratioTarget - g_installerProgressRatioCurrent));
g_installerProgressRatioCurrent += std::min(ratioTarget - g_installerProgressRatioCurrent, ProgressSpeed * ImGui::GetIO().DeltaTime);
DrawProgressBar(g_installerProgressRatioCurrent);
if (g_installerFinished)
@@ -1243,8 +1318,14 @@ static void DrawInstallingProgress()
static void InstallerThread()
{
if (!Installer::install(g_installerSources, g_installPath, false, g_installerJournal, [&]() {
if (!Installer::install(g_installerSources, g_installPath, false, g_installerJournal, std::chrono::seconds(1), [&]() {
g_installerProgressRatioTarget = float(double(g_installerJournal.progressCounter) / double(g_installerJournal.progressTotal));
// If user is being asked for confirmation on cancelling the installation, halt the installer from progressing further.
g_installerHalted.wait(true);
// If user has confirmed they wish to cancel the installation, return false to indicate the installer should fail and stop.
return !g_installerCancelled.load();
}))
{
g_installerFailed = true;
@@ -1254,9 +1335,8 @@ static void InstallerThread()
Installer::rollback(g_installerJournal);
}
// Rest for a bit after finishing the installation, the device is tired
std::this_thread::sleep_for(std::chrono::seconds(1));
g_installerFinished = true;
g_installerCancelled = false;
}
static void InstallerStart()
@@ -1297,104 +1377,162 @@ static bool InstallerParseSources(std::string &errorMessage)
return sourcesParsed;
}
static void DrawNextButton()
static void DrawNavigationButton()
{
if (g_currentPage != WizardPage::Installing)
if (g_currentPage == WizardPage::Installing)
{
bool nextButtonEnabled = !g_isDisappearing;
if (nextButtonEnabled && g_currentPage == WizardPage::SelectGameAndUpdate)
// Navigation buttons are not offered during installation at the moment.
return;
}
bool nextButtonEnabled = !g_isDisappearing && (g_currentPage != WizardPage::Installing);
if (nextButtonEnabled && g_currentPage == WizardPage::SelectGameAndUpdate)
{
nextButtonEnabled = !g_gameSourcePath.empty() && !g_updateSourcePath.empty();
}
bool skipButton = false;
if (g_currentPage == WizardPage::SelectDLC)
{
skipButton = std::all_of(g_dlcSourcePaths.begin(), g_dlcSourcePaths.end(), [](const std::filesystem::path &path) { return path.empty(); });
}
float squashRatio;
constexpr float NAV_BUTTON_MAX_TEXT_WIDTH = 90.0f;
const char *nextButtonKey = "Installer_Button_Next";
if (skipButton)
{
nextButtonKey = "Installer_Button_Skip";
}
else if (g_currentPage == WizardPage::InstallFailed)
{
nextButtonKey = "Installer_Button_Retry";
}
const std::string &nextButtonText = Localise(nextButtonKey);
ImVec2 nextTextSize = ComputeTextSize(g_newRodinFont, nextButtonText.c_str(), 20.0f, squashRatio, NAV_BUTTON_MAX_TEXT_WIDTH);
ImVec2 min = { g_aspectRatioOffsetX + Scale(CONTAINER_X + CONTAINER_WIDTH - nextTextSize.x * squashRatio - BOTTOM_X_GAP - BUTTON_TEXT_GAP), g_aspectRatioOffsetY + Scale(CONTAINER_Y + CONTAINER_HEIGHT + BOTTOM_Y_GAP) };
ImVec2 max = { g_aspectRatioOffsetX + Scale(CONTAINER_X + CONTAINER_WIDTH - BOTTOM_X_GAP), g_aspectRatioOffsetY + Scale(CONTAINER_Y + CONTAINER_HEIGHT + BOTTOM_Y_GAP + BUTTON_HEIGHT) };
bool buttonPressed = false;
DrawButton(min, max, nextButtonText.c_str(), false, nextButtonEnabled, buttonPressed, NAV_BUTTON_MAX_TEXT_WIDTH);
if (buttonPressed)
{
XexPatcher::Result patcherResult;
if (g_currentPage == WizardPage::SelectGameAndUpdate && (patcherResult = Installer::checkGameUpdateCompatibility(g_gameSourcePath, g_updateSourcePath), patcherResult != XexPatcher::Result::Success))
{
nextButtonEnabled = !g_gameSourcePath.empty() && !g_updateSourcePath.empty();
g_currentMessagePrompt = Localise("Installer_Message_IncompatibleGameData");
g_currentMessagePromptConfirmation = false;
}
bool skipButton = false;
if (g_currentPage == WizardPage::SelectDLC)
else if (g_currentPage == WizardPage::SelectDLC)
{
skipButton = std::all_of(g_dlcSourcePaths.begin(), g_dlcSourcePaths.end(), [](const std::filesystem::path &path) { return path.empty(); });
}
float squashRatio;
constexpr float NEXT_BUTTON_MAX_TEXT_WIDTH = 100.0f;
const std::string &buttonText = Localise(skipButton ? "Installer_Button_Skip" : "Installer_Button_Next");
ImVec2 textSize = ComputeTextSize(g_newRodinFont, buttonText.c_str(), 20.0f, squashRatio, NEXT_BUTTON_MAX_TEXT_WIDTH);
textSize.x += BUTTON_TEXT_GAP;
ImVec2 min = { g_aspectRatioOffsetX + Scale(CONTAINER_X + CONTAINER_WIDTH - textSize.x * squashRatio - BOTTOM_X_GAP), g_aspectRatioOffsetY + Scale(CONTAINER_Y + CONTAINER_HEIGHT + BOTTOM_Y_GAP) };
ImVec2 max = { g_aspectRatioOffsetX + Scale(CONTAINER_X + CONTAINER_WIDTH - BOTTOM_X_GAP), g_aspectRatioOffsetY + Scale(CONTAINER_Y + CONTAINER_HEIGHT + BOTTOM_Y_GAP + BUTTON_HEIGHT) };
bool buttonPressed = false;
DrawButton(min, max, buttonText.c_str(), false, nextButtonEnabled, buttonPressed, NEXT_BUTTON_MAX_TEXT_WIDTH);
if (buttonPressed)
{
XexPatcher::Result patcherResult;
if (g_currentPage == WizardPage::SelectGameAndUpdate && (patcherResult = Installer::checkGameUpdateCompatibility(g_gameSourcePath, g_updateSourcePath), patcherResult != XexPatcher::Result::Success))
// Check if any of the DLC was not specified.
bool dlcIncomplete = false;
for (int i = 0; (i < int(DLC::Count)) && !dlcIncomplete; i++)
{
g_currentMessagePrompt = Localise("Installer_Message_IncompatibleGameData");
if (g_dlcSourcePaths[i].empty() && !g_dlcInstalled[i])
{
dlcIncomplete = true;
}
}
bool dlcInstallerMode = g_gameSourcePath.empty();
std::string sourcesErrorMessage;
if (!InstallerParseSources(sourcesErrorMessage))
{
// Some of the sources that were provided to the installer are not valid. Restart the file selection process.
std::stringstream stringStream;
stringStream << Localise("Installer_Message_InvalidFiles");
if (!sourcesErrorMessage.empty()) {
stringStream << std::endl << std::endl << sourcesErrorMessage;
}
g_currentMessagePrompt = stringStream.str();
g_currentMessagePromptConfirmation = false;
g_currentPage = dlcInstallerMode ? WizardPage::SelectDLC : WizardPage::SelectGameAndUpdate;
}
else if (g_currentPage == WizardPage::SelectDLC)
else if (dlcIncomplete && !dlcInstallerMode)
{
// Check if any of the DLC was not specified.
bool dlcIncomplete = false;
for (int i = 0; (i < int(DLC::Count)) && !dlcIncomplete; i++)
{
if (g_dlcSourcePaths[i].empty() && !g_dlcInstalled[i])
{
dlcIncomplete = true;
}
}
bool dlcInstallerMode = g_gameSourcePath.empty();
std::string sourcesErrorMessage;
if (!InstallerParseSources(sourcesErrorMessage))
{
// Some of the sources that were provided to the installer are not valid. Restart the file selection process.
std::stringstream stringStream;
stringStream << Localise("Installer_Message_InvalidFiles");
if (!sourcesErrorMessage.empty()) {
stringStream << std::endl << std::endl << sourcesErrorMessage;
}
g_currentMessagePrompt = stringStream.str();
g_currentMessagePromptConfirmation = false;
g_currentPage = dlcInstallerMode ? WizardPage::SelectDLC : WizardPage::SelectGameAndUpdate;
}
else if (dlcIncomplete && !dlcInstallerMode)
{
// Not all the DLC was specified, we show a prompt and await a confirmation before starting the installer.
g_currentMessagePrompt = Localise("Installer_Message_DLCWarning");
g_currentMessagePromptConfirmation = true;
}
else if (skipButton && dlcInstallerMode)
{
// Nothing was selected and the installer was in DLC mode, just close it.
g_isDisappearing = true;
g_disappearTime = ImGui::GetTime();
}
else
{
g_currentPage = WizardPage::CheckSpace;
}
// Not all the DLC was specified, we show a prompt and await a confirmation before starting the installer.
g_currentMessagePrompt = Localise("Installer_Message_DLCWarning");
g_currentMessagePromptSource = MessagePromptSource::Next;
g_currentMessagePromptConfirmation = true;
}
else if (g_currentPage == WizardPage::CheckSpace)
{
InstallerStart();
}
else if (g_currentPage == WizardPage::InstallSucceeded)
else if (skipButton && dlcInstallerMode)
{
// Nothing was selected and the installer was in DLC mode, just close it.
g_isDisappearing = true;
g_disappearTime = ImGui::GetTime();
}
else if (g_currentPage == WizardPage::InstallFailed)
{
g_currentPage = g_firstPage;
}
else
{
g_currentPage = WizardPage(int(g_currentPage) + 1);
g_currentPage = WizardPage::CheckSpace;
}
}
else if (g_currentPage == WizardPage::CheckSpace)
{
InstallerStart();
}
else if (g_currentPage == WizardPage::InstallSucceeded)
{
g_isDisappearing = true;
g_disappearTime = ImGui::GetTime();
}
else if (g_currentPage == WizardPage::InstallFailed)
{
g_currentPage = g_firstPage;
}
else
{
g_currentPage = WizardPage(int(g_currentPage) + 1);
}
}
}
static void CheckCancelAction()
{
if (!g_currentCursorBack)
{
return;
}
g_currentCursorBack = false;
if (g_currentPage == WizardPage::InstallSucceeded)
{
// Nothing to back out on this page.
return;
}
if (g_currentPage == WizardPage::Installing && g_installerCancelled)
{
// Installer's already been cancelled, no need for more confirmations.
return;
}
Game_PlaySound("sys_actstg_pausecansel");
if (g_currentPage == g_firstPage || g_currentPage == WizardPage::InstallFailed)
{
// Ask for confirmation if user wants to quit the installer.
g_currentMessagePrompt = Localise("Installer_Message_Quit");
g_currentMessagePromptSource = MessagePromptSource::Back;
g_currentMessagePromptConfirmation = true;
}
else if (g_currentPage == WizardPage::Installing)
{
// Ask for confirmation if the user wants to cancel the installation.
g_currentMessagePrompt = Localise("Installer_Message_Cancel");
g_currentMessagePromptSource = MessagePromptSource::Back;
g_currentMessagePromptConfirmation = true;
// Indicate to the installer that all progress should stop until the user confirms if they wish to cancel.
g_installerHalted = true;
}
else if (int(g_currentPage) > 0)
{
// Just go back to the previous page.
g_currentPage = WizardPage(int(g_currentPage) - 1);
}
}
@@ -1403,7 +1541,7 @@ static void DrawHorizontalBorder(bool bottomBorder)
const uint32_t FADE_COLOR_LEFT = IM_COL32(155, 155, 155, 0);
const uint32_t SOLID_COLOR = IM_COL32(155, 200, 155, 255);
const uint32_t FADE_COLOR_RIGHT = IM_COL32(155, 225, 155, 0);
auto drawList = ImGui::GetForegroundDrawList();
auto drawList = ImGui::GetBackgroundDrawList();
double borderScale = 1.0 - ComputeMotionInstaller(g_appearTime, g_disappearTime, CONTAINER_LINE_ANIMATION_TIME, CONTAINER_LINE_ANIMATION_DURATION);
float midX = g_aspectRatioOffsetX + Scale(CONTAINER_X + CONTAINER_WIDTH / 5);
float minX = std::lerp(g_aspectRatioOffsetX + Scale(CONTAINER_X - BORDER_SIZE - BORDER_OVERSHOOT), midX, borderScale);
@@ -1435,7 +1573,7 @@ static void DrawVerticalBorder(bool rightBorder)
{
const uint32_t SOLID_COLOR = IM_COL32(155, rightBorder ? 225 : 155, 155, 255);
const uint32_t FADE_COLOR = IM_COL32(155, rightBorder ? 225 : 155, 155, 0);
auto drawList = ImGui::GetForegroundDrawList();
auto drawList = ImGui::GetBackgroundDrawList();
double borderScale = 1.0 - ComputeMotionInstaller(g_appearTime, g_disappearTime, CONTAINER_LINE_ANIMATION_TIME, CONTAINER_LINE_ANIMATION_DURATION);
float minX = g_aspectRatioOffsetX + (rightBorder ? Scale(CONTAINER_X + CONTAINER_WIDTH) : Scale(CONTAINER_X - BORDER_SIZE));
float maxX = minX + Scale(BORDER_SIZE);
@@ -1491,17 +1629,52 @@ static void DrawMessagePrompt()
if (messageWindowReturned)
{
if (g_currentMessagePromptConfirmation && (g_currentMessageResult == 0) && (g_currentPage == WizardPage::SelectDLC))
if (g_currentMessagePromptConfirmation && (g_currentMessageResult == 0))
{
// If user confirms the message prompt that they wish to skip installing the DLC, proceed to the next step.
g_currentPage = WizardPage::CheckSpace;
if (g_currentMessagePromptSource == MessagePromptSource::Back)
{
if (g_currentPage == WizardPage::Installing)
{
// If user confirms they wish to cancel the installation, notify the installation thread it must finish as soon as possible.
g_installerCancelled = true;
}
else
{
// In all cases, proceed to just quit the application.
g_isQuitting = true;
g_isDisappearing = true;
g_disappearTime = ImGui::GetTime();
}
}
else if (g_currentPage == WizardPage::SelectDLC)
{
// If user confirms the message prompt that they wish to skip installing the DLC, proceed to the next step.
g_currentPage = WizardPage::CheckSpace;
}
}
if (g_currentMessagePromptSource == MessagePromptSource::Back)
{
// Regardless of the confirmation, the installation thread must be resumed.
g_installerHalted = false;
g_installerHalted.notify_all();
}
g_currentMessagePrompt.clear();
g_currentMessagePromptSource = MessagePromptSource::Unknown;
g_currentMessageResult = -1;
}
}
static void PickerDrawForeground()
{
if (g_currentPickerVisible)
{
auto drawList = ImGui::GetBackgroundDrawList();
drawList->AddRectFilled({ 0.0f, 0.0f }, ImGui::GetIO().DisplaySize, IM_COL32(0, 0, 0, 190));
}
}
static void PickerCheckTutorial()
{
if (!g_pickerTutorialTriggered || !g_currentMessagePrompt.empty())
@@ -1556,10 +1729,10 @@ void InstallerWizard::Init()
g_pulseInstall = LOAD_ZSTD_TEXTURE(g_pulse_install);
g_upHedgeDev = LOAD_ZSTD_TEXTURE(g_hedgedev);
for (int i = 0; i < g_creditsSize; i++)
for (int i = 0; i < g_credits.size(); i++)
{
g_creditsStr += g_credits[i];
g_creditsStr += " ";
g_creditsStr += " ";
}
}
@@ -1579,15 +1752,23 @@ void InstallerWizard::Draw()
DrawSourcePickers();
DrawSources();
DrawInstallingProgress();
DrawNextButton();
DrawNavigationButton();
CheckCancelAction();
DrawBorders();
DrawMessagePrompt();
PickerDrawForeground();
PickerCheckTutorial();
PickerCheckResults();
if (g_isDisappearing)
{
const double disappearDuration = ALL_ANIMATIONS_FULL_DURATION / 60.0;
double disappearDuration = ALL_ANIMATIONS_FULL_DURATION / 60.0;
if (g_isQuitting)
{
// Add some extra waiting time when quitting the application altogether.
disappearDuration += QUITTING_EXTRA_DURATION / 60.0;
}
if (ImGui::GetTime() > (g_disappearTime + disappearDuration))
{
s_isVisible = false;
@@ -1669,5 +1850,5 @@ bool InstallerWizard::Run(std::filesystem::path installPath, bool skipGame)
InstallerWizard::Shutdown();
EmbeddedPlayer::Shutdown();
return true;
return !g_isQuitting;
}
+43 -17
View File
@@ -22,7 +22,9 @@ static bool g_isAwaitingResult = false;
static bool g_isClosing = false;
static bool g_isControlsVisible = false;
static double g_rowSelectionTime;
static int g_selectedRowIndex;
static int g_prevSelectedRowIndex;
static int g_foregroundCount;
static bool g_upWasHeld;
@@ -46,10 +48,10 @@ int g_cancelButtonIndex;
class SDLEventListenerForMessageWindow : public SDLEventListener
{
public:
void OnSDLEvent(SDL_Event* event) override
bool OnSDLEvent(SDL_Event* event) override
{
if (App::s_isInit || !MessageWindow::s_isVisible || !hid::IsInputAllowed())
return;
return false;
constexpr float axisValueRange = 32767.0f;
constexpr float axisTapRange = 0.5f;
@@ -140,13 +142,15 @@ public:
break;
}
}
return false;
}
}
g_sdlEventListenerForMessageWindow;
bool DrawContainer(float appearTime, ImVec2 centre, ImVec2 max, bool isForeground = true)
{
auto drawList = ImGui::GetForegroundDrawList();
auto drawList = ImGui::GetBackgroundDrawList();
ImVec2 _min = { centre.x - max.x, centre.y - max.y };
ImVec2 _max = { centre.x + max.x, centre.y + max.y };
@@ -197,7 +201,7 @@ bool DrawContainer(float appearTime, ImVec2 centre, ImVec2 max, bool isForegroun
void DrawButton(int rowIndex, float yOffset, float width, float height, std::string& text)
{
auto drawList = ImGui::GetForegroundDrawList();
auto drawList = ImGui::GetBackgroundDrawList();
auto clipRectMin = drawList->GetClipRectMin();
auto clipRectMax = drawList->GetClipRectMax();
@@ -208,7 +212,13 @@ void DrawButton(int rowIndex, float yOffset, float width, float height, std::str
bool isSelected = rowIndex == g_selectedRowIndex;
if (isSelected)
DrawSelectionContainer(min, max, true);
{
auto prevItemOffset = (g_prevSelectedRowIndex - g_selectedRowIndex) * height;
auto animRatio = std::clamp((ImGui::GetTime() - g_rowSelectionTime) * 60.0 / 8.0, 0.0, 1.0);
prevItemOffset *= pow(1.0 - animRatio, 3.0);
DrawSelectionContainer({ min.x, min.y + prevItemOffset }, { max.x, max.y + prevItemOffset }, true);
}
auto fontSize = Scale(28);
auto textSize = g_fntSeurat->CalcTextSizeA(fontSize, FLT_MAX, 0, text.c_str());
@@ -235,7 +245,7 @@ void DrawNextButtonGuide(bool isController, bool isKeyboard)
if (App::s_isInit)
icon = EButtonIcon::A;
ButtonGuide::Open(Button(Localise("Common_Next"), icon));
ButtonGuide::Open(Button("Common_Next", icon));
}
static void ResetSelection()
@@ -265,7 +275,7 @@ void MessageWindow::Draw()
if (!s_isVisible)
return;
auto drawList = ImGui::GetForegroundDrawList();
auto drawList = ImGui::GetBackgroundDrawList();
auto& res = ImGui::GetIO().DisplaySize;
ImVec2 centre = { res.x / 2, res.y / 2 };
@@ -292,9 +302,9 @@ void MessageWindow::Draw()
if (Config::Language == ELanguage::Japanese)
{
textMarginX -= Scale(2.5f);
textMarginY -= Scale(7.5f);
textMarginY -= Scale(2.0f);
textY += Scale(lines.size() % 2 == 0 ? 8.5f : 15.5f);
textY += Scale(lines.size() % 2 == 0 ? 1.5f : 8.0f);
}
bool isController = hid::IsInputDeviceController();
@@ -375,6 +385,8 @@ void MessageWindow::Draw()
bool scrollUp = !g_upWasHeld && upIsHeld;
bool scrollDown = !g_downWasHeld && downIsHeld;
auto prevSelectedRowIndex = g_selectedRowIndex;
if (scrollUp)
{
--g_selectedRowIndex;
@@ -391,26 +403,34 @@ void MessageWindow::Draw()
if (scrollUp || scrollDown)
{
Game_PlaySound("sys_actstg_pausecursor");
g_rowSelectionTime = ImGui::GetTime();
g_prevSelectedRowIndex = prevSelectedRowIndex;
g_joypadAxis.y = 0;
}
g_upWasHeld = upIsHeld;
g_downWasHeld = downIsHeld;
if (isController || (isKeyboard && App::s_isInit))
auto selectIcon = EButtonIcon::A;
auto backIcon = EButtonIcon::B;
if (isController || isKeyboard)
{
if (isKeyboard && !App::s_isInit)
{
// Only display keyboard prompt during installer.
selectIcon = EButtonIcon::Enter;
backIcon = EButtonIcon::Escape;
}
std::array<Button, 2> buttons =
{
Button(Localise("Common_Select"), EButtonIcon::A),
Button(Localise("Common_Back"), EButtonIcon::B),
Button("Common_Select", selectIcon),
Button("Common_Back", backIcon),
};
ButtonGuide::Open(buttons);
}
else // Only display keyboard prompt during installer.
{
ButtonGuide::Open(Button(Localise("Common_Select"), EButtonIcon::Enter));
}
if (g_isDeclined)
{
@@ -448,7 +468,13 @@ void MessageWindow::Draw()
}
}
ButtonGuide::Open(Button(Localise("Common_Select"), EButtonIcon::LMB));
std::array<Button, 2> buttons =
{
Button(Localise("Common_Select"), EButtonIcon::LMB),
Button(Localise("Common_Back"), EButtonIcon::Escape),
};
ButtonGuide::Open(buttons);
}
if (g_selectedRowIndex != -1 && g_isAccepted)
+269 -71
View File
@@ -1,5 +1,6 @@
#include "options_menu.h"
#include "options_menu_thumbnails.h"
#include "tv_static.h"
#include <api/SWA.h>
#include <apu/audio.h>
@@ -18,6 +19,7 @@
#include <app.h>
#include <decompressor.h>
#include <exports.h>
#include <sdl_listener.h>
#include <res/images/options_menu/miles_electric.dds.h>
@@ -55,6 +57,8 @@ static constexpr float SETTINGS_NARROW_GRID_COUNT = 70.0f;
static constexpr float INFO_NARROW_GRID_COUNT = 34.0f;
static constexpr float PADDING_NARROW_GRID_COUNT = 1.0f;
static constexpr float INFO_TEXT_MARQUEE_DELAY = 1.2f;
static constexpr int32_t g_categoryCount = 4;
static int32_t g_categoryIndex;
static ImVec2 g_categoryAnimMin;
@@ -99,6 +103,24 @@ static double g_appearTime = 0.0;
static std::unique_ptr<GuestTexture> g_upMilesElectric;
static float g_rightStickY;
class SDLEventListenerForOptionsMenu : public SDLEventListener
{
public:
bool OnSDLEvent(SDL_Event* event) override
{
if (!OptionsMenu::s_isVisible || !hid::IsInputAllowed())
return false;
if (event->type == SDL_CONTROLLERAXISMOTION && event->caxis.axis == SDL_CONTROLLER_AXIS_RIGHTY)
g_rightStickY = event->caxis.value / 32767.0f;
return false;
}
}
g_sdlEventListenerForOptionsMenu;
static void DrawTitle()
{
static constexpr double fadeOffset = 3.0;
@@ -125,7 +147,7 @@ static void DrawTitle()
g_titleAnimBegin = false;
}
auto drawList = ImGui::GetForegroundDrawList();
auto drawList = ImGui::GetBackgroundDrawList();
auto x = Scale(122);
auto y = Scale(56);
@@ -236,7 +258,7 @@ static void DrawScanlineBars()
float height = Scale(105.0f);
auto& res = ImGui::GetIO().DisplaySize;
auto drawList = ImGui::GetForegroundDrawList();
auto drawList = ImGui::GetBackgroundDrawList();
if (OptionsMenu::s_pauseMenuType != SWA::eMenuType_WorldMap)
{
@@ -339,7 +361,7 @@ static float AlignToNextGrid(float value)
return round(value / GRID_SIZE) * GRID_SIZE;
}
static void DrawContainer(ImVec2 min, ImVec2 max)
static void DrawContainer(ImVec2 min, ImVec2 max, bool drawRightOutline)
{
double containerHeight = g_isStage ? 1.0 : ComputeMotion(g_appearTime, 0.0, CONTAINER_LINE_ANIMATION_DURATION);
@@ -348,7 +370,7 @@ static void DrawContainer(ImVec2 min, ImVec2 max)
max.y = Lerp(center, max.y, containerHeight);
auto& res = ImGui::GetIO().DisplaySize;
auto drawList = ImGui::GetForegroundDrawList();
auto drawList = ImGui::GetBackgroundDrawList();
double outerAlpha = g_isStage ? 1.0 : ComputeMotion(g_appearTime, CONTAINER_OUTER_TIME, CONTAINER_OUTER_DURATION);
double innerAlpha = g_isStage ? 1.0 : ComputeMotion(g_appearTime, CONTAINER_INNER_TIME, CONTAINER_INNER_DURATION);
@@ -365,10 +387,10 @@ static void DrawContainer(ImVec2 min, ImVec2 max)
SetShaderModifier(IMGUI_SHADER_MODIFIER_CHECKERBOARD);
drawList->AddRectFilled(min, { min.x + gridSize, max.y }, outerColor); // Container outline left
drawList->AddRectFilled({ max.x - gridSize, min.y }, max, outerColor); // Container outline right
drawList->AddRectFilled({ min.x + gridSize, min.y }, { max.x - gridSize, min.y + gridSize }, outerColor); // Container outline top
drawList->AddRectFilled({ min.x + gridSize, max.y - gridSize }, { max.x - gridSize, max.y }, outerColor); // Container outline bottom
drawList->AddRectFilled({ min.x, min.y + gridSize }, { min.x + gridSize, max.y - gridSize }, outerColor); // Container outline left
drawList->AddRectFilled({ max.x - gridSize, min.y + gridSize }, { max.x, max.y - gridSize }, drawRightOutline ? outerColor : innerColor); // Container outline right
drawList->AddRectFilled(min, { max.x, min.y + gridSize }, outerColor); // Container outline top
drawList->AddRectFilled({ min.x, max.y - gridSize }, max, outerColor); // Container outline bottom
drawList->AddRectFilled({ min.x + gridSize, min.y + gridSize }, { max.x - gridSize, max.y - gridSize }, innerColor); // Inner container
@@ -452,40 +474,47 @@ static bool DrawCategories()
Game_PlaySound("sys_actstg_score");
}
auto drawList = ImGui::GetForegroundDrawList();
auto drawList = ImGui::GetBackgroundDrawList();
auto clipRectMin = drawList->GetClipRectMin();
auto clipRectMax = drawList->GetClipRectMax();
constexpr float NARROW_PADDING_GRID_COUNT = 1.5f;
constexpr float WIDE_PADDING_GRID_COUNT = 3.0f;
float gridSize = Scale(GRID_SIZE);
float textPadding = gridSize * Lerp(NARROW_PADDING_GRID_COUNT, WIDE_PADDING_GRID_COUNT, g_aspectRatioNarrowScale);
float tabPadding = gridSize;
float size = Scale(32.0f);
ImVec2 textSizes[g_categoryCount];
float tabWidthSum = 0.0f;
float clipRectWidth = clipRectMax.x - clipRectMin.x;
float textWidthSum = 0.0f;
for (size_t i = 0; i < g_categoryCount; i++)
{
textSizes[i] = g_dfsogeistdFont->CalcTextSizeA(size, FLT_MAX, 0.0f, GetCategory(i).c_str());
tabWidthSum += textSizes[i].x + textPadding * 2.0f;
textWidthSum += textSizes[i].x;
}
float textSquashRatio = 1.0f;
float maxTextWidthSum = clipRectWidth - (gridSize * 4.0f * (g_categoryCount - 1));
if (textWidthSum > maxTextWidthSum)
{
textSquashRatio = maxTextWidthSum / textWidthSum;
for (auto& textSize : textSizes)
textSize.x *= textSquashRatio;
textWidthSum = maxTextWidthSum;
}
tabWidthSum += (g_categoryCount - 1) * tabPadding;
float tabHeight = gridSize * 4.0f;
float xOffset = ((clipRectMax.x - clipRectMin.x) - tabWidthSum) / 2.0f;
float textPadding = (clipRectWidth - textWidthSum) / (g_categoryCount + 1.0f);
float xOffset = textPadding;
xOffset -= (1.0 - motion) * gridSize * 4.0;
ImVec2 minVec[g_categoryCount];
ImVec2 textPositions[g_categoryCount];
for (size_t i = 0; i < g_categoryCount; i++)
{
ImVec2 min = { clipRectMin.x + xOffset, clipRectMin.y };
float tabPadding = std::min(textPadding / 2.0f, gridSize * 3.0f);
xOffset += textSizes[i].x + textPadding * 2.0f;
ImVec2 max = { clipRectMin.x + xOffset, clipRectMin.y + tabHeight };
xOffset += tabPadding;
ImVec2 min = { clipRectMin.x + xOffset - tabPadding, clipRectMin.y };
ImVec2 max = { min.x + textSizes[i].x + tabPadding * 2.0f, min.y + tabHeight};
if (g_categoryIndex == i)
{
@@ -549,21 +578,23 @@ static bool DrawCategories()
SetShaderModifier(IMGUI_SHADER_MODIFIER_NONE);
}
min.x += textPadding;
// Store to draw again later, otherwise the tab background gets drawn on top of text during the animation.
minVec[i] = min;
textPositions[i] = { clipRectMin.x + xOffset, clipRectMin.y };
xOffset += textSizes[i].x + textPadding;
}
SetScale({ textSquashRatio, 1.0f });
for (size_t i = 0; i < g_categoryCount; i++)
{
auto& min = minVec[i];
auto& pos = textPositions[i];
uint8_t alpha = (i == g_categoryIndex ? 235 : 128) * motion;
SetOrigin({ pos.x, pos.y });
SetGradient
(
min,
{ min.x + textSizes[i].x, min.y + textSizes[i].y },
pos,
{ pos.x + textSizes[i].x, pos.y + textSizes[i].y },
IM_COL32(128, 255, 0, alpha),
IM_COL32(255, 192, 0, alpha)
);
@@ -572,17 +603,19 @@ static bool DrawCategories()
(
g_dfsogeistdFont,
size,
min,
pos,
IM_COL32_WHITE,
GetCategory(i).c_str(),
4,
IM_COL32_BLACK,
IMGUI_SHADER_MODIFIER_CATEGORY_BEVEL
);
ResetGradient();
}
SetScale({ 1.0f, 1.0f });
SetOrigin({ 0.0f, 0.0f });
ResetGradient();
if (g_isStage || (ImGui::GetTime() - g_appearTime) >= (CONTAINER_FULL_DURATION / 60.0))
{
drawList->PushClipRect({ clipRectMin.x, clipRectMin.y + gridSize * 6.0f }, { clipRectMax.x - gridSize, clipRectMax.y - gridSize });
@@ -599,7 +632,7 @@ static void DrawSelectionArrows(ImVec2 min, ImVec2 max, bool isLeftTapped, bool
static bool isLeftArrowMotion = false;
static bool isRightArrowMotion = false;
auto drawList = ImGui::GetForegroundDrawList();
auto drawList = ImGui::GetBackgroundDrawList();
auto gridSize = Scale(GRID_SIZE);
auto width = gridSize * 2.5f;
auto padding = gridSize;
@@ -723,7 +756,7 @@ static void DrawConfigOption(int32_t rowIndex, float yOffset, ConfigDef<T>* conf
bool isAccessible, std::string* inaccessibleReason = nullptr,
T valueMin = T(0), T valueCenter = T(0.5), T valueMax = T(1), bool isSlider = true)
{
auto drawList = ImGui::GetForegroundDrawList();
auto drawList = ImGui::GetBackgroundDrawList();
auto clipRectMin = drawList->GetClipRectMin();
auto clipRectMax = drawList->GetClipRectMax();
auto& padState = SWA::CInputState::GetInstance()->GetPadState();
@@ -1104,6 +1137,10 @@ static void DrawConfigOption(int32_t rowIndex, float yOffset, ConfigDef<T>* conf
valueText = fmt::format("{}x{}", GameWindow::s_width, GameWindow::s_height);
}
}
else if (config == &Config::Monitor)
{
valueText = fmt::format("{}", config->Value + 1);
}
else
{
valueText = fmt::format("{}", config->Value);
@@ -1147,10 +1184,12 @@ static void DrawConfigOption(int32_t rowIndex, float yOffset, ConfigDef<T>* conf
static void DrawConfigOptions()
{
auto drawList = ImGui::GetForegroundDrawList();
auto drawList = ImGui::GetBackgroundDrawList();
auto clipRectMin = drawList->GetClipRectMin();
auto clipRectMax = drawList->GetClipRectMax();
drawList->PushClipRect({ clipRectMin.x, clipRectMin.y }, { clipRectMax.x, clipRectMax.y - Scale(5.0f) });
g_selectedItem = nullptr;
float gridSize = Scale(GRID_SIZE);
@@ -1172,7 +1211,7 @@ static void DrawConfigOptions()
DrawConfigOption(rowCount++, yOffset, &Config::Hints, !isStage, cmnReason);
DrawConfigOption(rowCount++, yOffset, &Config::ControlTutorial, !isStage, cmnReason);
DrawConfigOption(rowCount++, yOffset, &Config::AchievementNotifications, true);
DrawConfigOption(rowCount++, yOffset, &Config::TimeOfDayTransition, true);
DrawConfigOption(rowCount++, yOffset, &Config::TimeOfDayTransition, !Config::UseArrowsForTimeOfDayTransition);
break;
case 1: // INPUT
@@ -1220,7 +1259,7 @@ static void DrawConfigOptions()
DrawConfigOption(rowCount++, yOffset, &Config::MotionBlur, true);
DrawConfigOption(rowCount++, yOffset, &Config::XboxColorCorrection, true);
DrawConfigOption(rowCount++, yOffset, &Config::CutsceneAspectRatio, true);
DrawConfigOption(rowCount++, yOffset, &Config::UIScaleMode, true);
DrawConfigOption(rowCount++, yOffset, &Config::UIAlignmentMode, true);
break;
}
@@ -1239,22 +1278,62 @@ static void DrawConfigOptions()
int32_t prevSelectedRowIndex = g_selectedRowIndex;
auto time = ImGui::GetTime();
auto fastScroll = (time - g_lastTappedTime) > 0.6;
auto fastScrollSpeed = 1.0 / 3.5;
auto fastScrollEncounteredEdge = false;
static auto fastScrollSpeedUp = false;
if (scrollUp || scrollDown)
g_lastTappedTime = time;
if (!upIsHeld && !downIsHeld)
fastScrollSpeedUp = false;
if (fastScrollSpeedUp)
fastScrollSpeed /= 2;
if (fastScroll)
{
if ((time - g_lastIncrementTime) < fastScrollSpeed)
{
fastScroll = false;
}
else
{
g_lastIncrementTime = time;
scrollUp = upIsHeld;
scrollDown = downIsHeld;
}
}
if (scrollUp)
{
--g_selectedRowIndex;
if (g_selectedRowIndex < 0)
g_selectedRowIndex = rowCount - 1;
{
g_selectedRowIndex = fastScroll ? 0 : rowCount - 1;
fastScrollEncounteredEdge = fastScroll;
fastScrollSpeedUp = false;
}
}
else if (scrollDown)
{
++g_selectedRowIndex;
if (g_selectedRowIndex >= rowCount)
g_selectedRowIndex = 0;
{
g_selectedRowIndex = fastScroll ? rowCount - 1 : 0;
fastScrollEncounteredEdge = fastScroll;
fastScrollSpeedUp = false;
}
}
if (scrollUp || scrollDown)
if ((scrollUp || scrollDown) && !fastScrollEncounteredEdge)
{
g_rowSelectionTime = ImGui::GetTime();
g_rowSelectionTime = time;
g_prevSelectedRowIndex = prevSelectedRowIndex;
Game_PlaySound("sys_worldmap_cursor");
}
@@ -1270,24 +1349,32 @@ static void DrawConfigOptions()
{
g_firstVisibleRowIndex = g_selectedRowIndex;
disableMoveAnimation = true;
if (g_selectedRowIndex > 0)
fastScrollSpeedUp = true;
}
if (g_firstVisibleRowIndex + visibleRowCount - 1 < g_selectedRowIndex)
{
g_firstVisibleRowIndex = std::max(0, g_selectedRowIndex - visibleRowCount + 1);
disableMoveAnimation = true;
if (g_selectedRowIndex < rowCount - 1)
fastScrollSpeedUp = true;
}
if (disableMoveAnimation)
g_prevSelectedRowIndex = g_selectedRowIndex;
drawList->PopClipRect();
// Pop clip rect from DrawCategories
drawList->PopClipRect();
// Draw scroll bar
if (rowCount > visibleRowCount)
{
float totalHeight = (clipRectMax.y - clipRectMin.y);
float totalHeight = (clipRectMax.y - clipRectMin.y) - Scale(2.0f);
float heightRatio = float(visibleRowCount) / float(rowCount);
float offsetRatio = float(g_firstVisibleRowIndex) / float(rowCount);
@@ -1299,7 +1386,7 @@ static void DrawConfigOptions()
drawList->AddRectFilled
(
{ clipRectMax.x, minY },
{ clipRectMax.x + gridSize, minY + totalHeight * heightRatio },
{ clipRectMax.x + gridSize - Scale(1.0f), minY + totalHeight * heightRatio},
IM_COL32(0, 128, 0, 255)
);
}
@@ -1307,10 +1394,10 @@ static void DrawConfigOptions()
static void DrawSettingsPanel(ImVec2 settingsMin, ImVec2 settingsMax)
{
auto drawList = ImGui::GetForegroundDrawList();
auto drawList = ImGui::GetBackgroundDrawList();
SetProceduralOrigin(settingsMin);
DrawContainer(settingsMin, settingsMax);
DrawContainer(settingsMin, settingsMax, true);
if (DrawCategories())
{
@@ -1331,10 +1418,10 @@ static void DrawSettingsPanel(ImVec2 settingsMin, ImVec2 settingsMax)
static void DrawInfoPanel(ImVec2 infoMin, ImVec2 infoMax)
{
auto drawList = ImGui::GetForegroundDrawList();
auto drawList = ImGui::GetBackgroundDrawList();
SetProceduralOrigin(infoMin);
DrawContainer(infoMin, infoMax);
DrawContainer(infoMin, infoMax, false);
auto clipRectMin = drawList->GetClipRectMin();
auto clipRectMax = drawList->GetClipRectMax();
@@ -1350,7 +1437,27 @@ static void DrawInfoPanel(ImVec2 infoMin, ImVec2 infoMax)
ImVec2 thumbnailMin = { clipRectMin.x, clipRectMin.y + Scale(GRID_SIZE / 2.0f) };
ImVec2 thumbnailMax = { clipRectMax.x, thumbnailMin.y + thumbnailHeight };
drawList->AddImage(thumbnail, thumbnailMin, thumbnailMax);
if (g_isStage)
{
drawList->AddImage(thumbnail, thumbnailMin, thumbnailMax);
}
else
{
float time = g_appearTime + CONTAINER_FULL_DURATION / 60.0;
drawList->AddImage(
thumbnail,
thumbnailMin,
thumbnailMax,
{ 0.0f, 0.0f },
{ 1.0f, 1.0f },
IM_COL32(255, 255, 255, 255 * TVStatic::ComputeThumbnailAlpha(time)));
TVStatic::Draw(
{ (thumbnailMin.x + thumbnailMax.x) / 2.0f, (thumbnailMin.y + thumbnailMax.y) / 2.0f },
{ (thumbnailMax.x - thumbnailMin.x), (thumbnailMax.y - thumbnailMin.y) },
time);
}
if (g_inaccessibleReason)
{
@@ -1358,7 +1465,7 @@ static void DrawInfoPanel(ImVec2 infoMin, ImVec2 infoMax)
}
else
{
// Specialised description for resolution scale
// Specialised description for resolution scale.
if (g_selectedItem == &Config::ResolutionScale)
{
char buf[100];
@@ -1371,54 +1478,143 @@ static void DrawInfoPanel(ImVec2 infoMin, ImVec2 infoMax)
desc = buf;
}
desc += "\n\n" + g_selectedItem->GetValueDescription(Config::Language);
const auto& valueDescription = g_selectedItem->GetValueDescription(Config::Language);
if (!valueDescription.empty())
{
desc += "\n\n" + valueDescription;
}
}
clipRectMin = { clipRectMin.x, thumbnailMax.y };
auto fontSize = Scale(28.0f);
auto annotationFontSize = fontSize * ANNOTATION_FONT_SIZE_MODIFIER;
// Extra padding between the start of the description text and the bottom of the thumbnail
float offsetY = Scale(24.0f);
/* Extra padding between the start
of the description text and the
bottom of the thumbnail. */
auto offsetY = Scale(24.0f);
float textX = clipRectMin.x - Scale(0.5f);
float textY = thumbnailMax.y + offsetY;
auto textX = clipRectMin.x - Scale(0.5f);
auto textY = thumbnailMax.y + offsetY;
if (Config::Language == ELanguage::Japanese)
{
// Removing some padding of the applied due to the inclusion of annotation for Japanese
/* Removing some padding of the applied due
to the inclusion of annotation for Japanese. */
textY -= Scale(8.0f);
// The annotation (and thus the Japanese) can be drawn above the edges of the info panel thus the clip needs to be extended a bit
/* The annotation (and thus the Japanese) can be
drawn above the edges of the info panel thus the
clip needs to be extended a bit. */
clipRectMin.x -= annotationFontSize;
clipRectMin.y -= annotationFontSize;
clipRectMax.x += annotationFontSize;
clipRectMax.y += annotationFontSize;
textY += annotationFontSize;
}
auto textSize = MeasureCentredParagraph(g_seuratFont, fontSize, clipRectMax.x - clipRectMin.x, 5.0f, desc.c_str());
drawList->PushClipRect(clipRectMin, clipRectMax, false);
static auto isScrolling = false;
static auto isManualScrolling = false;
static auto scrollOffset = 0.0f;
static auto scrollTimer = 0.0f;
static auto scrollDirection = 1.0f;
auto scrollMax = textSize.y - (clipRectMax.y - textY);
auto scrollSpeed = Scale(50);
if (scrollMax > 0.0f)
{
auto vert = -g_rightStickY;
if (fabs(vert) > 0.25f)
{
isManualScrolling = true;
scrollOffset += vert * scrollSpeed * App::s_deltaTime;
}
else if (isManualScrolling && fabs(vert) <= 0.25f)
{
isScrolling = false;
isManualScrolling = false;
scrollTimer = 0.0f;
scrollDirection = vert > 0.0f ? 1.0f : -1.0f;
}
if (!isManualScrolling)
{
if (!isScrolling)
{
scrollTimer += App::s_deltaTime;
if (scrollTimer >= INFO_TEXT_MARQUEE_DELAY)
isScrolling = true;
}
if (isScrolling)
{
scrollOffset += scrollSpeed * scrollDirection * App::s_deltaTime;
if (scrollOffset >= scrollMax)
{
isScrolling = false;
scrollOffset = scrollMax;
scrollTimer = 0.0f;
scrollDirection = -1.0f;
}
else if (scrollOffset <= 0.0f)
{
isScrolling = false;
scrollOffset = 0;
scrollTimer = 0.0f;
scrollDirection = 1.0f;
}
}
}
scrollOffset = std::clamp(scrollOffset, 0.0f, scrollMax);
}
else
{
isScrolling = false;
scrollOffset = 0.0f;
scrollTimer = 0.0f;
scrollDirection = 1.0f;
}
SetVerticalMarqueeFade({ clipRectMin.x, clipRectMin.y + Scale(5.5f) }, clipRectMax, Scale(10), Scale(10));
DrawRubyAnnotatedText
(
g_seuratFont,
fontSize,
clipRectMax.x - clipRectMin.x,
{ textX, textY },
{ textX, textY - scrollOffset },
5.0f,
desc.c_str(),
[=](const char* str, ImVec2 pos)
{
DrawTextBasic(g_seuratFont, fontSize, pos, IM_COL32(255, 255, 255, 255), str);
DrawTextBasic(g_seuratFont, fontSize, pos, IM_COL32_WHITE, str);
},
[=](const char* str, float size, ImVec2 pos)
{
DrawTextBasic(g_seuratFont, size, pos, IM_COL32(255, 255, 255, 255), str);
DrawTextBasic(g_seuratFont, size, pos, IM_COL32_WHITE, str);
}
);
ResetMarqueeFade();
drawList->PopClipRect();
// Reset parameters on new selected row.
if (ImGui::GetTime() - g_rowSelectionTime <= 0.0f)
{
isScrolling = false;
scrollOffset = 0.0f;
scrollTimer = 0.0f;
scrollDirection = 1.0f;
}
}
ResetProceduralOrigin();
@@ -1435,7 +1631,7 @@ static void SetOptionsMenuVisible(bool isVisible)
static bool DrawMilesElectric()
{
auto drawList = ImGui::GetForegroundDrawList();
auto drawList = ImGui::GetBackgroundDrawList();
auto& res = ImGui::GetIO().DisplaySize;
// Compensate for the lack of CSD UI dimming the background.
@@ -1488,7 +1684,7 @@ static bool DrawMilesElectric()
static bool DrawFadeTransition()
{
auto drawList = ImGui::GetForegroundDrawList();
auto drawList = ImGui::GetBackgroundDrawList();
auto& res = ImGui::GetIO().DisplaySize;
auto scaleMotion = ComputeMotion(g_appearTime, 0, MILES_ELECTRIC_SCALE_DURATION);
@@ -1513,6 +1709,8 @@ void OptionsMenu::Init()
LoadThumbnails();
g_upMilesElectric = LOAD_ZSTD_TEXTURE(g_miles_electric);
TVStatic::Init();
}
void OptionsMenu::Draw()
@@ -1533,7 +1731,7 @@ void OptionsMenu::Draw()
if (!g_isClosing)
{
auto drawList = ImGui::GetForegroundDrawList();
auto drawList = ImGui::GetBackgroundDrawList();
auto& res = ImGui::GetIO().DisplaySize;
if (g_isStage)
@@ -1592,16 +1790,16 @@ void OptionsMenu::Open(bool isPause, SWA::EMenuType pauseMenuType)
std::array<Button, 4> buttons =
{
Button(Localise("Common_Switch"), EButtonIcon::LBRB, EButtonAlignment::Left, &g_isControlsVisible),
Button(Localise("Common_Reset"), EButtonIcon::X, &g_canReset),
Button(Localise("Common_Select"), EButtonIcon::A, &g_isControlsVisible),
Button(Localise("Common_Back"), EButtonIcon::B, &g_isControlsVisible)
Button("Common_Switch", EButtonIcon::LBRB, EButtonAlignment::Left, &g_isControlsVisible),
Button("Common_Reset", EButtonIcon::X, &g_canReset),
Button("Common_Select", EButtonIcon::A, &g_isControlsVisible),
Button("Common_Back", EButtonIcon::B, &g_isControlsVisible)
};
ButtonGuide::Open(buttons);
ButtonGuide::SetSideMargins(250);
hid::SetProhibitedButtons(XAMINPUT_GAMEPAD_START);
hid::SetProhibitedInputs(XAMINPUT_GAMEPAD_START, false, true);
}
void OptionsMenu::Close()
@@ -1615,7 +1813,7 @@ void OptionsMenu::Close()
ButtonGuide::Close();
Config::Save();
hid::SetProhibitedButtons(0);
hid::SetProhibitedInputs();
}
// Skip Miles Electric animation at main menu.
+76 -11
View File
@@ -1,14 +1,21 @@
#include <ui/options_menu_thumbnails.h>
#include <decompressor.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/allow_background_input.dds.h>
#include <res/images/options_menu/thumbnails/antialiasing.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_4x.dds.h>
#include <res/images/options_menu/thumbnails/antialiasing_8x.dds.h>
#include <res/images/options_menu/thumbnails/aspect_ratio.dds.h>
#include <res/images/options_menu/thumbnails/battle_theme.dds.h>
#include <res/images/options_menu/thumbnails/brightness.dds.h>
#include <res/images/options_menu/thumbnails/control_tutorial.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/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/default.dds.h>
#include <res/images/options_menu/thumbnails/effects_volume.dds.h>
@@ -24,7 +31,8 @@
#include <res/images/options_menu/thumbnails/motion_blur_off.dds.h>
#include <res/images/options_menu/thumbnails/motion_blur_original.dds.h>
#include <res/images/options_menu/thumbnails/motion_blur_enhanced.dds.h>
#include <res/images/options_menu/thumbnails/movie_scale_mode.dds.h>
#include <res/images/options_menu/thumbnails/movie_scale_fit.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_volume.dds.h>
#include <res/images/options_menu/thumbnails/resolution_scale.dds.h>
@@ -35,15 +43,17 @@
#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/subtitles.dds.h>
#include <res/images/options_menu/thumbnails/time_of_day_transition_xbox.dds.h>
#include <res/images/options_menu/thumbnails/time_of_day_transition_playstation.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_true.dds.h>
#include <res/images/options_menu/thumbnails/ui_scale_mode.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/vertical_camera.dds.h>
#include <res/images/options_menu/thumbnails/voice_language.dds.h>
#include <res/images/options_menu/thumbnails/vibration.dds.h>
#include <res/images/options_menu/thumbnails/vsync.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/window_size.dds.h>
#include <res/images/options_menu/thumbnails/xbox_color_correction.dds.h>
@@ -53,22 +63,28 @@ static std::unordered_map<std::string_view, std::unique_ptr<GuestTexture>> g_nam
static std::unordered_map<const IConfigDef*, std::unique_ptr<GuestTexture>> g_configThumbnails;
static VALUE_THUMBNAIL_MAP(ETimeOfDayTransition) g_timeOfDayTransitionThumbnails;
static VALUE_THUMBNAIL_MAP(EChannelConfiguration) g_channelConfigurationThumbnails;
static VALUE_THUMBNAIL_MAP(EAntiAliasing) g_msaaAntiAliasingThumbnails;
static VALUE_THUMBNAIL_MAP(bool) g_vsyncThumbnails;
static VALUE_THUMBNAIL_MAP(bool) g_transparencyAntiAliasingThumbnails;
static VALUE_THUMBNAIL_MAP(EShadowResolution) g_shadowResolutionThumbnails;
static VALUE_THUMBNAIL_MAP(EGITextureFiltering) g_giTextureFilteringThumbnails;
static VALUE_THUMBNAIL_MAP(EMotionBlur) g_motionBlurThumbnails;
static VALUE_THUMBNAIL_MAP(bool) g_xboxColorCorrectionThumbnails;
static VALUE_THUMBNAIL_MAP(ECutsceneAspectRatio) g_cutsceneAspectRatioThumbnails;
static VALUE_THUMBNAIL_MAP(EUIAlignmentMode) g_uiAlignmentThumbnails;
void LoadThumbnails()
{
g_namedThumbnails["Default"] = LOAD_ZSTD_TEXTURE(g_default);
g_namedThumbnails["WindowSize"] = LOAD_ZSTD_TEXTURE(g_window_size);
g_namedThumbnails["ControlTutorialXB"] = LOAD_ZSTD_TEXTURE(g_control_tutorial_xb);
g_namedThumbnails["ControlTutorialPS"] = LOAD_ZSTD_TEXTURE(g_control_tutorial_ps);
g_configThumbnails[&Config::Language] = LOAD_ZSTD_TEXTURE(g_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::ControlTutorial] = LOAD_ZSTD_TEXTURE(g_control_tutorial);
g_configThumbnails[&Config::AchievementNotifications] = LOAD_ZSTD_TEXTURE(g_achievement_notifications);
g_timeOfDayTransitionThumbnails[ETimeOfDayTransition::Xbox] = LOAD_ZSTD_TEXTURE(g_time_of_day_transition_xbox);
@@ -82,6 +98,10 @@ void LoadThumbnails()
g_configThumbnails[&Config::MasterVolume] = LOAD_ZSTD_TEXTURE(g_master_volume);
g_configThumbnails[&Config::MusicVolume] = LOAD_ZSTD_TEXTURE(g_music_volume);
g_configThumbnails[&Config::EffectsVolume] = LOAD_ZSTD_TEXTURE(g_effects_volume);
g_channelConfigurationThumbnails[EChannelConfiguration::Stereo] = LOAD_ZSTD_TEXTURE(g_channel_stereo);
g_channelConfigurationThumbnails[EChannelConfiguration::Surround] = LOAD_ZSTD_TEXTURE(g_channel_surround);
g_configThumbnails[&Config::MusicAttenuation] = LOAD_ZSTD_TEXTURE(g_music_attenuation);
g_configThumbnails[&Config::BattleTheme] = LOAD_ZSTD_TEXTURE(g_battle_theme);
g_configThumbnails[&Config::WindowSize] = LOAD_ZSTD_TEXTURE(g_window_size);
@@ -89,10 +109,17 @@ void LoadThumbnails()
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::VSync] = LOAD_ZSTD_TEXTURE(g_vsync);
g_vsyncThumbnails[false] = LOAD_ZSTD_TEXTURE(g_vsync_off);
g_vsyncThumbnails[true] = LOAD_ZSTD_TEXTURE(g_vsync_on);
g_configThumbnails[&Config::FPS] = LOAD_ZSTD_TEXTURE(g_fps);
g_configThumbnails[&Config::Brightness] = LOAD_ZSTD_TEXTURE(g_brightness);
g_configThumbnails[&Config::AntiAliasing] = LOAD_ZSTD_TEXTURE(g_antialiasing);
g_msaaAntiAliasingThumbnails[EAntiAliasing::None] = LOAD_ZSTD_TEXTURE(g_antialiasing_none);
g_msaaAntiAliasingThumbnails[EAntiAliasing::MSAA2x] = LOAD_ZSTD_TEXTURE(g_antialiasing_2x);
g_msaaAntiAliasingThumbnails[EAntiAliasing::MSAA4x] = LOAD_ZSTD_TEXTURE(g_antialiasing_4x);
g_msaaAntiAliasingThumbnails[EAntiAliasing::MSAA8x] = LOAD_ZSTD_TEXTURE(g_antialiasing_8x);
g_transparencyAntiAliasingThumbnails[false] = LOAD_ZSTD_TEXTURE(g_transparency_antialiasing_false);
g_transparencyAntiAliasingThumbnails[true] = LOAD_ZSTD_TEXTURE(g_transparency_antialiasing_true);
@@ -111,8 +138,13 @@ void LoadThumbnails()
g_motionBlurThumbnails[EMotionBlur::Original] = LOAD_ZSTD_TEXTURE(g_motion_blur_original);
g_motionBlurThumbnails[EMotionBlur::Enhanced] = LOAD_ZSTD_TEXTURE(g_motion_blur_enhanced);
g_cutsceneAspectRatioThumbnails[ECutsceneAspectRatio::Original] = LOAD_ZSTD_TEXTURE(g_movie_scale_fit);
g_cutsceneAspectRatioThumbnails[ECutsceneAspectRatio::Unlocked] = LOAD_ZSTD_TEXTURE(g_movie_scale_fill);
g_uiAlignmentThumbnails[EUIAlignmentMode::Centre] = LOAD_ZSTD_TEXTURE(g_ui_alignment_centre);
g_uiAlignmentThumbnails[EUIAlignmentMode::Edge] = LOAD_ZSTD_TEXTURE(g_ui_alignment_edge);
g_configThumbnails[&Config::XboxColorCorrection] = LOAD_ZSTD_TEXTURE(g_xbox_color_correction);
g_configThumbnails[&Config::UIScaleMode] = LOAD_ZSTD_TEXTURE(g_ui_scale_mode);
}
template<typename T>
@@ -148,10 +180,23 @@ GuestTexture* GetThumbnail(const IConfigDef* cfg)
{
auto texture = g_namedThumbnails["Default"].get();
if (cfg == &Config::ControlTutorial)
{
bool isPlayStation = Config::ControllerIcons == EControllerIcons::PlayStation;
if (Config::ControllerIcons == EControllerIcons::Auto)
isPlayStation = hid::g_inputDeviceController == hid::EInputDevice::PlayStation;
texture = isPlayStation ? g_namedThumbnails["ControlTutorialPS"].get() : g_namedThumbnails["ControlTutorialXB"].get();
}
if (cfg == &Config::TimeOfDayTransition)
{
TryGetValueThumbnail<ETimeOfDayTransition>(cfg, &g_timeOfDayTransitionThumbnails, &texture);
}
else if (cfg == &Config::AntiAliasing)
{
TryGetValueThumbnail<EAntiAliasing>(cfg, &g_msaaAntiAliasingThumbnails, &texture);
}
else if (cfg == &Config::TransparencyAntiAliasing)
{
TryGetValueThumbnail<bool>(cfg, &g_transparencyAntiAliasingThumbnails, &texture);
@@ -168,6 +213,26 @@ GuestTexture* GetThumbnail(const IConfigDef* cfg)
{
TryGetValueThumbnail<EMotionBlur>(cfg, &g_motionBlurThumbnails, &texture);
}
else if (cfg == &Config::XboxColorCorrection)
{
TryGetValueThumbnail<bool>(cfg, &g_xboxColorCorrectionThumbnails, &texture);
}
else if (cfg == &Config::CutsceneAspectRatio)
{
TryGetValueThumbnail<ECutsceneAspectRatio>(cfg, &g_cutsceneAspectRatioThumbnails, &texture);
}
else if (cfg == &Config::VSync)
{
TryGetValueThumbnail<bool>(cfg, &g_vsyncThumbnails, &texture);
}
else if (cfg == &Config::ChannelConfiguration)
{
TryGetValueThumbnail<EChannelConfiguration>(cfg, &g_channelConfigurationThumbnails, &texture);
}
else if (cfg == &Config::UIAlignmentMode)
{
TryGetValueThumbnail<EUIAlignmentMode>(cfg, &g_uiAlignmentThumbnails, &texture);
}
return texture;
}
+392
View File
@@ -0,0 +1,392 @@
#include "tv_static.h"
#include "imgui_utils.h"
#include <decompressor.h>
#include <gpu/video.h>
#include <patches/aspect_ratio_patches.h>
#include <res/images/options_menu/options_static.dds.h>
#include <res/images/options_menu/options_static_flash.dds.h>
namespace
{
struct FloatLinear
{
float value;
float time;
static float Sample(const FloatLinear& a, const FloatLinear& b, float time)
{
const float t = std::clamp((time - a.time) / (b.time - a.time), 0.0f, 1.0f);
return (b.value - a.value) * t + a.value;
}
};
struct FloatHermite
{
float value;
float time;
float inTangent;
float outTangent;
static float Sample(const FloatHermite& a, const FloatHermite& b, float time)
{
const float t = std::clamp((time - a.time) / (b.time - a.time), 0.0f, 1.0f);
float valueDelta = b.value - a.value;
float frameDelta = b.time - a.time;
float biasSquaric = t * t;
float biasCubic = biasSquaric * t;
float valueCubic = (a.outTangent + a.inTangent) * frameDelta - valueDelta * 2.0f;
float valueSquaric = valueDelta * 3.0f - (a.inTangent * 2.0f + a.outTangent) * frameDelta;
float valueLinear = frameDelta * a.inTangent;
return valueCubic * biasCubic + valueSquaric * biasSquaric + valueLinear * t + a.value;
}
};
}
template<typename T, size_t N>
static auto Sample(const std::array<T, N>& keys, float time)
{
T firstKey = keys[0];
T lastKey = keys[N - 1];
if (time < firstKey.time)
return firstKey.value;
if (time > lastKey.time)
return lastKey.value;
size_t keyIndex = 0;
for (auto key : keys)
{
if (key.time >= time)
break;
keyIndex++;
}
if (keyIndex >= N)
return keys[N - 1].value;
return T::Sample(keys[keyIndex - 1], keys[keyIndex], time);
}
static std::unique_ptr<GuestTexture> g_flashTexture;
static std::unique_ptr<GuestTexture> g_noiseTexture;
static constexpr float FRAME_OFFSET = 65.0f;
static constexpr float FRAME_SCALE = 1.0f / 60.0f;
static constexpr float FRAME_DURATION = 32.0f;
static std::array g_flashScaleX =
{
FloatHermite{ 0, 67 * FRAME_SCALE, 0, 0.673736f },
FloatHermite{ 2, 70 * FRAME_SCALE, -0.003465f, -0.682543f },
FloatHermite{ 0, 73 * FRAME_SCALE, 0, 0 },
};
static std::array g_flashScaleY =
{
FloatHermite{ 0, 67 * FRAME_SCALE, 0, 0.67238f },
FloatHermite{ 2, 70 * FRAME_SCALE, -0.001741f, -0.664096f },
FloatHermite{ 0, 73 * FRAME_SCALE, 0, 0 },
};
static std::array g_flashColor =
{
FloatLinear{ 255.0f, 67 * FRAME_SCALE },
FloatLinear{ 160.0f, 70 * FRAME_SCALE },
FloatLinear{ 255.0f, 73 * FRAME_SCALE },
};
static std::array g_flashAlpha =
{
FloatLinear{ 255.0f, 70 * FRAME_SCALE },
FloatLinear{ 0.0f, 73 * FRAME_SCALE },
};
static std::array g_noiseScale =
{
FloatLinear{ 0.0f, 70 * FRAME_SCALE },
FloatLinear{ 1.0f, 75 * FRAME_SCALE },
};
static std::array g_noiseTL_gradTL =
{
FloatLinear{ 0.0f, 77 * FRAME_SCALE },
FloatLinear{ (192 / 255.0f), 85 * FRAME_SCALE },
FloatLinear{ 0.0f, 90 * FRAME_SCALE },
};
static std::array g_noiseTL_gradBL =
{
FloatLinear{ 0.0f, 73 * FRAME_SCALE },
FloatLinear{ (223 / 255.0f), 85 * FRAME_SCALE },
FloatLinear{ 0.0f, 94 * FRAME_SCALE },
};
static std::array g_noiseTL_gradTR =
{
FloatLinear{ 0.0f, 74 * FRAME_SCALE },
FloatLinear{ (223 / 255.0f), 85 * FRAME_SCALE },
FloatLinear{ 0.0f, 93 * FRAME_SCALE },
};
static std::array g_noiseTL_gradBR =
{
FloatLinear{ 0.0f, 70 * FRAME_SCALE },
FloatLinear{ 1.0f, 85 * FRAME_SCALE },
FloatLinear{ 0.0f, 97 * FRAME_SCALE },
};
static std::array g_noiseTR_gradTL =
{
FloatLinear{ 0.0f, 74 * FRAME_SCALE },
FloatLinear{ (223 / 255.0f), 85 * FRAME_SCALE },
FloatLinear{ 0.0f, 93 * FRAME_SCALE },
};
static std::array g_noiseTR_gradBL =
{
FloatLinear{ 0.0f, 70 * FRAME_SCALE },
FloatLinear{ 1.0f, 85 * FRAME_SCALE },
FloatLinear{ 0.0f, 97 * FRAME_SCALE },
};
static std::array g_noiseTR_gradTR =
{
FloatLinear{ 0.0f, 77 * FRAME_SCALE },
FloatLinear{ (192 / 255.0f), 85 * FRAME_SCALE },
FloatLinear{ 0.0f, 90 * FRAME_SCALE },
};
static std::array g_noiseTR_gradBR =
{
FloatLinear{ 0.0f, 73 * FRAME_SCALE },
FloatLinear{ (223 / 255.0f), 85 * FRAME_SCALE },
FloatLinear{ 0.0f, 94 * FRAME_SCALE },
};
static std::array g_noiseBL_gradTL =
{
FloatLinear{ 0.0f, 73 * FRAME_SCALE },
FloatLinear{ (223 / 255.0f), 85 * FRAME_SCALE },
FloatLinear{ 0.0f, 94 * FRAME_SCALE },
};
static std::array g_noiseBL_gradBL =
{
FloatLinear{ 0.0f, 77 * FRAME_SCALE },
FloatLinear{ (192 / 255.0f), 85 * FRAME_SCALE },
FloatLinear{ 0.0f, 90 * FRAME_SCALE },
};
static std::array g_noiseBL_gradTR =
{
FloatLinear{ 0.0f, 70 * FRAME_SCALE },
FloatLinear{ 1.0f, 85 * FRAME_SCALE },
FloatLinear{ 0.0f, 97 * FRAME_SCALE },
};
static std::array g_noiseBL_gradBR =
{
FloatLinear{ 0.0f, 74 * FRAME_SCALE },
FloatLinear{ (223 / 255.0f), 85 * FRAME_SCALE },
FloatLinear{ 0.0f, 93 * FRAME_SCALE },
};
static std::array g_noiseBR_gradTL =
{
FloatLinear{ 0.0f, 70 * FRAME_SCALE },
FloatLinear{ 1.0f, 85 * FRAME_SCALE },
FloatLinear{ 0.0f, 97 * FRAME_SCALE },
};
static std::array g_noiseBR_gradBL =
{
FloatLinear{ 0.0f, 74 * FRAME_SCALE },
FloatLinear{ (223 / 255.0f), 85 * FRAME_SCALE },
FloatLinear{ 0.0f, 93 * FRAME_SCALE },
};
static std::array g_noiseBR_gradTR =
{
FloatLinear{ 0.0f, 73 * FRAME_SCALE },
FloatLinear{ (223 / 255.0f), 85 * FRAME_SCALE },
FloatLinear{ 0.0f, 94 * FRAME_SCALE },
};
static std::array g_noiseBR_gradBR =
{
FloatLinear{ 0.0f, 77 * FRAME_SCALE },
FloatLinear{ (192 / 255.0f), 85 * FRAME_SCALE },
FloatLinear{ 0.0f, 90 * FRAME_SCALE },
};
static std::array g_thumbnailAlpha =
{
FloatLinear{ 0.0f, 85 * FRAME_SCALE },
FloatLinear{ 1.0f, 90 * FRAME_SCALE },
};
static std::pair<ImVec2, ImVec2> ComputeRect(const ImVec2& center, const ImVec2& scale)
{
ImVec2 min = { center.x - scale.x / 2.0f, center.y - scale.y / 2.0f };
ImVec2 max = { center.x + scale.x / 2.0f, center.y + scale.y / 2.0f };
return std::make_pair(min, max);
}
static void DrawFlash(const ImVec2& center, const ImVec2& resolution, float time)
{
auto drawList = ImGui::GetBackgroundDrawList();
float baseScale = resolution.y / 135.0f * 100.0f;
auto [min, max] = ComputeRect(center, { Sample(g_flashScaleX, time) * baseScale, Sample(g_flashScaleY, time) * baseScale});
float color = Sample(g_flashColor, time);
float alpha = Sample(g_flashAlpha, time);
drawList->AddImage(g_flashTexture.get(), min, max, { 0.0f, 0.0f }, { 1.0f, 1.0f }, IM_COL32(color, 255, color, alpha));
}
static void PrimRectUVColorCorners(ImDrawList* This, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min, const ImVec2& uv_max,
ImU32 col_upr_left, ImU32 col_upr_right, ImU32 col_bot_right, ImU32 col_bot_left, bool reverse_order = false)
{
ImVec2 a(p_min), c(p_max), uv_a(uv_min), uv_c(uv_max);
ImVec2 b(c.x, a.y), d(a.x, c.y), uv_b(uv_c.x, uv_a.y), uv_d(uv_a.x, uv_c.y);
if (reverse_order)
{
ImVec2 _a = a; a = b; b = _a;
ImVec2 _c = c; c = d; d = _c;
ImVec2 _uv_a = uv_a; uv_a = uv_b; uv_b = _uv_a;
ImVec2 _uv_c = uv_c; uv_c = uv_d; uv_d = _uv_c;
}
ImDrawIdx idx = (ImDrawIdx)This->_VtxCurrentIdx;
This->_IdxWritePtr[0] = idx; This->_IdxWritePtr[1] = (ImDrawIdx)(idx + 1); This->_IdxWritePtr[2] = (ImDrawIdx)(idx + 2);
This->_IdxWritePtr[3] = idx; This->_IdxWritePtr[4] = (ImDrawIdx)(idx + 2); This->_IdxWritePtr[5] = (ImDrawIdx)(idx + 3);
This->_VtxWritePtr[0].pos = a; This->_VtxWritePtr[0].uv = uv_a; This->_VtxWritePtr[0].col = reverse_order ? col_upr_right : col_upr_left;
This->_VtxWritePtr[1].pos = b; This->_VtxWritePtr[1].uv = uv_b; This->_VtxWritePtr[1].col = reverse_order ? col_upr_left : col_upr_right;
This->_VtxWritePtr[2].pos = c; This->_VtxWritePtr[2].uv = uv_c; This->_VtxWritePtr[2].col = reverse_order ? col_bot_left : col_bot_right;
This->_VtxWritePtr[3].pos = d; This->_VtxWritePtr[3].uv = uv_d; This->_VtxWritePtr[3].col = reverse_order ? col_bot_right : col_bot_left;
This->_VtxWritePtr += 4;
This->_VtxCurrentIdx += 4;
This->_IdxWritePtr += 6;
}
static void AddImageGradient(ImDrawList* DrawList, ImTextureID user_texture_id, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min, const ImVec2& uv_max,
ImU32 col_upr_left, ImU32 col_upr_right, ImU32 col_bot_right, ImU32 col_bot_left, bool reverse_order = false)
{
if (((col_upr_left | col_upr_right | col_bot_right | col_bot_left) & IM_COL32_A_MASK) == 0)
return;
const bool push_texture_id = user_texture_id && (user_texture_id != DrawList->_CmdHeader.TextureId);
if (push_texture_id)
DrawList->PushTextureID(user_texture_id);
DrawList->PrimReserve(6, 4);
const ImVec2 uv = DrawList->_Data->TexUvWhitePixel;
PrimRectUVColorCorners(DrawList, p_min, p_max,
user_texture_id ? uv_min : uv,
user_texture_id ? uv_max : uv,
col_upr_left, col_upr_right, col_bot_right, col_bot_left,
reverse_order);
if (push_texture_id)
DrawList->PopTextureID();
}
static void DrawStatic(const ImVec2& center, const ImVec2& resolution, float time)
{
auto drawList = ImGui::GetBackgroundDrawList();
const float scaleFactor = Sample(g_noiseScale, time);
const ImVec2 scale2D = { 1, scaleFactor };
auto [min, max] = ComputeRect(center, {scale2D.x * resolution.x, scale2D.y * resolution.y});
// Texture pixel size divided by image resolution (which must be divisible by 4) to get proper UV clipping.
ImVec2 UV_MAX = { 270.0f / 272.0f,
135.0f / 136.0f };
// Corner points
ImVec2 topCenter = { center.x, min.y };
ImVec2 bottomCenter = { center.x, max.y };
ImVec2 centerLeft = { min.x, center.y };
ImVec2 centerRight = { max.x, center.y };
bool flipUV = time >= (73 * FRAME_SCALE);
const float UV_1 = flipUV ? 0.0f : 1.0f;
const float UV_0 = flipUV ? 1.0f : 0.0f;
// Top Left
AddImageGradient(
drawList,
g_noiseTexture.get(),
min,
center,
ImVec2(UV_0 * UV_MAX.x, 0.0f * UV_MAX.y),
ImVec2(0.5f * UV_MAX.x, 0.5f * UV_MAX.y),
IM_COL32(255, 255, 255, 255 * Sample(g_noiseTL_gradTL, time)),
IM_COL32(255, 255, 255, 255 * Sample(g_noiseTL_gradTR, time)),
IM_COL32(255, 255, 255, 255 * Sample(g_noiseTL_gradBR, time)),
IM_COL32(255, 255, 255, 255 * Sample(g_noiseTL_gradBL, time)),
true);
// Top Right
AddImageGradient(
drawList,
g_noiseTexture.get(),
topCenter,
centerRight,
ImVec2(0.5f * UV_MAX.x, 0.0f * UV_MAX.y),
ImVec2(UV_1 * UV_MAX.x, 0.5f * UV_MAX.y),
IM_COL32(255, 255, 255, 255 * Sample(g_noiseTR_gradTL, time)),
IM_COL32(255, 255, 255, 255 * Sample(g_noiseTR_gradTR, time)),
IM_COL32(255, 255, 255, 255 * Sample(g_noiseTR_gradBR, time)),
IM_COL32(255, 255, 255, 255 * Sample(g_noiseTR_gradBL, time)),
true);
// Bottom Right
AddImageGradient(
drawList,
g_noiseTexture.get(),
center,
max,
ImVec2(0.5f * UV_MAX.x, 0.5f * UV_MAX.y),
ImVec2(UV_1 * UV_MAX.x, 1.0f * UV_MAX.y),
IM_COL32(255, 255, 255, 255 * Sample(g_noiseBR_gradTL, time)),
IM_COL32(255, 255, 255, 255 * Sample(g_noiseBR_gradTR, time)),
IM_COL32(255, 255, 255, 255 * Sample(g_noiseBR_gradBR, time)),
IM_COL32(255, 255, 255, 255 * Sample(g_noiseBR_gradBL, time)),
true);
// Bottom Left
AddImageGradient(
drawList,
g_noiseTexture.get(),
centerLeft,
bottomCenter,
ImVec2(UV_0 * UV_MAX.x, 0.5f * UV_MAX.y),
ImVec2(0.5f * UV_MAX.x, 1.0f * UV_MAX.y),
IM_COL32(255, 255, 255, 255 * Sample(g_noiseBL_gradTL, time)),
IM_COL32(255, 255, 255, 255 * Sample(g_noiseBL_gradTR, time)),
IM_COL32(255, 255, 255, 255 * Sample(g_noiseBL_gradBR, time)),
IM_COL32(255, 255, 255, 255 * Sample(g_noiseBL_gradBL, time)),
true);
}
void TVStatic::Init()
{
g_flashTexture = LOAD_ZSTD_TEXTURE(g_options_static_flash);
g_noiseTexture = LOAD_ZSTD_TEXTURE(g_options_static);
}
static float ComputeTime(double appearTime)
{
return (ImGui::GetTime() - appearTime) + FRAME_OFFSET * FRAME_SCALE;
}
float TVStatic::ComputeThumbnailAlpha(double appearTime)
{
return Sample(g_thumbnailAlpha, ComputeTime(appearTime));
}
void TVStatic::Draw(const ImVec2& center, const ImVec2& resolution, double appearTime)
{
float time = ComputeTime(appearTime);
DrawStatic(center, resolution, time);
DrawFlash(center, resolution, time);
}
+8
View File
@@ -0,0 +1,8 @@
#pragma once
struct TVStatic
{
static void Init();
static float ComputeThumbnailAlpha(double appearTime);
static void Draw(const ImVec2& center, const ImVec2& resolution, double appearTime);
};
+21 -1
View File
@@ -67,9 +67,29 @@ void AchievementManager::Unlock(uint16_t id)
AchievementOverlay::Open(id);
}
void AchievementManager::Load()
void AchievementManager::UnlockAll()
{
for (uint16_t i = 24; i <= 83; i++)
{
if (i == 30)
i = 31;
if (i == 55)
i = 64;
AchievementManager::Unlock(i);
}
}
void AchievementManager::Reset()
{
Data = {};
}
void AchievementManager::Load()
{
AchievementManager::Reset();
Status = EAchStatus::Success;
auto dataPath = GetDataPath(true);
@@ -27,6 +27,8 @@ public:
static size_t GetTotalRecords();
static bool IsUnlocked(uint16_t id);
static void Unlock(uint16_t id);
static void UnlockAll();
static void Reset();
static void Load();
static void Save(bool ignoreStatus = false);
};
+55 -18
View File
@@ -1,7 +1,7 @@
#include "config.h"
#include <os/logger.h>
#include <ui/game_window.h>
#include <user/paths.h>
#include <exports.h>
std::vector<IConfigDef*> g_configDefinitions;
@@ -302,6 +302,7 @@ CONFIG_DEFINE_ENUM_TEMPLATE(EVoiceLanguage)
CONFIG_DEFINE_ENUM_TEMPLATE(EGraphicsAPI)
{
{ "Auto", EGraphicsAPI::Auto },
#ifdef UNLEASHED_RECOMP_D3D12
{ "D3D12", EGraphicsAPI::D3D12 },
#endif
@@ -376,11 +377,11 @@ CONFIG_DEFINE_ENUM_TEMPLATE(ECutsceneAspectRatio)
{ "Unlocked", ECutsceneAspectRatio::Unlocked }
};
CONFIG_DEFINE_ENUM_TEMPLATE(EUIScaleMode)
CONFIG_DEFINE_ENUM_TEMPLATE(EUIAlignmentMode)
{
{ "Edge", EUIScaleMode::Edge },
{ "Centre", EUIScaleMode::Centre },
{ "Center", EUIScaleMode::Centre }
{ "Edge", EUIAlignmentMode::Edge },
{ "Centre", EUIAlignmentMode::Centre },
{ "Center", EUIAlignmentMode::Centre }
};
#undef CONFIG_DEFINE
@@ -406,11 +407,6 @@ CONFIG_DEFINE_ENUM_TEMPLATE(EUIScaleMode)
extern CONFIG_ENUM_LOCALE(type) g_##type##_locale; \
ConfigDef<type> Config::name{section, #name, &g_##name##_locale, defaultValue, &g_##type##_template, &g_##type##_locale};
#undef CONFIG_DEFINE_CALLBACK
#define CONFIG_DEFINE_CALLBACK(section, type, name, defaultValue, readCallback) \
extern CONFIG_LOCALE g_##name##_locale; \
ConfigDef<type> Config::name{section, #name, defaultValue, [](ConfigDef<type>* def) readCallback};
#include "config_def.h"
// CONFIG_DEFINE
@@ -447,13 +443,6 @@ ConfigDef<T, isHidden>::ConfigDef(std::string section, std::string name, CONFIG_
g_configDefinitions.emplace_back(this);
}
// CONFIG_DEFINE_CALLBACK
template<typename T, bool isHidden>
ConfigDef<T, isHidden>::ConfigDef(std::string section, std::string name, T defaultValue, std::function<void(ConfigDef<T, isHidden>*)> callback) : Section(section), Name(name), DefaultValue(defaultValue), Callback(callback)
{
g_configDefinitions.emplace_back(this);
}
template<typename T, bool isHidden>
ConfigDef<T, isHidden>::~ConfigDef() = default;
@@ -472,7 +461,7 @@ void ConfigDef<T, isHidden>::ReadValue(toml::v3::ex::parse_result& toml)
if constexpr (std::is_same<T, std::string>::value)
{
Value = section[Name].value_or<std::string>(DefaultValue);
Value = section[Name].value_or(DefaultValue);
}
else if constexpr (std::is_enum_v<T>)
{
@@ -722,8 +711,54 @@ std::filesystem::path Config::GetConfigPath()
return GetUserPath() / "config.toml";
}
void Config::CreateCallbacks()
{
Config::WindowSize.LockCallback = [](ConfigDef<int32_t>* def)
{
// Try matching the current window size with a known configuration.
if (def->Value < 0)
def->Value = GameWindow::FindNearestDisplayMode();
};
Config::WindowSize.ApplyCallback = [](ConfigDef<int32_t>* def)
{
auto displayModes = GameWindow::GetDisplayModes();
// Use largest supported resolution if overflowed.
if (def->Value >= displayModes.size())
def->Value = displayModes.size() - 1;
auto& mode = displayModes[def->Value];
auto centre = SDL_WINDOWPOS_CENTERED_DISPLAY(GameWindow::GetDisplay());
GameWindow::SetDimensions(mode.w, mode.h, centre, centre);
};
Config::Monitor.Callback = [](ConfigDef<int32_t>* def)
{
GameWindow::SetDisplay(def->Value);
};
Config::Fullscreen.Callback = [](ConfigDef<bool>* def)
{
GameWindow::SetFullscreen(def->Value);
GameWindow::SetDisplay(Config::Monitor);
};
Config::ResolutionScale.Callback = [](ConfigDef<float>* def)
{
def->Value = std::clamp(def->Value, 0.25f, 2.0f);
};
}
void Config::Load()
{
if (!s_isCallbacksCreated)
{
CreateCallbacks();
s_isCallbacksCreated = true;
}
auto configPath = GetConfigPath();
if (!std::filesystem::exists(configPath))
@@ -757,6 +792,8 @@ void Config::Load()
void Config::Save()
{
LOGN("Saving configuration...");
auto userPath = GetUserPath();
if (!std::filesystem::exists(userPath))
+9 -5
View File
@@ -25,6 +25,10 @@ public:
#define CONFIG_LOCALE std::unordered_map<ELanguage, std::tuple<std::string, std::string>>
#define CONFIG_ENUM_LOCALE(type) std::unordered_map<ELanguage, std::unordered_map<type, std::tuple<std::string, std::string>>>
#define CONFIG_CALLBACK(name) if (name.Callback) name.Callback(&name)
#define CONFIG_LOCK_CALLBACK(name) if (name.LockCallback) name.LockCallback(&name)
#define CONFIG_APPLY_CALLBACK(name) if (name.ApplyCallback) name.ApplyCallback(&name)
#define WINDOWPOS_CENTRED 0x2FFF0000
extern std::vector<IConfigDef*> g_configDefinitions;
@@ -62,6 +66,7 @@ enum class EChannelConfiguration : uint32_t
enum class EGraphicsAPI : uint32_t
{
Auto,
#ifdef UNLEASHED_RECOMP_D3D12
D3D12,
#endif
@@ -138,7 +143,7 @@ enum class ECutsceneAspectRatio : uint32_t
Unlocked
};
enum class EUIScaleMode : uint32_t
enum class EUIAlignmentMode : uint32_t
{
Edge,
Centre
@@ -173,9 +178,6 @@ public:
// CONFIG_DEFINE_ENUM_LOCALISED
ConfigDef(std::string section, std::string name, CONFIG_LOCALE* nameLocale, T defaultValue, std::unordered_map<std::string, T>* enumTemplate, CONFIG_ENUM_LOCALE(T)* enumLocale);
// CONFIG_DEFINE_CALLBACK
ConfigDef(std::string section, std::string name, T defaultValue, std::function<void(ConfigDef<T, isHidden>*)> callback);
ConfigDef(const ConfigDef&) = delete;
ConfigDef(ConfigDef&&) = delete;
~ConfigDef();
@@ -220,15 +222,17 @@ public:
#define CONFIG_DEFINE_LOCALISED(section, type, name, defaultValue) CONFIG_DECLARE(type, name)
#define CONFIG_DEFINE_ENUM(section, type, name, defaultValue) CONFIG_DECLARE(type, name)
#define CONFIG_DEFINE_ENUM_LOCALISED(section, type, name, defaultValue) CONFIG_DECLARE(type, name)
#define CONFIG_DEFINE_CALLBACK(section, type, name, defaultValue, readCallback) CONFIG_DECLARE(type, name)
class Config
{
public:
#include "config_def.h"
static inline bool s_isCallbacksCreated;
static std::filesystem::path GetConfigPath();
static void CreateCallbacks();
static void Load();
static void Save();
};
+23 -40
View File
@@ -46,45 +46,22 @@ CONFIG_DEFINE_ENUM_LOCALISED("Audio", EChannelConfiguration, ChannelConfiguratio
CONFIG_DEFINE_LOCALISED("Audio", bool, MusicAttenuation, false);
CONFIG_DEFINE_LOCALISED("Audio", bool, BattleTheme, true);
#ifdef UNLEASHED_RECOMP_D3D12
CONFIG_DEFINE_ENUM("Video", EGraphicsAPI, GraphicsAPI, EGraphicsAPI::D3D12);
#else
CONFIG_DEFINE_ENUM("Video", EGraphicsAPI, GraphicsAPI, EGraphicsAPI::Vulkan);
#endif
CONFIG_DEFINE("Video", std::string, GraphicsDevice, "");
CONFIG_DEFINE_ENUM("Video", EGraphicsAPI, GraphicsAPI, EGraphicsAPI::Auto);
CONFIG_DEFINE("Video", int32_t, WindowX, WINDOWPOS_CENTRED);
CONFIG_DEFINE("Video", int32_t, WindowY, WINDOWPOS_CENTRED);
CONFIG_DEFINE_LOCALISED("Video", int32_t, WindowSize, -1);
CONFIG_DEFINE("Video", int32_t, WindowWidth, 1280);
CONFIG_DEFINE("Video", int32_t, WindowHeight, 720);
CONFIG_DEFINE_ENUM("Video", EWindowState, WindowState, EWindowState::Normal);
CONFIG_DEFINE_CALLBACK("Video", int32_t, Monitor, 0,
{
def->Locale = &g_Monitor_locale;
Window_SetDisplay(def->Value);
});
CONFIG_DEFINE_LOCALISED("Video", int32_t, Monitor, 0);
CONFIG_DEFINE_ENUM_LOCALISED("Video", EAspectRatio, AspectRatio, EAspectRatio::Auto);
CONFIG_DEFINE_CALLBACK("Video", float, ResolutionScale, 1.0f,
{
def->Locale = &g_ResolutionScale_locale;
def->Value = std::clamp(def->Value, 0.25f, 2.0f);
});
CONFIG_DEFINE_CALLBACK("Video", bool, Fullscreen, true,
{
def->Locale = &g_Fullscreen_locale;
Window_SetFullscreen(def->Value);
Window_SetDisplay(Monitor);
});
CONFIG_DEFINE_LOCALISED("Video", float, ResolutionScale, 1.0f);
CONFIG_DEFINE_LOCALISED("Video", bool, Fullscreen, true);
CONFIG_DEFINE_LOCALISED("Video", bool, VSync, true);
CONFIG_DEFINE_ENUM("Video", ETripleBuffering, TripleBuffering, ETripleBuffering::Auto);
CONFIG_DEFINE_LOCALISED("Video", int32_t, FPS, 60);
CONFIG_DEFINE("Video", bool, ShowFPS, false);
CONFIG_DEFINE("Video", uint32_t, MaxFrameLatency, 2);
CONFIG_DEFINE_LOCALISED("Video", float, Brightness, 0.5f);
CONFIG_DEFINE_ENUM_LOCALISED("Video", EAntiAliasing, AntiAliasing, EAntiAliasing::MSAA4x);
@@ -96,17 +73,23 @@ CONFIG_DEFINE_ENUM("Video", EDepthOfFieldQuality, DepthOfFieldQuality, EDepthOfF
CONFIG_DEFINE_ENUM_LOCALISED("Video", EMotionBlur, MotionBlur, EMotionBlur::Original);
CONFIG_DEFINE_LOCALISED("Video", bool, XboxColorCorrection, false);
CONFIG_DEFINE_ENUM_LOCALISED("Video", ECutsceneAspectRatio, CutsceneAspectRatio, ECutsceneAspectRatio::Original);
CONFIG_DEFINE_ENUM_LOCALISED("Video", EUIScaleMode, UIScaleMode, EUIScaleMode::Edge);
CONFIG_DEFINE_ENUM_LOCALISED("Video", EUIAlignmentMode, UIAlignmentMode, EUIAlignmentMode::Edge);
CONFIG_DEFINE_HIDDEN("Exports", bool, AllowCancellingUnleash, false);
CONFIG_DEFINE_HIDDEN("Exports", bool, DisableAutoSaveWarning, false);
CONFIG_DEFINE_HIDDEN("Exports", bool, DisableDLCIcon, false);
CONFIG_DEFINE_HIDDEN("Exports", bool, DisableDWMRoundedCorners, false);
CONFIG_DEFINE_HIDDEN("Exports", bool, FixUnleashOutOfControlDrain, false);
CONFIG_DEFINE_HIDDEN("Exports", bool, HomingAttackOnBoost, true);
CONFIG_DEFINE_HIDDEN("Exports", bool, HUDToggleHotkey, false);
CONFIG_DEFINE_HIDDEN("Exports", bool, SaveScoreAtCheckpoints, false);
CONFIG_DEFINE_HIDDEN("Exports", bool, SkipIntroLogos, false);
CONFIG_DEFINE_HIDDEN("Exports", bool, UseOfficialTitleOnTitleBar, false);
CONFIG_DEFINE_HIDDEN("Codes", bool, AllowCancellingUnleash, false);
CONFIG_DEFINE_HIDDEN("Codes", bool, DisableAutoSaveWarning, false);
CONFIG_DEFINE_HIDDEN("Codes", bool, DisableDLCIcon, false);
CONFIG_DEFINE_HIDDEN("Codes", bool, DisableDWMRoundedCorners, false);
CONFIG_DEFINE_HIDDEN("Codes", bool, EnableEventCollisionDebugView, false);
CONFIG_DEFINE_HIDDEN("Codes", bool, EnableGIMipLevelDebugView, false);
CONFIG_DEFINE_HIDDEN("Codes", bool, EnableObjectCollisionDebugView, false);
CONFIG_DEFINE_HIDDEN("Codes", bool, EnableStageCollisionDebugView, false);
CONFIG_DEFINE_HIDDEN("Codes", bool, FixEggmanlandUsingEventGalleryTransition, false);
CONFIG_DEFINE_HIDDEN("Codes", bool, FixUnleashOutOfControlDrain, false);
CONFIG_DEFINE_HIDDEN("Codes", bool, HomingAttackOnJump, false);
CONFIG_DEFINE_HIDDEN("Codes", bool, HUDToggleKey, false);
CONFIG_DEFINE_HIDDEN("Codes", bool, SaveScoreAtCheckpoints, false);
CONFIG_DEFINE_HIDDEN("Codes", bool, SkipIntroLogos, false);
CONFIG_DEFINE_HIDDEN("Codes", bool, UseArrowsForTimeOfDayTransition, false);
CONFIG_DEFINE_HIDDEN("Codes", bool, UseOfficialTitleOnTitleBar, false);
CONFIG_DEFINE("Update", time_t, LastChecked, 0);
+125 -5
View File
@@ -116,6 +116,66 @@ name = "DisableHintsMidAsmHook"
address = 0x827A2E34
jump_address_on_true = 0x827A2E4C
# Disable Egg Dragoon hint "V_WHG_083" ("That lit-up part on the bottom looks fishy. I'll try aiming for that.")
[[midasm_hook]]
name = "DisableHintsMidAsmHook"
address = 0x82AA3224
jump_address_on_true = 0x82AA3228
# Disable Egg Dragoon hint "V_CHP_057" ("That green round thing looks suspicious! Give it a bonk, Sonic!")
[[midasm_hook]]
name = "DisableHintsMidAsmHook"
address = 0x82AB1A00
jump_address_on_true = 0x82AB1A04
# Disable Dark Moray hints
[[midasm_hook]]
name = "DisableHintsMidAsmHook"
address = 0x829F7538
jump_address_on_true = 0x829F753C
# Disable Dark Moray hint "snow_boss_01" ("Ack, Sonic! You're frozen! Move the left stick left and right quickly to break free!")
[[midasm_hook]]
name = "DisableHintsMidAsmHook"
address = 0x829F7C90
jump_address_on_true = 0x829F7C94
# Disable Dark Guardian hint "V_CHP_051" ("Ooh, ooh! Sonic! What if you took the boxes over to the blue place!")
[[midasm_hook]]
name = "DisableHintsMidAsmHook"
address = 0x82A33008
jump_address_on_true = 0x82A33054
# Disable Dark Guardian hint "V_CHP_055" ("Looks like those annoying nightmares stay gone for a while if you clear 'em out!")
[[midasm_hook]]
name = "DisableHintsMidAsmHook"
address = 0x82A1DE14
jump_address_on_true = 0x82A1DE34
# Disable Dark Guardian hint "V_WHG_076" ("I keep beating him down and he gets right back up! There's gotta be a better way.")
[[midasm_hook]]
name = "DisableHintsMidAsmHook"
address = 0x82A260D8
jump_address_on_true = 0x82A26198
# Disable Dark Guardian hint "V_WHG_078" ("Looks like I can push this box thing.")
[[midasm_hook]]
name = "DisableHintsMidAsmHook"
address = 0x82A2F934
jump_address_on_true = 0x82A2F94C
# Disable Dark Gaia Phoenix hint "V_WHG_070" ("Ngh! Looks like I can't get at it while it's covered in fire...")
[[midasm_hook]]
name = "DisableHintsMidAsmHook"
address = 0x829D2388
jump_address_on_true = 0x829D238C
# Disable Dark Gaia Phoenix hint "V_WHG_071" ("Looks like he runs out of breath eventually. That's my chance.")
[[midasm_hook]]
name = "DisableHintsMidAsmHook"
address = 0x829E409C
jump_address_on_true = 0x829E40A0
# Disable navigation volumes
[[midasm_hook]]
name = "DisableControlTutorialMidAsmHook"
@@ -355,6 +415,11 @@ name = "WerehogBattleMusicMidAsmHook"
address = 0x82B47728
registers = ["r11"]
[[midasm_hook]]
name = "WerehogBattleMusicMidAsmHook"
address = 0x82B47C58
registers = ["r11"]
[[midasm_hook]]
name = "MotionBlurMidAsmHook"
address = 0x82BA99D0
@@ -522,11 +587,6 @@ address = 0x824B08C0
registers = ["r3"]
return_on_true = true
[[midasm_hook]]
name = "ToggleSubtitlesMidAsmHook"
address = 0x82B9BB74
registers = ["r27"]
[[midasm_hook]]
name = "AchievementManagerUnlockMidAsmHook"
address = 0x82BCFF28
@@ -662,6 +722,12 @@ name = "PostureDPadSupportXMidAsmHook"
address = 0x8266B6AC
registers = ["r29", "f0"]
# SWA::Player::CSonicStateStompingLand
[[midasm_hook]]
name = "PostureDPadSupportMidAsmHook"
address = 0x8231F824
registers = ["r29", "f12", "f13"]
# SWA::Boss::Temple::CTemple (shared)
[[midasm_hook]]
name = "PostureDPadSupportMidAsmHook"
@@ -680,6 +746,12 @@ name = "PostureDPadSupportMidAsmHook"
address = 0x82A7B288
registers = ["r30", "f13", "f10"]
# SWA::Boss::Temple::CTempleStateBoost / SWA::Boss::Temple::CTempleStateDamage / SWA::Boss::Temple::CTempleStateGuard
[[midasm_hook]]
name = "PostureDPadSupportXMidAsmHook"
address = 0x82A72358
registers = ["r30", "f13"]
# SWA::Player::CSuperSonicPostureInputSpaceHurrier
[[midasm_hook]]
name = "PostureSpaceHurrierDPadSupportXMidAsmHook"
@@ -817,6 +889,12 @@ name = "AddPrimitive2DMidAsmHook"
address = 0x82581C2C
registers = ["r3"]
# Shop
[[midasm_hook]]
name = "AddPrimitive2DMidAsmHook"
address = 0x82B2BCD8
registers = ["r3"]
[[midasm_hook]]
name = "ObjGetItemFieldOfViewMidAsmHook"
address = 0x82692934
@@ -936,3 +1014,45 @@ address = 0x82BADADC
registers = ["r4", "r5", "r6", "r30"]
jump_address_on_true = 0x82BAD9F0
jump_address_on_false = 0x82BADAFC
[[midasm_hook]]
name = "CExStageBossCStateBattleAllocMidAsmHook"
address = 0x82B026DC
registers = ["r3"]
after_instruction = true
[[midasm_hook]]
name = "CExStageBossCStateBattleCtorMidAsmHook"
address = 0x82B026E4
registers = ["r3"]
[[midasm_hook]]
name = "EvilHudGuideAllocMidAsmHook"
address = 0x823B6908
registers = ["r3"]
[[midasm_hook]]
name = "EvilHudGuideUpdateMidAsmHook"
address = 0x82449800
registers = ["r30", "f30"]
[[midasm_hook]]
name = "SparkleLocusMidAsmHook"
address = 0x82E96804
jump_address_on_true = 0x82E96808
# Rooftop Run barrel fix (works at up to ~400 FPS)
[[midasm_hook]]
name = "HighFrameRateDeltaTimeFixVectorMidAsmHook"
address = 0x827673CC
registers = ["v62"]
[[midasm_hook]]
name = "BossEggDragoonDrillMissileCMissileSetRotationMidAsmHook"
address = 0x82A9BADC
registers = ["r4"]
[[midasm_hook]]
name = "AnimationDataMakeMidAsmHook"
address = 0x82BB38E4
registers = ["r31", "r29", "r28"]