Compare commits

...

77 Commits

Author SHA1 Message Date
Skyth 5e2dafd7a0 Delay creation of textures. 2025-02-02 23:53:14 +03:00
Skyth (Asilkan) aaad10d797 Implement copy bypass optimization. (#262)
* Initial work on copy bypass optimization.

* Force depth stencil textures to be transient.

* Get rid of texture copying for shadow maps.

* Move barrier populate function.

* Set viewport/scissor rect explicitly for MSAA depth resolve.
2025-02-02 21:29:47 +03:00
Skyth (Asilkan) 342d696f99 Implement vertical marquee fade. (#261) 2025-02-02 20:09:19 +03:00
Hyper 0426b79094 config: update key bindings enum template 2025-02-02 15:51:16 +00:00
Hyper 3497d9b34b CTitleStateIntro_patches: fixed message inconsistencies (#259) 2025-02-01 13:37:48 +00:00
Darío 9d98d507b0 Add low-end defaults for low-end devices. (#252)
* Add low-end defaults for low-end devices. Uses either the reported type by the API or the VRAM.

* Update video.cpp

* Check for UMA flag on D3D12 to detect integrated GPUs.

* Display device type and UMA in inspector.

* Dynamic depth bias on F1.

---------

Co-authored-by: Skyth <19259897+blueskythlikesclouds@users.noreply.github.com>
2025-01-31 22:43:23 +03:00
Darío cd38776576 Update checker. (#251)
* Update checker.

* Fix build and enum class.

* Get rid of submodule for httplib.

* Get rid of submodule for curl.

* Minor style changes and fix video.cpp Linux build error.

* CTitleStateIntro_patches: implemented update message

* Update update_checker.cpp

* CTitleStateIntro_patches: fix fade out accepting input

---------

Co-authored-by: Hyper <34012267+hyperbx@users.noreply.github.com>
2025-01-31 16:47:15 +03:00
Darío 54d5588d79 Add fallback for creating other video backends. (#254)
* Add fallback for creating other video backends.

* Update video.cpp
2025-01-31 15:45:48 +03:00
Hyper ddd8ce77db Implemented key binding config (#257)
Co-authored-by: Skyth (Asilkan) <19259897+blueskythlikesclouds@users.noreply.github.com>
2025-01-31 11:37:15 +00:00
Hyper 8cba851212 api: added globals struct 2025-01-31 02:35:00 +00:00
Hyper 87d2ab5af1 locale: update localisation for Voice Language 2025-01-31 00:47:01 +00:00
Hyper f2e01bbe79 options_menu: rename Invert Camera X/Y options (#249) 2025-01-31 02:00:27 +03:00
Hyper ed0aeb3b7d options_menu: implemented value switch animations (#248) 2025-01-31 02:00:19 +03:00
Skyth (Asilkan) 21c1d36836 Allow specifying all corners for ImGui gradients. (#247) 2025-01-30 23:25:19 +03:00
DeaTh-G 70f042d11f Fix text clipping on installer wizard description field (#246) 2025-01-30 19:52:37 +03:00
Skyth (Asilkan) fefb08cc4b Put HUD toggle hotkey behind an export. (#245) 2025-01-30 19:16:36 +03:00
Skyth (Asilkan) 9fea5f9e4c Hide console on Release configuration. (#244) 2025-01-30 19:16:21 +03:00
Skyth (Asilkan) c78c2010a3 Apply extend left/right flags to a bunch of casts. (#242) 2025-01-30 17:12:51 +03:00
Skyth e89ecf8d68 Downmix installer sounds to stereo. 2025-01-30 14:31:04 +03:00
Skyth (Asilkan) 3171dc8266 Pick correct bullet particles in Tornado Defense depending on controller icons. (#241) 2025-01-30 12:53:24 +03:00
Skyth 1ef5b86036 Update resources submodule. 2025-01-30 12:21:27 +03:00
Skyth (Asilkan) e761c9f4d6 Disable ingame frame limiters. (#240) 2025-01-29 23:58:21 +03:00
DeaTh-G 4816d4aa9d Add ruby annotation support for text displayed in various spaces. (#232)
* allow preliminary annotation on DrawCenteredParagraph

* improve annotated text line spacing

* fix functionality of non-annotated paragraphs

* a lot of very bad code that but line wrapping works

* support ruby annotations for options menu descriptions

* make installer wizard more accurate

* remove wrapper function

* add furigana support to config names

* add furigana support for marquee text for options

* fully support annotated text in options menu

* fix option names being split to multiple lines

* fix and cleanup installer wizard text placements

* implement furigana support for message window

* remove regex usage

* remove excessive const ref usage
2025-01-29 20:43:21 +03:00
Skyth (Asilkan) 091b4ef089 Implement hack to fix options menu SFX not playing in stages. (#239) 2025-01-29 19:32:44 +03:00
Hyper 692542fb85 installer_wizard: update localisation 2025-01-29 13:21:05 +00:00
Hyper b26baea13c Apply correct alpha to toggle lights for greyed out buttons 2025-01-29 12:52:10 +00:00
Skyth (Asilkan) a19e434e9b Implement scroll bar animation in options menu. (#238) 2025-01-29 15:16:42 +03:00
Hyper 92e3cbea45 installer_wizard: add toggle light to language selection 2025-01-29 12:03:35 +00:00
Hyper 93f120ae83 Implemented toggle light (#237)
* Implemented toggle light, moved common textures to imgui_utils

* Fix compilation error.
2025-01-29 14:52:45 +03:00
Hyper aa6118b448 Implemented title animation for options menu (#235)
* options_menu: implemented title animation

* Adjust options title flash animation.

* Replace use of std::numbers::pi with M_PI.

* Implement shader modifier for options title rectangle.

* Replicate the same scaling applied to the rectangle in world map.

---------

Co-authored-by: Skyth <19259897+blueskythlikesclouds@users.noreply.github.com>
2025-01-29 14:39:10 +03:00
Hyper 863e1602ff imgui_utils: expose lerp values to BREATHE_MOTION macro 2025-01-29 07:18:36 +00:00
Hyper d5d2e83a10 installer_wizard: added breathing anim to title during installation 2025-01-29 07:13:47 +00:00
Hyper cb57e337b8 message_window: disable mouse checks in-game 2025-01-29 06:48:52 +00:00
Hyper 4fd08efa6d message_window: fix input and button guide inconsistencies 2025-01-29 02:38:41 +00:00
Hyper f6e43761b7 Added checks for unpatched XEX and XEXP for game detection (#234) 2025-01-28 22:22:36 +00:00
Skyth (Asilkan) 293f873229 Replicate Tornado Defense UI squash at original 4:3 aspect ratio. (#231) 2025-01-28 22:08:55 +03:00
Skyth (Asilkan) 4691fe2e5a Treat save file paths in mod loader INIs as save folders. (#230) 2025-01-28 20:02:25 +03:00
Skyth 9289ee5e02 Update XenonRecomp submodule. 2025-01-28 18:35:20 +03:00
Skyth (Asilkan) 50529a95fc Make pipeline precompilation/recompilation part of the consumer thread queue. (#229) 2025-01-28 18:24:53 +03:00
Skyth (Asilkan) 87d9e0dac7 Disable horizontal stretching at narrow aspect ratios. (#228) 2025-01-28 15:33:06 +03:00
Skyth (Asilkan) 996f23cc54 Hide tails icon in world map. (#227) 2025-01-28 14:58:38 +03:00
Hyper 8b345d2cbd Implemented Windows registry read/write (#225)
* Implemented Windows registry read/write

* Simplify registry API and handle path writes

* Linux SetWorkingDirectory

* Implement reading path and unicode string from windows registry

* Use string_view value names for registry

* Use RegGetValueW

* Paths adjustments

* /

* Update working directory update failure message

* Updated linux SetWorkingDirectory

* Update flatpak define

* Remove RootDirectoryPath and save registry at startup

* dont save registry on exit

* Slight formatting update

---------

Co-authored-by: Sajid <sajidur78@gmail.com>
2025-01-28 14:41:29 +03:00
Hyper 7b9b4245de SDL/HID fixes and clean-up (#224) 2025-01-28 00:38:46 +00:00
Skyth (Asilkan) ff29b3e786 Repeat world map info grid on center UI scale mode. (#222) 2025-01-28 01:45:04 +03:00
Skyth (Asilkan) 37d7e49d93 Keep world map icons broken at Original 4:3. (#219) 2025-01-27 23:57:08 +03:00
Skyth (Asilkan) 64a927615d Fix aspect ratio option sometimes crashing the game when changing it. (#218) 2025-01-27 23:47:34 +03:00
Hyper 9521cb3ee9 Added export to disable rounded corners on Windows 11 (#212) 2025-01-27 18:25:15 +00:00
Skyth (Asilkan) 948e2f271f Recompute camera projection matrix during resize to fix stretching on pause menu. (#215) 2025-01-27 19:17:31 +03:00
Skyth (Asilkan) f7ce4f179a Fix rope renderable material parameters during rendering. (#214) 2025-01-27 18:59:43 +03:00
Skyth (Asilkan) fc7918772b Fix options menu centering & tweak scanlines. (#213)
* Fix options menu centering, tweak accuracy.

* Accurate scanline outline drawing at 720p.

* Implement scanline improvements in installer wizard.
2025-01-27 18:00:07 +03:00
Skyth (Asilkan) a9677084ea Fix faulty ImGui clip rect usage. (#211) 2025-01-27 01:24:05 +03:00
Hyper a5db997e5d Fix controller input lingering when unfocused (#210) 2025-01-26 20:49:13 +00:00
Skyth (Asilkan) 4d41d8eaae Stretch title screen fade to the screen. (#209) 2025-01-26 23:42:32 +03:00
Hyper 7c989af42c input_patches: fix cursor velocity retaining after D-Pad input 2025-01-26 20:15:56 +00:00
Hyper dee2a36d0f Added Win32 file info (#198) 2025-01-26 16:18:25 +00:00
SuperSonic16 d2d8c541e1 Remove direct install arg from build-flatpak.yml (#206) 2025-01-26 17:32:35 +03:00
Skyth (Asilkan) 81d4390076 Fix ImGui mouse position events not working at boxed aspect ratios. (#205) 2025-01-26 17:23:29 +03:00
Hyper 57e888658a Fix PS3 time of day transition aspect ratio and outro animation (#197) 2025-01-26 13:37:55 +00:00
Skyth (Asilkan) 0d4b66fe98 Apply master volume in audio backend. (#202) 2025-01-26 13:54:08 +03:00
Skyth (Asilkan) 76ec5f032d Implement aspect ratio adjustments for inspire overlays. (#201)
* Handle texture/movie overlays for original 4:3.

* Implement aspect ratio adjustments for texture/movie overlays.

* Fix fade scale for original 4:3.
2025-01-26 13:19:39 +03:00
Hyper a9573584cd installer_wizard: move credits to header, improve formatting 2025-01-26 03:54:10 +00:00
Hyper 8e58cbcbf2 input_patches: clean up ambiguous naming
Changed all instances of "touch" or "touchpad" to "cursor" to clear up ambiguity with future input methods.
2025-01-26 00:37:33 +00:00
Hyper c1988e9323 Fix achievements button guide reappearing for Yes/No prompts (#193) 2025-01-25 21:16:43 +00:00
Hyper 5321ee0fba Fix Werehog columns not accepting D-Pad input (#192) 2025-01-25 20:37:56 +00:00
Hyper 1ee312442c Move patches/ui sources to patches directory (#191) 2025-01-25 18:58:26 +00:00
Hyper 5441075c5e Fix World Map cursor threshold and touchpad issues (#190)
* input_patches: fix cursor threshold and SFX during tutorial

* input_patches: fix touchpad flag magnetism
2025-01-25 18:51:25 +00:00
Skyth (Asilkan) 470d8f797b Stretch NPC fade in/out to the screen. (#189) 2025-01-25 19:06:13 +03:00
Skyth (Asilkan) d041e2ba30 Fix ImGui procedural filtering. (#188) 2025-01-25 18:28:43 +03:00
Skyth (Asilkan) e88ed2502c Restore previous fade values after resizing. (#187) 2025-01-25 16:29:38 +03:00
Skyth (Asilkan) e13b0ea009 Handle gameplay UI scaling not getting affected by explicit scene translation. (#183) 2025-01-25 14:19:24 +03:00
Skyth (Asilkan) 5d437f8de6 Ultrawide cast adjustments. (#170)
* Extend media room header frame.

* Extend world map tutorial scan lines.

* Apply offset scaling to EXP arrows.
2025-01-25 01:24:00 +03:00
Hyper c6a25f21c2 Implemented achievement data verification (#161) 2025-01-24 20:59:48 +00:00
Skyth d443693d78 Fix Dark Moray always being frozen. 2025-01-24 22:57:21 +03:00
Skyth (Asilkan) dfa2a31286 Fix PlayStation time of day transition animation not rendering. (#167)
* Fix PlayStation transition animation not rendering.

* Remove medal swinging animation from the YNCP.
2025-01-24 20:26:09 +03:00
Skyth (Asilkan) fb5d0cd94e Enforce linear filtering for debug 2D primitives. (#164) 2025-01-24 15:00:04 +03:00
Hyper cf5c3423d1 installer_wizard: fix credits scrolling progress and speed 2025-01-23 22:54:23 +00:00
Hyper f9440e2076 player_patches: fix last score not resetting to zero 2025-01-23 21:22:46 +00:00
98 changed files with 5344 additions and 2030 deletions
+1 -1
View File
@@ -48,7 +48,7 @@ jobs:
run: | run: |
echo "commit_message=$(git log -1 --pretty=%s)" >> $GITHUB_ENV echo "commit_message=$(git log -1 --pretty=%s)" >> $GITHUB_ENV
export CCACHE_DIR=/tmp/ccache export CCACHE_DIR=/tmp/ccache
flatpak-builder --user --force-clean --install-deps-from=flathub --repo=repo --install --ccache builddir ./flatpak/${{ env.FLATPAK_ID }}.json flatpak-builder --user --force-clean --install-deps-from=flathub --repo=repo --ccache builddir ./flatpak/${{ env.FLATPAK_ID }}.json
flatpak build-bundle repo ./${{ env.FLATPAK_ID }}.flatpak ${{ env.FLATPAK_ID }} --runtime-repo=https://flathub.org/repo/flathub.flatpakrepo flatpak build-bundle repo ./${{ env.FLATPAK_ID }}.flatpak ${{ env.FLATPAK_ID }} --runtime-repo=https://flathub.org/repo/flathub.flatpakrepo
# Uploads the built flatpak bundle to GitHub # Uploads the built flatpak bundle to GitHub
+3
View File
@@ -61,3 +61,6 @@
[submodule "thirdparty/implot"] [submodule "thirdparty/implot"]
path = thirdparty/implot path = thirdparty/implot
url = https://github.com/epezent/implot.git url = https://github.com/epezent/implot.git
[submodule "thirdparty/json"]
path = thirdparty/json
url = https://github.com/nlohmann/json
+69 -31
View File
@@ -129,15 +129,15 @@ set(UNLEASHED_RECOMP_HID_CXX_SOURCES
) )
set(UNLEASHED_RECOMP_PATCHES_CXX_SOURCES set(UNLEASHED_RECOMP_PATCHES_CXX_SOURCES
"patches/ui/CHudPause_patches.cpp"
"patches/ui/CTitleStateIntro_patches.cpp"
"patches/ui/CTitleStateMenu_patches.cpp"
"patches/ui/frontend_listener.cpp"
"patches/aspect_ratio_patches.cpp" "patches/aspect_ratio_patches.cpp"
"patches/audio_patches.cpp" "patches/audio_patches.cpp"
"patches/camera_patches.cpp" "patches/camera_patches.cpp"
"patches/CGameModeStageTitle_patches.cpp" "patches/CGameModeStageTitle_patches.cpp"
"patches/CHudPause_patches.cpp"
"patches/CTitleStateIntro_patches.cpp"
"patches/CTitleStateMenu_patches.cpp"
"patches/fps_patches.cpp" "patches/fps_patches.cpp"
"patches/frontend_listener.cpp"
"patches/input_patches.cpp" "patches/input_patches.cpp"
"patches/inspire_patches.cpp" "patches/inspire_patches.cpp"
"patches/misc_patches.cpp" "patches/misc_patches.cpp"
@@ -150,20 +150,21 @@ set(UNLEASHED_RECOMP_PATCHES_CXX_SOURCES
set(UNLEASHED_RECOMP_UI_CXX_SOURCES set(UNLEASHED_RECOMP_UI_CXX_SOURCES
"ui/achievement_menu.cpp" "ui/achievement_menu.cpp"
"ui/achievement_overlay.cpp" "ui/achievement_overlay.cpp"
"ui/installer_wizard.cpp"
"ui/button_guide.cpp" "ui/button_guide.cpp"
"ui/fader.cpp" "ui/fader.cpp"
"ui/message_window.cpp"
"ui/options_menu_thumbnails.cpp"
"ui/options_menu.cpp"
"ui/sdl_listener.cpp"
"ui/game_window.cpp" "ui/game_window.cpp"
"ui/imgui_utils.cpp"
"ui/installer_wizard.cpp"
"ui/message_window.cpp"
"ui/options_menu.cpp"
"ui/options_menu_thumbnails.cpp"
) )
set(UNLEASHED_RECOMP_INSTALL_CXX_SOURCES set(UNLEASHED_RECOMP_INSTALL_CXX_SOURCES
"install/installer.cpp" "install/installer.cpp"
"install/iso_file_system.cpp" "install/iso_file_system.cpp"
"install/memory_mapped_file.cpp" "install/memory_mapped_file.cpp"
"install/update_checker.cpp"
"install/xcontent_file_system.cpp" "install/xcontent_file_system.cpp"
"install/xex_patcher.cpp" "install/xex_patcher.cpp"
"install/hashes/apotos_shamar.cpp" "install/hashes/apotos_shamar.cpp"
@@ -178,7 +179,10 @@ set(UNLEASHED_RECOMP_INSTALL_CXX_SOURCES
set(UNLEASHED_RECOMP_USER_CXX_SOURCES set(UNLEASHED_RECOMP_USER_CXX_SOURCES
"user/achievement_data.cpp" "user/achievement_data.cpp"
"user/achievement_manager.cpp"
"user/config.cpp" "user/config.cpp"
"user/registry.cpp"
"user/paths.cpp"
) )
set(UNLEASHED_RECOMP_MOD_CXX_SOURCES set(UNLEASHED_RECOMP_MOD_CXX_SOURCES
@@ -205,6 +209,7 @@ set(UNLEASHED_RECOMP_THIRDPARTY_INCLUDES
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/ddspp" "${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/ddspp"
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/imgui" "${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/imgui"
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/implot" "${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/implot"
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/json/include"
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/libmspack/libmspack/mspack" "${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/libmspack/libmspack/mspack"
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/magic_enum/include" "${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/magic_enum/include"
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/stb" "${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/stb"
@@ -230,6 +235,7 @@ set(UNLEASHED_RECOMP_CXX_SOURCES
"exports.cpp" "exports.cpp"
"main.cpp" "main.cpp"
"misc_impl.cpp" "misc_impl.cpp"
"sdl_listener.cpp"
"stdafx.cpp" "stdafx.cpp"
"version.cpp" "version.cpp"
@@ -248,17 +254,62 @@ set(UNLEASHED_RECOMP_CXX_SOURCES
${UNLEASHED_RECOMP_THIRDPARTY_SOURCES} ${UNLEASHED_RECOMP_THIRDPARTY_SOURCES}
) )
include("version.cmake")
set(VERSION_TXT "${PROJECT_SOURCE_DIR}/res/version.txt")
# Only show Git info and build type if not Release.
set(SHOW_GIT_INFO_AND_BUILD_TYPE 0)
if (NOT ${CMAKE_BUILD_TYPE} MATCHES "Release")
set(SHOW_GIT_INFO_AND_BUILD_TYPE 1)
endif()
GenerateVersionSources(
OUTPUT_DIR ${PROJECT_SOURCE_DIR}
VERSION_TXT ${VERSION_TXT}
H_TEMPLATE "${PROJECT_SOURCE_DIR}/res/version.h.template"
CXX_TEMPLATE "${PROJECT_SOURCE_DIR}/res/version.cpp.template"
BUILD_TYPE ${CMAKE_BUILD_TYPE}
SHOW_GIT_INFO ${SHOW_GIT_INFO_AND_BUILD_TYPE}
SHOW_BUILD_TYPE ${SHOW_GIT_INFO_AND_BUILD_TYPE}
)
if (WIN32) if (WIN32)
# Set up Win32 resources for application icon. # Create binary version number for Win32 integer attributes.
set(ICON_PATH "${PROJECT_SOURCE_DIR}/../UnleashedRecompResources/images/game_icon.ico") CreateVersionString(
VERSION_TXT ${VERSION_TXT}
OUTPUT_CSV 1
OUTPUT_VAR WIN32_VERSION_BINARY
)
# Create string version number for Win32 detailed attributes.
CreateVersionString(
VERSION_TXT ${VERSION_TXT}
BUILD_TYPE ${CMAKE_BUILD_TYPE}
SHOW_GIT_INFO ${SHOW_GIT_INFO_AND_BUILD_TYPE}
SHOW_BUILD_TYPE ${SHOW_GIT_INFO_AND_BUILD_TYPE}
OUTPUT_VAR WIN32_VERSION_STRING
)
# Set Win32 icon path.
set(WIN32_ICON_PATH "${PROJECT_SOURCE_DIR}/../UnleashedRecompResources/images/game_icon.ico")
configure_file("res/win32/res.rc.template" "${CMAKE_BINARY_DIR}/res.rc" @ONLY) configure_file("res/win32/res.rc.template" "${CMAKE_BINARY_DIR}/res.rc" @ONLY)
add_executable(UnleashedRecomp ${UNLEASHED_RECOMP_CXX_SOURCES} "${CMAKE_BINARY_DIR}/res.rc") add_executable(UnleashedRecomp ${UNLEASHED_RECOMP_CXX_SOURCES} "${CMAKE_BINARY_DIR}/res.rc")
# Hide console for release configurations.
if (${CMAKE_BUILD_TYPE} MATCHES "Release")
target_link_options(UnleashedRecomp PRIVATE "/SUBSYSTEM:WINDOWS" "/ENTRY:mainCRTStartup")
endif()
else() else()
add_executable(UnleashedRecomp ${UNLEASHED_RECOMP_CXX_SOURCES}) add_executable(UnleashedRecomp ${UNLEASHED_RECOMP_CXX_SOURCES})
endif() endif()
if (UNLEASHED_RECOMP_FLATPAK) if (UNLEASHED_RECOMP_FLATPAK)
target_compile_definitions(UnleashedRecomp PRIVATE "GAME_INSTALL_DIRECTORY=\"/var/data\"") target_compile_definitions(UnleashedRecomp PRIVATE
"UNLEASHED_RECOMP_FLATPAK"
"GAME_INSTALL_DIRECTORY=\"/var/data\""
)
endif() endif()
if (UNLEASHED_RECOMP_D3D12) if (UNLEASHED_RECOMP_D3D12)
@@ -272,6 +323,7 @@ if (CMAKE_SYSTEM_NAME MATCHES "Linux")
endif() endif()
find_package(directx-dxc REQUIRED) find_package(directx-dxc REQUIRED)
find_package(CURL REQUIRED)
if (UNLEASHED_RECOMP_D3D12) if (UNLEASHED_RECOMP_D3D12)
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/D3D12) file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/D3D12)
@@ -317,6 +369,7 @@ target_link_libraries(UnleashedRecomp PRIVATE
tomlplusplus::tomlplusplus tomlplusplus::tomlplusplus
UnleashedRecompLib UnleashedRecompLib
xxHash::xxhash xxHash::xxhash
CURL::libcurl
) )
target_include_directories(UnleashedRecomp PRIVATE target_include_directories(UnleashedRecomp PRIVATE
@@ -360,6 +413,7 @@ function(compile_pixel_shader FILE_PATH)
compile_shader(${FILE_PATH} ps_6_0) compile_shader(${FILE_PATH} ps_6_0)
endfunction() endfunction()
compile_pixel_shader(blend_color_alpha_ps)
compile_vertex_shader(copy_vs) compile_vertex_shader(copy_vs)
compile_pixel_shader(csd_filter_ps) compile_pixel_shader(csd_filter_ps)
compile_vertex_shader(csd_no_tex_vs) compile_vertex_shader(csd_no_tex_vs)
@@ -407,23 +461,6 @@ generate_aggregate_header(
"${CMAKE_CURRENT_SOURCE_DIR}/api/SWA.h" "${CMAKE_CURRENT_SOURCE_DIR}/api/SWA.h"
) )
# Only show build type if not Release.
set(SHOW_GIT_INFO_AND_BUILD_TYPE 0)
if (NOT ${CMAKE_BUILD_TYPE} MATCHES "Release")
set(SHOW_GIT_INFO_AND_BUILD_TYPE 1)
endif()
include("version.cmake")
GenerateVersionSources(
OUTPUT_DIR "${PROJECT_SOURCE_DIR}"
VERSION_TXT "${PROJECT_SOURCE_DIR}/res/version.txt"
H_TEMPLATE "${PROJECT_SOURCE_DIR}/res/version.h.template"
CXX_TEMPLATE "${PROJECT_SOURCE_DIR}/res/version.cpp.template"
BUILD_TYPE ${CMAKE_BUILD_TYPE}
SHOW_GIT_INFO ${SHOW_GIT_INFO_AND_BUILD_TYPE}
SHOW_BUILD_TYPE ${SHOW_GIT_INFO_AND_BUILD_TYPE}
)
set(RESOURCES_SOURCE_PATH "${PROJECT_SOURCE_DIR}/../UnleashedRecompResources") set(RESOURCES_SOURCE_PATH "${PROJECT_SOURCE_DIR}/../UnleashedRecompResources")
set(RESOURCES_OUTPUT_PATH "${PROJECT_SOURCE_DIR}/res") set(RESOURCES_OUTPUT_PATH "${PROJECT_SOURCE_DIR}/res")
@@ -437,6 +474,7 @@ BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/co
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/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_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_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/light.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/light.dds" ARRAY_NAME "g_light" COMPRESSION_TYPE "zstd")
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/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_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") 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")
@@ -463,8 +501,7 @@ BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/op
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/gi_texture_filtering_bilinear.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/gi_texture_filtering_bilinear.dds" ARRAY_NAME "g_gi_texture_filtering_bilinear" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/gi_texture_filtering_bilinear.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/gi_texture_filtering_bilinear.dds" ARRAY_NAME "g_gi_texture_filtering_bilinear" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/gi_texture_filtering_bicubic.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/gi_texture_filtering_bicubic.dds" ARRAY_NAME "g_gi_texture_filtering_bicubic" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/gi_texture_filtering_bicubic.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/gi_texture_filtering_bicubic.dds" ARRAY_NAME "g_gi_texture_filtering_bicubic" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/hints.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/hints.dds" ARRAY_NAME "g_hints" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/hints.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/hints.dds" ARRAY_NAME "g_hints" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/invert_camera_x.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/invert_camera_x.dds" ARRAY_NAME "g_invert_camera_x" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/horizontal_camera.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/horizontal_camera.dds" ARRAY_NAME "g_horizontal_camera" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/invert_camera_y.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/invert_camera_y.dds" ARRAY_NAME "g_invert_camera_y" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/language.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/language.dds" ARRAY_NAME "g_language" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/language.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/language.dds" ARRAY_NAME "g_language" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/master_volume.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/master_volume.dds" ARRAY_NAME "g_master_volume" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/master_volume.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/master_volume.dds" ARRAY_NAME "g_master_volume" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/monitor.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/monitor.dds" ARRAY_NAME "g_monitor" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/monitor.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/monitor.dds" ARRAY_NAME "g_monitor" COMPRESSION_TYPE "zstd")
@@ -487,6 +524,7 @@ BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/op
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/transparency_antialiasing_false.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/transparency_antialiasing_false.dds" ARRAY_NAME "g_transparency_antialiasing_false" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/transparency_antialiasing_false.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/transparency_antialiasing_false.dds" ARRAY_NAME "g_transparency_antialiasing_false" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/transparency_antialiasing_true.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/transparency_antialiasing_true.dds" ARRAY_NAME "g_transparency_antialiasing_true" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/transparency_antialiasing_true.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/transparency_antialiasing_true.dds" ARRAY_NAME "g_transparency_antialiasing_true" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/ui_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_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/vertical_camera.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/vertical_camera.dds" ARRAY_NAME "g_vertical_camera" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/voice_language.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/voice_language.dds" ARRAY_NAME "g_voice_language" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/voice_language.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/voice_language.dds" ARRAY_NAME "g_voice_language" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/vibration.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/vibration.dds" ARRAY_NAME "g_vibration" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/vibration.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.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/vsync.dds" ARRAY_NAME "g_vsync" COMPRESSION_TYPE "zstd")
+2
View File
@@ -52,6 +52,7 @@
#include "Hedgehog/Universe/Engine/hhUpdateInfo.h" #include "Hedgehog/Universe/Engine/hhUpdateInfo.h"
#include "Hedgehog/Universe/Engine/hhUpdateUnit.h" #include "Hedgehog/Universe/Engine/hhUpdateUnit.h"
#include "Hedgehog/Universe/Thread/hhParallelJob.h" #include "Hedgehog/Universe/Thread/hhParallelJob.h"
#include "SWA/Achievement/AchievementID.h"
#include "SWA/Achievement/AchievementManager.h" #include "SWA/Achievement/AchievementManager.h"
#include "SWA/Achievement/AchievementTest.h" #include "SWA/Achievement/AchievementTest.h"
#include "SWA/CSD/CsdDatabaseWrapper.h" #include "SWA/CSD/CsdDatabaseWrapper.h"
@@ -61,6 +62,7 @@
#include "SWA/Camera/Camera.h" #include "SWA/Camera/Camera.h"
#include "SWA/Camera/CameraController.h" #include "SWA/Camera/CameraController.h"
#include "SWA/CharacterUtility/CharacterProxy.h" #include "SWA/CharacterUtility/CharacterProxy.h"
#include "SWA/Globals.h"
#include "SWA/HUD/GeneralWindow/GeneralWindow.h" #include "SWA/HUD/GeneralWindow/GeneralWindow.h"
#include "SWA/HUD/Loading/Loading.h" #include "SWA/HUD/Loading/Loading.h"
#include "SWA/HUD/Pause/HudPause.h" #include "SWA/HUD/Pause/HudPause.h"
@@ -0,0 +1,55 @@
#pragma once
enum EAchievementID : uint32_t
{
eAchievementID_StillBroken = 24,
eAchievementID_LookingBetter,
eAchievementID_StillAJigsawPuzzle,
eAchievementID_PickingUpThePieces,
eAchievementID_AlmostThere,
eAchievementID_OneMoreToGo,
eAchievementID_WorldSavior = 31,
eAchievementID_PartlyCloudy,
eAchievementID_Sunny,
eAchievementID_HalfMoon,
eAchievementID_FullMoon,
eAchievementID_BlueStreak,
eAchievementID_PowerOverwhelming,
eAchievementID_GettingTheHangOfThings,
eAchievementID_CreatureOfTheNight,
eAchievementID_HelpingHand,
eAchievementID_LayTheSmackdown,
eAchievementID_WallCrawler,
eAchievementID_Airdevil,
eAchievementID_Hyperdrive,
eAchievementID_Basher,
eAchievementID_Smasher,
eAchievementID_Crasher,
eAchievementID_Thrasher,
eAchievementID_SocialButterfly,
eAchievementID_HungryHungryHedgehog,
eAchievementID_AcePilot,
eAchievementID_DayTripper,
eAchievementID_HardDaysNight,
eAchievementID_GetOnTheExorciseBandwagon,
eAchievementID_GyroWithRelish = 64,
eAchievementID_PigInABlanket,
eAchievementID_ExoticToppings,
eAchievementID_SausageFriedRice,
eAchievementID_IcedHotdog,
eAchievementID_KebabOnABun,
eAchievementID_KetchupAndMustard,
eAchievementID_HardBoiled,
eAchievementID_FriedClamRoll,
eAchievementID_FirstTimeCustomer,
eAchievementID_OhYouShouldntHave,
eAchievementID_ThatsEnoughSeriously,
eAchievementID_Hedgehunk,
eAchievementID_IAintAfraidOfNoGhost,
eAchievementID_BFFs,
eAchievementID_SpeedingTicket,
eAchievementID_ComboKing,
eAchievementID_RingLeader,
eAchievementID_KnockoutBrawler,
eAchievementID_BlueMeteor
};
@@ -1,6 +1,7 @@
#pragma once #pragma once
#include <SWA.inl> #include <SWA.inl>
#include <SWA/Achievement/AchievementID.h>
namespace SWA::Achievement namespace SWA::Achievement
{ {
@@ -11,7 +12,7 @@ namespace SWA::Achievement
{ {
public: public:
SWA_INSERT_PADDING(0x08); SWA_INSERT_PADDING(0x08);
be<uint32_t> m_AchievementID; be<EAchievementID> m_AchievementID;
}; };
SWA_INSERT_PADDING(0x98); SWA_INSERT_PADDING(0x98);
@@ -1,6 +1,7 @@
#pragma once #pragma once
#include <SWA.inl> #include <SWA.inl>
#include <SWA/Achievement/AchievementID.h>
namespace SWA namespace SWA
{ {
@@ -9,7 +10,7 @@ namespace SWA
public: public:
SWA_INSERT_PADDING(0x38); SWA_INSERT_PADDING(0x38);
be<uint32_t> m_Unk1; be<uint32_t> m_Unk1;
be<uint32_t> m_AchievementID; be<EAchievementID> m_AchievementID;
uint8_t m_Unk2; uint8_t m_Unk2;
}; };
} }
+69
View File
@@ -0,0 +1,69 @@
#pragma once
#include <SWA.inl>
namespace SWA
{
struct SGlobals
{
// ms_DrawLightFieldSamplingPoint: サンプリング点をデバッグ表示
static inline bool* ms_DrawLightFieldSamplingPoint;
// ms_IgnoreLightFieldData: データを無視する
static inline bool* ms_IgnoreLightFieldData;
// IsCollisionRender
static inline bool* ms_IsCollisionRender;
// N/A
static inline bool* ms_IsLoading;
// IsObjectCollisionRender
static inline bool* ms_IsObjectCollisionRender;
// ms_IsRenderDebugDraw: デバッグ描画
static inline bool* ms_IsRenderDebugDraw;
// ms_IsRenderDebugDrawText: デバッグ文字描画
static inline bool* ms_IsRenderDebugDrawText;
// ms_IsRenderDebugPositionDraw: デバッグ位置描画
static inline bool* ms_IsRenderDebugPositionDraw;
// ms_IsRenderGameMainHud: ゲームメインHUD 描画
static inline bool* ms_IsRenderGameMainHud;
// ms_IsRenderHud: 全 HUD 描画
static inline bool* ms_IsRenderHud;
// ms_IsRenderHudPause: ポーズメニュー 描画
static inline bool* ms_IsRenderHudPause;
// IsTriggerRender
static inline bool* ms_IsTriggerRender;
// ms_LightFieldDebug: 値をデバッグ表示
static inline bool* ms_LightFieldDebug;
// VisualizeLoadedLevel: ミップレベルを視覚化 赤=0, 緑=1, 青=2, 黄=未ロード
static inline bool* ms_VisualizeLoadedLevel;
static void Init()
{
ms_DrawLightFieldSamplingPoint = (bool*)MmGetHostAddress(0x83367BCE);
ms_IgnoreLightFieldData = (bool*)MmGetHostAddress(0x83367BCF);
ms_IsCollisionRender = (bool*)MmGetHostAddress(0x833678A6);
ms_IsLoading = (bool*)MmGetHostAddress(0x83367A4C);
ms_IsObjectCollisionRender = (bool*)MmGetHostAddress(0x83367905);
ms_IsRenderDebugDraw = (bool*)MmGetHostAddress(0x8328BB23);
ms_IsRenderDebugDrawText = (bool*)MmGetHostAddress(0x8328BB25);
ms_IsRenderDebugPositionDraw = (bool*)MmGetHostAddress(0x8328BB24);
ms_IsRenderGameMainHud = (bool*)MmGetHostAddress(0x8328BB27);
ms_IsRenderHud = (bool*)MmGetHostAddress(0x8328BB26);
ms_IsRenderHudPause = (bool*)MmGetHostAddress(0x8328BB28);
ms_IsTriggerRender = (bool*)MmGetHostAddress(0x83367904);
ms_LightFieldDebug = (bool*)MmGetHostAddress(0x83367BCD);
ms_VisualizeLoadedLevel = (bool*)MmGetHostAddress(0x833678C1);
}
};
}
@@ -21,14 +21,20 @@ namespace SWA
{ {
public: public:
SWA_INSERT_PADDING(0xD8); SWA_INSERT_PADDING(0xD8);
be<uint32_t> m_pUnk; be<uint32_t> m_FieldD8;
SWA_INSERT_PADDING(0x3C); SWA_INSERT_PADDING(0x3C);
Chao::CSD::RCPtr<Chao::CSD::CScene> m_rcNightToDay; Chao::CSD::RCPtr<Chao::CSD::CScene> m_rcNightToDay;
SWA_INSERT_PADDING(0x0C); SWA_INSERT_PADDING(0x0C);
be<uint32_t> m_IsVisible; be<uint32_t> m_IsVisible;
SWA_INSERT_PADDING(0x0C); SWA_INSERT_PADDING(0x0C);
be<ELoadingDisplayType> m_LoadingDisplayType; be<ELoadingDisplayType> m_LoadingDisplayType;
SWA_INSERT_PADDING(0x65); SWA_INSERT_PADDING(0x61);
bool m_IsNightToDay; bool m_IsNightToDay;
}; };
SWA_ASSERT_OFFSETOF(CLoading, m_FieldD8, 0xD8);
SWA_ASSERT_OFFSETOF(CLoading, m_rcNightToDay, 0x118);
SWA_ASSERT_OFFSETOF(CLoading, m_IsVisible, 0x12C);
SWA_ASSERT_OFFSETOF(CLoading, m_LoadingDisplayType, 0x13C);
SWA_ASSERT_OFFSETOF(CLoading, m_IsNightToDay, 0x1A1);
} }
+23 -4
View File
@@ -48,8 +48,7 @@ namespace SWA
class CHudPause : public CGameObject class CHudPause : public CGameObject
{ {
public: public:
xpointer<void> m_pVftable; SWA_INSERT_PADDING(0x30);
SWA_INSERT_PADDING(0x2C);
RCPtr<CProject> m_rcPause; RCPtr<CProject> m_rcPause;
RCPtr<CScene> m_rcBg; RCPtr<CScene> m_rcBg;
RCPtr<CScene> m_rcBg1; RCPtr<CScene> m_rcBg1;
@@ -58,12 +57,32 @@ namespace SWA
RCPtr<CScene> m_rcBg1Select_2; RCPtr<CScene> m_rcBg1Select_2;
RCPtr<CScene> m_rcStatusTitle; RCPtr<CScene> m_rcStatusTitle;
RCPtr<CScene> m_rcFooterA; RCPtr<CScene> m_rcFooterA;
SWA_INSERT_PADDING(0x5C); SWA_INSERT_PADDING(0x59);
bool m_IsVisible;
SWA_INSERT_PADDING(0x02);
be<EActionType> m_Action; be<EActionType> m_Action;
be<EMenuType> m_Menu; be<EMenuType> m_Menu;
be<EStatusType> m_Status; be<EStatusType> m_Status;
be<ETransitionType> m_Transition; be<ETransitionType> m_Transition;
SWA_INSERT_PADDING(0x20); SWA_INSERT_PADDING(0x04);
be<uint32_t> m_Submenu;
SWA_INSERT_PADDING(0x18);
bool m_IsShown; bool m_IsShown;
}; };
SWA_ASSERT_OFFSETOF(CHudPause, m_rcPause, 0xEC);
SWA_ASSERT_OFFSETOF(CHudPause, m_rcBg, 0xF4);
SWA_ASSERT_OFFSETOF(CHudPause, m_rcBg1, 0xFC);
SWA_ASSERT_OFFSETOF(CHudPause, m_rcBg1_2, 0x104);
SWA_ASSERT_OFFSETOF(CHudPause, m_rcBg1Select, 0x10C);
SWA_ASSERT_OFFSETOF(CHudPause, m_rcBg1Select_2, 0x114);
SWA_ASSERT_OFFSETOF(CHudPause, m_rcStatusTitle, 0x11C);
SWA_ASSERT_OFFSETOF(CHudPause, m_rcFooterA, 0x124);
SWA_ASSERT_OFFSETOF(CHudPause, m_IsVisible, 0x185);
SWA_ASSERT_OFFSETOF(CHudPause, m_Action, 0x188);
SWA_ASSERT_OFFSETOF(CHudPause, m_Menu, 0x18C);
SWA_ASSERT_OFFSETOF(CHudPause, m_Status, 0x190);
SWA_ASSERT_OFFSETOF(CHudPause, m_Transition, 0x194);
SWA_ASSERT_OFFSETOF(CHudPause, m_Submenu, 0x19C);
SWA_ASSERT_OFFSETOF(CHudPause, m_IsShown, 0x1B8);
} }
@@ -115,7 +115,7 @@ namespace boost
} }
public: public:
shared_ptr() : px(nullptr), pn(nullptr) {} shared_ptr() : px(), pn() {}
// TODO // TODO
explicit shared_ptr(T* p) = delete; explicit shared_ptr(T* p) = delete;
+6 -1
View File
@@ -1,4 +1,5 @@
#include <app.h> #include "app.h"
#include <api/SWA.h>
#include <gpu/video.h> #include <gpu/video.h>
#include <install/installer.h> #include <install/installer.h>
#include <kernel/function.h> #include <kernel/function.h>
@@ -8,6 +9,7 @@
#include <ui/game_window.h> #include <ui/game_window.h>
#include <user/config.h> #include <user/config.h>
#include <user/paths.h> #include <user/paths.h>
#include <user/registry.h>
void App::Restart(std::vector<std::string> restartArgs) void App::Restart(std::vector<std::string> restartArgs)
{ {
@@ -34,6 +36,9 @@ PPC_FUNC(sub_824EB490)
App::s_isMissingDLC = !Installer::checkAllDLC(GetGamePath()); App::s_isMissingDLC = !Installer::checkAllDLC(GetGamePath());
App::s_language = Config::Language; App::s_language = Config::Language;
SWA::SGlobals::Init();
Registry::Save();
__imp__sub_824EB490(ctx, base); __imp__sub_824EB490(ctx, base);
} }
+6 -8
View File
@@ -94,6 +94,8 @@ void XAudioRegisterClient(PPCFunc* callback, uint32_t param)
void XAudioSubmitFrame(void* samples) void XAudioSubmitFrame(void* samples)
{ {
auto floatSamples = reinterpret_cast<be<float>*>(samples);
if (g_downMixToStereo) if (g_downMixToStereo)
{ {
// 0: left 1.0f, right 0.0f // 0: left 1.0f, right 0.0f
@@ -103,8 +105,6 @@ void XAudioSubmitFrame(void* samples)
// 4: left 1.0f, right 0.0f // 4: left 1.0f, right 0.0f
// 5: left 0.0f, right 1.0f // 5: left 0.0f, right 1.0f
auto floatSamples = reinterpret_cast<be<float>*>(samples);
std::array<float, 2 * XAUDIO_NUM_SAMPLES> audioFrames; std::array<float, 2 * XAUDIO_NUM_SAMPLES> audioFrames;
for (size_t i = 0; i < XAUDIO_NUM_SAMPLES; i++) for (size_t i = 0; i < XAUDIO_NUM_SAMPLES; i++)
@@ -116,22 +116,20 @@ void XAudioSubmitFrame(void* samples)
float ch4 = floatSamples[4 * XAUDIO_NUM_SAMPLES + i]; float ch4 = floatSamples[4 * XAUDIO_NUM_SAMPLES + i];
float ch5 = floatSamples[5 * XAUDIO_NUM_SAMPLES + i]; float ch5 = floatSamples[5 * XAUDIO_NUM_SAMPLES + i];
audioFrames[i * 2 + 0] = ch0 + ch2 * 0.75f + ch4; audioFrames[i * 2 + 0] = (ch0 + ch2 * 0.75f + ch4) * Config::MasterVolume;
audioFrames[i * 2 + 1] = ch1 + ch2 * 0.75f + ch5; audioFrames[i * 2 + 1] = (ch1 + ch2 * 0.75f + ch5) * Config::MasterVolume;
} }
SDL_QueueAudio(g_audioDevice, &audioFrames, sizeof(audioFrames)); SDL_QueueAudio(g_audioDevice, &audioFrames, sizeof(audioFrames));
} }
else else
{ {
auto rawSamples = reinterpret_cast<be<uint32_t>*>(samples); std::array<float, XAUDIO_NUM_CHANNELS * XAUDIO_NUM_SAMPLES> audioFrames;
std::array<uint32_t, XAUDIO_NUM_CHANNELS * XAUDIO_NUM_SAMPLES> audioFrames;
for (size_t i = 0; i < XAUDIO_NUM_SAMPLES; i++) for (size_t i = 0; i < XAUDIO_NUM_SAMPLES; i++)
{ {
for (size_t j = 0; j < XAUDIO_NUM_CHANNELS; j++) for (size_t j = 0; j < XAUDIO_NUM_CHANNELS; j++)
audioFrames[i * XAUDIO_NUM_CHANNELS + j] = rawSamples[j * XAUDIO_NUM_SAMPLES + i]; audioFrames[i * XAUDIO_NUM_CHANNELS + j] = floatSamples[j * XAUDIO_NUM_SAMPLES + i] * Config::MasterVolume;
} }
SDL_QueueAudio(g_audioDevice, &audioFrames, sizeof(audioFrames)); SDL_QueueAudio(g_audioDevice, &audioFrames, sizeof(audioFrames));
+1 -1
View File
@@ -93,7 +93,7 @@ static void PlayEmbeddedSound(EmbeddedSound s)
void EmbeddedPlayer::Init() void EmbeddedPlayer::Init()
{ {
Mix_OpenAudio(XAUDIO_SAMPLES_HZ, AUDIO_F32SYS, XAUDIO_NUM_CHANNELS, 256); Mix_OpenAudio(XAUDIO_SAMPLES_HZ, AUDIO_F32SYS, 2, 256);
s_isActive = true; s_isActive = true;
} }
+2
View File
@@ -7,6 +7,8 @@
typedef returnType _##procName(__VA_ARGS__); \ typedef returnType _##procName(__VA_ARGS__); \
_##procName* procName = (_##procName*)PROC_ADDRESS(libraryName, #procName); _##procName* procName = (_##procName*)PROC_ADDRESS(libraryName, #procName);
#define STR(x) #x
template<typename T> template<typename T>
inline T RoundUp(const T& in_rValue, uint32_t in_round) inline T RoundUp(const T& in_rValue, uint32_t in_round)
{ {
+10 -6
View File
@@ -5,10 +5,12 @@
#define IMGUI_SHADER_MODIFIER_CHECKERBOARD 2 #define IMGUI_SHADER_MODIFIER_CHECKERBOARD 2
#define IMGUI_SHADER_MODIFIER_SCANLINE_BUTTON 3 #define IMGUI_SHADER_MODIFIER_SCANLINE_BUTTON 3
#define IMGUI_SHADER_MODIFIER_TEXT_SKEW 4 #define IMGUI_SHADER_MODIFIER_TEXT_SKEW 4
#define IMGUI_SHADER_MODIFIER_MARQUEE_FADE 5 #define IMGUI_SHADER_MODIFIER_HORIZONTAL_MARQUEE_FADE 5
#define IMGUI_SHADER_MODIFIER_GRAYSCALE 6 #define IMGUI_SHADER_MODIFIER_VERTICAL_MARQUEE_FADE 6
#define IMGUI_SHADER_MODIFIER_TITLE_BEVEL 7 #define IMGUI_SHADER_MODIFIER_GRAYSCALE 7
#define IMGUI_SHADER_MODIFIER_CATEGORY_BEVEL 8 #define IMGUI_SHADER_MODIFIER_TITLE_BEVEL 8
#define IMGUI_SHADER_MODIFIER_CATEGORY_BEVEL 9
#define IMGUI_SHADER_MODIFIER_RECTANGLE_BEVEL 10
#ifdef __cplusplus #ifdef __cplusplus
@@ -31,8 +33,10 @@ union ImGuiCallbackData
{ {
float boundsMin[2]; float boundsMin[2];
float boundsMax[2]; float boundsMax[2];
uint32_t gradientTop; uint32_t gradientTopLeft;
uint32_t gradientBottom; uint32_t gradientTopRight;
uint32_t gradientBottomRight;
uint32_t gradientBottomLeft;
} setGradient; } setGradient;
struct struct
+9
View File
@@ -3324,6 +3324,14 @@ namespace plume {
dynamicDepthBiasOption = d3d12Options16.DynamicDepthBiasSupported; dynamicDepthBiasOption = d3d12Options16.DynamicDepthBiasSupported;
} }
// Check if the architecture has UMA.
bool uma = false;
D3D12_FEATURE_DATA_ARCHITECTURE1 architecture1 = {};
res = deviceOption->CheckFeatureSupport(D3D12_FEATURE_ARCHITECTURE1, &architecture1, sizeof(architecture1));
if (SUCCEEDED(res)) {
uma = architecture1.UMA;
}
// Pick this adapter and device if it has better feature support than the current one. // Pick this adapter and device if it has better feature support than the current one.
bool preferOverNothing = (adapter == nullptr) || (d3d == nullptr); bool preferOverNothing = (adapter == nullptr) || (d3d == nullptr);
bool preferVideoMemory = adapterDesc.DedicatedVideoMemory > description.dedicatedVideoMemory; bool preferVideoMemory = adapterDesc.DedicatedVideoMemory > description.dedicatedVideoMemory;
@@ -3346,6 +3354,7 @@ namespace plume {
capabilities.sampleLocations = samplePositionsOption; capabilities.sampleLocations = samplePositionsOption;
capabilities.triangleFan = triangleFanSupportOption; capabilities.triangleFan = triangleFanSupportOption;
capabilities.dynamicDepthBias = dynamicDepthBiasOption; capabilities.dynamicDepthBias = dynamicDepthBiasOption;
capabilities.uma = uma;
description.name = Utf16ToUtf8(adapterDesc.Description); description.name = Utf16ToUtf8(adapterDesc.Description);
description.dedicatedVideoMemory = adapterDesc.DedicatedVideoMemory; description.dedicatedVideoMemory = adapterDesc.DedicatedVideoMemory;
@@ -475,6 +475,14 @@ namespace plume {
typedef uint32_t RenderSampleCounts; typedef uint32_t RenderSampleCounts;
enum class RenderDeviceType {
UNKNOWN,
INTEGRATED,
DISCRETE,
VIRTUAL,
CPU
};
// Global functions. // Global functions.
constexpr uint32_t RenderFormatSize(RenderFormat format) { constexpr uint32_t RenderFormatSize(RenderFormat format) {
@@ -1754,6 +1762,7 @@ namespace plume {
struct RenderDeviceDescription { struct RenderDeviceDescription {
std::string name = "Unknown"; std::string name = "Unknown";
RenderDeviceType type = RenderDeviceType::UNKNOWN;
uint32_t driverVersion = 0; uint32_t driverVersion = 0;
uint64_t dedicatedVideoMemory = 0; uint64_t dedicatedVideoMemory = 0;
}; };
@@ -1780,6 +1789,9 @@ namespace plume {
// Draw. // Draw.
bool triangleFan = false; bool triangleFan = false;
bool dynamicDepthBias = false; bool dynamicDepthBias = false;
// UMA.
bool uma = false;
}; };
struct RenderInterfaceCapabilities { struct RenderInterfaceCapabilities {
+16
View File
@@ -705,6 +705,21 @@ namespace plume {
} }
} }
static RenderDeviceType toDeviceType(VkPhysicalDeviceType type) {
switch (type) {
case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
return RenderDeviceType::INTEGRATED;
case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
return RenderDeviceType::DISCRETE;
case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
return RenderDeviceType::VIRTUAL;
case VK_PHYSICAL_DEVICE_TYPE_CPU:
return RenderDeviceType::CPU;
default:
return RenderDeviceType::UNKNOWN;
}
}
static void setObjectName(VkDevice device, VkDebugReportObjectTypeEXT objectType, uint64_t object, const std::string &name) { static void setObjectName(VkDevice device, VkDebugReportObjectTypeEXT objectType, uint64_t object, const std::string &name) {
# ifdef VULKAN_OBJECT_NAMES_ENABLED # ifdef VULKAN_OBJECT_NAMES_ENABLED
VkDebugMarkerObjectNameInfoEXT nameInfo = {}; VkDebugMarkerObjectNameInfoEXT nameInfo = {};
@@ -3496,6 +3511,7 @@ namespace plume {
if (preferOption) { if (preferOption) {
physicalDevice = physicalDevices[i]; physicalDevice = physicalDevices[i];
description.name = std::string(deviceProperties.deviceName); description.name = std::string(deviceProperties.deviceName);
description.type = toDeviceType(deviceProperties.deviceType);
description.driverVersion = deviceProperties.driverVersion; description.driverVersion = deviceProperties.driverVersion;
currentDeviceTypeScore = deviceTypeScore; currentDeviceTypeScore = deviceTypeScore;
} }
@@ -0,0 +1,41 @@
#include "../../../tools/XenosRecomp/XenosRecomp/shader_common.h"
#ifdef __spirv__
#define g_SrcAlpha_DestAlpha vk::RawBufferLoad<float4>(g_PushConstants.PixelShaderConstants + 2400, 0x10)
#define s0_Texture2DDescriptorIndex vk::RawBufferLoad<uint>(g_PushConstants.SharedConstants + 0)
#define s0_SamplerDescriptorIndex vk::RawBufferLoad<uint>(g_PushConstants.SharedConstants + 192)
#else
cbuffer PixelShaderConstants : register(b1, space4)
{
float4 g_SrcAlpha_DestAlpha : packoffset(c150);
};
cbuffer SharedConstants : register(b2, space4)
{
uint s0_Texture2DDescriptorIndex : packoffset(c0.x);
uint s0_SamplerDescriptorIndex : packoffset(c12.x);
DEFINE_SHARED_CONSTANTS();
};
#endif
float4 main(
in float4 iPos : SV_Position,
in float4 iTexCoord0 : TEXCOORD0) : SV_Target0
{
Texture2D<float4> texture = g_Texture2DDescriptorHeap[s0_Texture2DDescriptorIndex];
SamplerState samplerState = g_SamplerDescriptorHeap[s0_SamplerDescriptorIndex];
float4 color = texture.Sample(samplerState, iTexCoord0.xy);
if (any(or(iTexCoord0.xy < 0.0, iTexCoord0.xy > 1.0)))
color = float4(0.0, 0.0, 0.0, 1.0);
color.rgb *= color.a * g_SrcAlpha_DestAlpha.x;
color.a = g_SrcAlpha_DestAlpha.y + (1.0 - color.a) * g_SrcAlpha_DestAlpha.x;
return color;
}
@@ -38,7 +38,7 @@ cbuffer SharedConstants : register(b2, space4)
#endif #endif
float4 main(in float4 position : SV_Position, in float2 texCoord : TEXCOORD0) : SV_Target float4 main(in float4 position : SV_Position, in float4 texCoord : TEXCOORD0) : SV_Target
{ {
Texture2D<float4> sampColor = g_Texture2DDescriptorHeap[sampColor_Texture2DDescriptorIndex]; Texture2D<float4> sampColor = g_Texture2DDescriptorHeap[sampColor_Texture2DDescriptorIndex];
Texture2D<float4> sampVelocityMap = g_Texture2DDescriptorHeap[sampVelocityMap_Texture2DDescriptorIndex]; Texture2D<float4> sampVelocityMap = g_Texture2DDescriptorHeap[sampVelocityMap_Texture2DDescriptorIndex];
@@ -48,19 +48,19 @@ float4 main(in float4 position : SV_Position, in float2 texCoord : TEXCOORD0) :
SamplerState sampVelocityMap_s = g_SamplerDescriptorHeap[sampVelocityMap_SamplerDescriptorIndex]; SamplerState sampVelocityMap_s = g_SamplerDescriptorHeap[sampVelocityMap_SamplerDescriptorIndex];
SamplerState sampZBuffer_s = g_SamplerDescriptorHeap[sampZBuffer_SamplerDescriptorIndex]; SamplerState sampZBuffer_s = g_SamplerDescriptorHeap[sampZBuffer_SamplerDescriptorIndex];
float depth = sampZBuffer.SampleLevel(sampZBuffer_s, texCoord, 0).x; float depth = sampZBuffer.SampleLevel(sampZBuffer_s, texCoord.xy, 0).x;
float4 velocityMap = sampVelocityMap.SampleLevel(sampVelocityMap_s, texCoord, 0); float4 velocityMap = sampVelocityMap.SampleLevel(sampVelocityMap_s, texCoord.xy, 0);
float2 velocity = (velocityMap.xz + velocityMap.yw / 255.0) * 2.0 - 1.0; float2 velocity = (velocityMap.xz + velocityMap.yw / 255.0) * 2.0 - 1.0;
int sampleCount = min(64, round(length(velocity * g_ViewportSize.xy))); int sampleCount = min(64, round(length(velocity * g_ViewportSize.xy)));
float2 sampleOffset = velocity / (float) sampleCount; float2 sampleOffset = velocity / (float) sampleCount;
float3 color = sampColor.SampleLevel(sampColor_s, texCoord, 0).rgb; float3 color = sampColor.SampleLevel(sampColor_s, texCoord.xy, 0).rgb;
int count = 1; int count = 1;
for (int i = 1; i <= sampleCount; i++) for (int i = 1; i <= sampleCount; i++)
{ {
float2 sampleCoord = texCoord + sampleOffset * i; float2 sampleCoord = texCoord.xy + sampleOffset * i;
float3 sampleColor = sampColor.SampleLevel(sampColor_s, sampleCoord, 0).rgb; float3 sampleColor = sampColor.SampleLevel(sampColor_s, sampleCoord, 0).rgb;
float sampleDepth = sampZBuffer.SampleLevel(sampZBuffer_s, sampleCoord, 0).x; float sampleDepth = sampZBuffer.SampleLevel(sampZBuffer_s, sampleCoord, 0).x;
@@ -6,8 +6,10 @@ struct PushConstants
{ {
float2 BoundsMin; float2 BoundsMin;
float2 BoundsMax; float2 BoundsMax;
uint GradientTop; uint GradientTopLeft;
uint GradientBottom; uint GradientTopRight;
uint GradientBottomRight;
uint GradientBottomLeft;
uint ShaderModifier; uint ShaderModifier;
uint Texture2DDescriptorIndex; uint Texture2DDescriptorIndex;
float2 InverseDisplaySize; float2 InverseDisplaySize;
+38 -8
View File
@@ -61,16 +61,16 @@ float4 SampleLinear(float2 uvTexspace)
float4 PixelAntialiasing(float2 uvTexspace) float4 PixelAntialiasing(float2 uvTexspace)
{ {
float2 seam = floor(uvTexspace + 0.5);
uvTexspace = (uvTexspace - seam) / fwidth(uvTexspace) + seam;
uvTexspace = clamp(uvTexspace, seam - 0.5, seam + 0.5);
if ((g_PushConstants.InverseDisplaySize.y / g_PushConstants.InverseDisplaySize.x) >= (4.0 / 3.0)) if ((g_PushConstants.InverseDisplaySize.y / g_PushConstants.InverseDisplaySize.x) >= (4.0 / 3.0))
uvTexspace *= g_PushConstants.InverseDisplaySize.y * 720.0f; uvTexspace *= g_PushConstants.InverseDisplaySize.y * 720.0f;
else else
uvTexspace *= g_PushConstants.InverseDisplaySize.x * 960.0f; uvTexspace *= g_PushConstants.InverseDisplaySize.x * 960.0f;
return SampleLinear(uvTexspace); float2 seam = floor(uvTexspace + 0.5);
uvTexspace = (uvTexspace - seam) / fwidth(uvTexspace) + seam;
uvTexspace = clamp(uvTexspace, seam - 0.5, seam + 0.5);
return SampleLinear(uvTexspace - 0.5);
} }
float median(float r, float g, float b) float median(float r, float g, float b)
@@ -81,7 +81,7 @@ float median(float r, float g, float b)
float4 main(in Interpolators interpolators) : SV_Target float4 main(in Interpolators interpolators) : SV_Target
{ {
float4 color = interpolators.Color; float4 color = interpolators.Color;
color *= PixelAntialiasing(interpolators.Position.xy - (g_PushConstants.ProceduralOrigin + 0.5)); color *= PixelAntialiasing(interpolators.Position.xy - g_PushConstants.ProceduralOrigin);
if (g_PushConstants.Texture2DDescriptorIndex != 0) if (g_PushConstants.Texture2DDescriptorIndex != 0)
{ {
@@ -135,7 +135,7 @@ float4 main(in Interpolators interpolators) : SV_Target
} }
} }
if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_MARQUEE_FADE) 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 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.x);
@@ -143,10 +143,40 @@ float4 main(in Interpolators interpolators) : SV_Target
color.a *= minAlpha; color.a *= minAlpha;
color.a *= maxAlpha; 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 maxAlpha = saturate((g_PushConstants.BoundsMax.y - interpolators.Position.y) / g_PushConstants.Scale.y);
color.a *= minAlpha;
color.a *= maxAlpha;
}
else if (any(g_PushConstants.BoundsMin != g_PushConstants.BoundsMax)) else if (any(g_PushConstants.BoundsMin != g_PushConstants.BoundsMax))
{ {
float2 factor = saturate((interpolators.Position.xy - g_PushConstants.BoundsMin) / (g_PushConstants.BoundsMax - g_PushConstants.BoundsMin)); float2 factor = saturate((interpolators.Position.xy - g_PushConstants.BoundsMin) / (g_PushConstants.BoundsMax - g_PushConstants.BoundsMin));
color *= lerp(DecodeColor(g_PushConstants.GradientTop), DecodeColor(g_PushConstants.GradientBottom), smoothstep(0.0, 1.0, factor.y));
if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_RECTANGLE_BEVEL)
{
float bevelSize = 0.9;
float shadow = saturate((factor.x - bevelSize) / (1.0 - bevelSize));
shadow = max(shadow, saturate((factor.y - bevelSize) / (1.0 - bevelSize)));
float rim = saturate((1.0 - factor.x - bevelSize) / (1.0 - bevelSize));
rim = max(rim, saturate((1.0 - factor.y - bevelSize) / (1.0 - bevelSize)));
float3 rimColor = float3(1, 0.8, 0.29);
float3 shadowColor = float3(0.84, 0.57, 0);
color.rgb = lerp(color.rgb, rimColor, smoothstep(0.0, 1.0, rim));
color.rgb = lerp(color.rgb, shadowColor, smoothstep(0.0, 1.0, shadow));
}
else
{
float4 top = lerp(DecodeColor(g_PushConstants.GradientTopLeft), DecodeColor(g_PushConstants.GradientTopRight), smoothstep(0.0, 1.0, factor.x));
float4 bottom = lerp(DecodeColor(g_PushConstants.GradientBottomLeft), DecodeColor(g_PushConstants.GradientBottomRight), smoothstep(0.0, 1.0, factor.x));
color *= lerp(top, bottom, smoothstep(0.0, 1.0, factor.y));
}
} }
if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_GRAYSCALE) if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_GRAYSCALE)
+2 -1
View File
@@ -7,7 +7,8 @@ void main(in float2 position : POSITION, in float2 uv : TEXCOORD, in float4 colo
if (position.y < g_PushConstants.Origin.y) if (position.y < g_PushConstants.Origin.y)
position.x += g_PushConstants.Scale.x; position.x += g_PushConstants.Scale.x;
} }
else if (g_PushConstants.ShaderModifier != IMGUI_SHADER_MODIFIER_MARQUEE_FADE) else if (g_PushConstants.ShaderModifier != IMGUI_SHADER_MODIFIER_HORIZONTAL_MARQUEE_FADE &&
g_PushConstants.ShaderModifier != IMGUI_SHADER_MODIFIER_VERTICAL_MARQUEE_FADE)
{ {
position.xy = g_PushConstants.Origin + (position.xy - g_PushConstants.Origin) * g_PushConstants.Scale; position.xy = g_PushConstants.Origin + (position.xy - g_PushConstants.Origin) * g_PushConstants.Scale;
} }
File diff suppressed because it is too large Load Diff
+7 -1
View File
@@ -18,7 +18,7 @@ struct Video
static inline uint32_t s_viewportWidth; static inline uint32_t s_viewportWidth;
static inline uint32_t s_viewportHeight; static inline uint32_t s_viewportHeight;
static void CreateHostDevice(const char *sdlVideoDriver); static bool CreateHostDevice(const char *sdlVideoDriver);
static void WaitOnSwapChain(); static void WaitOnSwapChain();
static void Present(); static void Present();
static void StartPipelinePrecompilation(); static void StartPipelinePrecompilation();
@@ -154,10 +154,15 @@ struct GuestBaseTexture : GuestResource
struct GuestTexture : GuestBaseTexture struct GuestTexture : GuestBaseTexture
{ {
uint32_t depth = 0; uint32_t depth = 0;
uint32_t levels = 0;
RenderTextureViewDimension viewDimension = RenderTextureViewDimension::UNKNOWN; RenderTextureViewDimension viewDimension = RenderTextureViewDimension::UNKNOWN;
RenderComponentMapping componentMapping;
void* mappedMemory = nullptr; void* mappedMemory = nullptr;
std::unique_ptr<RenderFramebuffer> framebuffer; std::unique_ptr<RenderFramebuffer> framebuffer;
std::unique_ptr<GuestTexture> patchedTexture; std::unique_ptr<GuestTexture> patchedTexture;
struct GuestSurface* sourceSurface = nullptr;
void CreateTexture();
}; };
struct GuestLockedRect struct GuestLockedRect
@@ -205,6 +210,7 @@ struct GuestSurface : GuestBaseTexture
uint32_t guestFormat = 0; uint32_t guestFormat = 0;
ankerl::unordered_dense::map<const RenderTexture*, std::unique_ptr<RenderFramebuffer>> framebuffers; ankerl::unordered_dense::map<const RenderTexture*, std::unique_ptr<RenderFramebuffer>> framebuffers;
RenderSampleCounts sampleCount = RenderSampleCount::COUNT_1; RenderSampleCounts sampleCount = RenderSampleCount::COUNT_1;
ankerl::unordered_dense::set<GuestTexture*> destinationTextures;
}; };
enum GuestDeclType enum GuestDeclType
+29 -15
View File
@@ -68,7 +68,7 @@ public:
bool CanPoll() bool CanPoll()
{ {
return controller && (GameWindow::s_isFocused || Config::AllowBackgroundInput); return controller;
} }
void PollAxis() void PollAxis()
@@ -127,6 +127,11 @@ public:
SDL_GameControllerRumble(controller, vibration.wLeftMotorSpeed * 256, vibration.wRightMotorSpeed * 256, VIBRATION_TIMEOUT_MS); SDL_GameControllerRumble(controller, vibration.wLeftMotorSpeed * 256, vibration.wRightMotorSpeed * 256, VIBRATION_TIMEOUT_MS);
} }
void SetLED(const uint8_t r, const uint8_t g, const uint8_t b) const
{
SDL_GameControllerSetLED(controller, r, g, b);
}
}; };
std::array<Controller, 4> g_controllers; std::array<Controller, 4> g_controllers;
@@ -182,6 +187,15 @@ static void SetControllerInputDevice(Controller* controller)
} }
} }
static void SetControllerTimeOfDayLED(Controller& controller, bool isNight)
{
auto r = isNight ? 22 : 0;
auto g = isNight ? 0 : 37;
auto b = isNight ? 101 : 184;
controller.SetLED(r, g, b);
}
int HID_OnSDLEvent(void*, SDL_Event* event) int HID_OnSDLEvent(void*, SDL_Event* event)
{ {
switch (event->type) switch (event->type)
@@ -191,7 +205,13 @@ int HID_OnSDLEvent(void*, SDL_Event* event)
const auto freeIndex = FindFreeController(); const auto freeIndex = FindFreeController();
if (freeIndex != -1) if (freeIndex != -1)
g_controllers[freeIndex] = Controller(event->cdevice.which); {
auto controller = Controller(event->cdevice.which);
g_controllers[freeIndex] = controller;
SetControllerTimeOfDayLED(controller, App::s_isWerehog);
}
break; break;
} }
@@ -258,17 +278,8 @@ int HID_OnSDLEvent(void*, SDL_Event* event)
case SDL_USER_EVILSONIC: case SDL_USER_EVILSONIC:
{ {
auto* controller = FindController(event->cdevice.which); for (auto& controller : g_controllers)
SetControllerTimeOfDayLED(controller, event->user.code);
if (!controller)
break;
auto isNight = event->user.code;
auto r = isNight ? 22 : 0;
auto g = isNight ? 0 : 37;
auto b = isNight ? 101 : 184;
SDL_GameControllerSetLED(controller->controller, r, g, b);
break; break;
} }
@@ -279,12 +290,15 @@ int HID_OnSDLEvent(void*, SDL_Event* event)
void hid::Init() void hid::Init()
{ {
SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_GAMECUBE, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS3, "1"); SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS3, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4, "1"); SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5, "1"); SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_GAMECUBE, "1"); SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_PLAYER_LED, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_WII, "1"); SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_WII, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
SDL_SetHint(SDL_HINT_XINPUT_ENABLED, "1"); SDL_SetHint(SDL_HINT_XINPUT_ENABLED, "1");
SDL_InitSubSystem(SDL_INIT_EVENTS); SDL_InitSubSystem(SDL_INIT_EVENTS);
+7
View File
@@ -1,4 +1,6 @@
#include "hid.h" #include "hid.h"
#include <ui/game_window.h>
#include <user/config.h>
hid::EInputDevice hid::g_inputDevice; hid::EInputDevice hid::g_inputDevice;
hid::EInputDevice hid::g_inputDeviceController; hid::EInputDevice hid::g_inputDeviceController;
@@ -11,6 +13,11 @@ void hid::SetProhibitedButtons(uint16_t wButtons)
hid::g_prohibitedButtons = wButtons; hid::g_prohibitedButtons = wButtons;
} }
bool hid::IsInputAllowed()
{
return GameWindow::s_isFocused || Config::AllowBackgroundInput;
}
bool hid::IsInputDeviceController() bool hid::IsInputDeviceController()
{ {
return hid::g_inputDevice != hid::EInputDevice::Keyboard && return hid::g_inputDevice != hid::EInputDevice::Keyboard &&
+2 -1
View File
@@ -35,12 +35,13 @@ namespace hid
extern uint16_t g_prohibitedButtons; extern uint16_t g_prohibitedButtons;
void Init(); void Init();
void SetProhibitedButtons(uint16_t wButtons);
uint32_t GetState(uint32_t dwUserIndex, XAMINPUT_STATE* pState); uint32_t GetState(uint32_t dwUserIndex, XAMINPUT_STATE* pState);
uint32_t SetState(uint32_t dwUserIndex, XAMINPUT_VIBRATION* pVibration); uint32_t SetState(uint32_t dwUserIndex, XAMINPUT_VIBRATION* pVibration);
uint32_t GetCapabilities(uint32_t dwUserIndex, XAMINPUT_CAPABILITIES* pCaps); uint32_t GetCapabilities(uint32_t dwUserIndex, XAMINPUT_CAPABILITIES* pCaps);
void SetProhibitedButtons(uint16_t wButtons);
bool IsInputAllowed();
bool IsInputDeviceController(); bool IsInputDeviceController();
std::string GetInputDeviceName(); std::string GetInputDeviceName();
} }
+11 -1
View File
@@ -201,7 +201,17 @@ static DLC detectDLC(const std::filesystem::path &sourcePath, VirtualFileSystem
bool Installer::checkGameInstall(const std::filesystem::path &baseDirectory, std::filesystem::path &modulePath) bool Installer::checkGameInstall(const std::filesystem::path &baseDirectory, std::filesystem::path &modulePath)
{ {
modulePath = baseDirectory / PatchedDirectory / GameExecutableFile; modulePath = baseDirectory / PatchedDirectory / GameExecutableFile;
return std::filesystem::exists(modulePath);
if (!std::filesystem::exists(modulePath))
return false;
if (!std::filesystem::exists(baseDirectory / UpdateDirectory / UpdateExecutablePatchFile))
return false;
if (!std::filesystem::exists(baseDirectory / GameDirectory / GameExecutableFile))
return false;
return true;
} }
bool Installer::checkDLCInstall(const std::filesystem::path &baseDirectory, DLC dlc) bool Installer::checkDLCInstall(const std::filesystem::path &baseDirectory, DLC dlc)
+170
View File
@@ -0,0 +1,170 @@
#include "update_checker.h"
#include <curl/curl.h>
#include <nlohmann/json.hpp>
#include "version.h"
#ifdef WIN32
#include <shellapi.h>
#endif
// UpdateChecker
using json = nlohmann::json;
static const char *CHECK_URL = "https://api.github.com/repos/hedge-dev/UnleashedRecomp/releases/latest";
static const char *VISIT_URL = "https://github.com/hedge-dev/UnleashedRecomp/releases/latest";
static const char *USER_AGENT = "UnleashedRecomp-Agent";
static std::atomic<bool> g_updateCheckerInProgress = false;
static std::atomic<bool> g_updateCheckerFinished = false;
static UpdateChecker::Result g_updateCheckerResult = UpdateChecker::Result::NotStarted;
size_t updateCheckerWriteCallback(void *contents, size_t size, size_t nmemb, std::string *output)
{
size_t totalSize = size * nmemb;
output->append((char *)contents, totalSize);
return totalSize;
}
static bool parseVersion(const std::string &versionStr, int &major, int &minor, int &revision)
{
size_t start = 0;
if (versionStr[0] == 'v')
{
start = 1;
}
size_t firstDot = versionStr.find('.', start);
size_t secondDot = versionStr.find('.', firstDot + 1);
if (firstDot == std::string::npos || secondDot == std::string::npos)
{
return false;
}
try
{
major = std::stoi(versionStr.substr(start, firstDot - start));
minor = std::stoi(versionStr.substr(firstDot + 1, secondDot - firstDot - 1));
revision = std::stoi(versionStr.substr(secondDot + 1));
}
catch (const std::exception &e)
{
fmt::println("Error while parsing version: {}.", e.what());
return false;
}
return true;
}
void updateCheckerThread()
{
CURL *curl = curl_easy_init();
CURLcode res;
int major, minor, revision;
std::string response;
curl_easy_setopt(curl, CURLOPT_URL, CHECK_URL);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, updateCheckerWriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl, CURLOPT_USERAGENT, USER_AGENT);
res = curl_easy_perform(curl);
if (res == CURLE_OK)
{
try
{
json root = json::parse(response);
auto tag_name_element = root.find("tag_name");
if (tag_name_element != root.end() && tag_name_element->is_string())
{
if (parseVersion(*tag_name_element, major, minor, revision))
{
if ((g_versionMajor < major) || (g_versionMajor == major && g_versionMinor < minor) || (g_versionMajor == major && g_versionMinor == minor && g_versionRevision < revision))
{
g_updateCheckerResult = UpdateChecker::Result::UpdateAvailable;
}
else
{
g_updateCheckerResult = UpdateChecker::Result::AlreadyUpToDate;
}
}
else
{
fmt::println("Error while parsing response: tag_name does not contain a valid version string.");
g_updateCheckerResult = UpdateChecker::Result::Failed;
}
}
else
{
fmt::println("Error while parsing response: tag_name not found or not the right type.");
g_updateCheckerResult = UpdateChecker::Result::Failed;
}
}
catch (const json::exception &e)
{
fmt::println("Error while parsing response: {}", e.what());
g_updateCheckerResult = UpdateChecker::Result::Failed;
}
}
else
{
fmt::println("Error while performing request: {}", curl_easy_strerror(res));
g_updateCheckerResult = UpdateChecker::Result::Failed;
}
curl_easy_cleanup(curl);
g_updateCheckerFinished = true;
g_updateCheckerInProgress = false;
}
void UpdateChecker::initialize()
{
curl_global_init(CURL_GLOBAL_DEFAULT);
}
bool UpdateChecker::start()
{
if (g_updateCheckerInProgress)
{
return false;
}
g_updateCheckerInProgress = true;
g_updateCheckerFinished = false;
std::thread thread(&updateCheckerThread);
thread.detach();
return true;
}
UpdateChecker::Result UpdateChecker::check()
{
if (g_updateCheckerFinished)
{
return g_updateCheckerResult;
}
else if (g_updateCheckerInProgress)
{
return UpdateChecker::Result::InProgress;
}
else
{
return UpdateChecker::Result::NotStarted;
}
}
void UpdateChecker::visitWebsite()
{
#if defined(WIN32)
ShellExecuteA(0, 0, VISIT_URL, 0, 0, SW_SHOW);
#elif defined(__linux__)
std::string command = "xdg-open " + std::string(VISIT_URL) + " &";
std::system(command.c_str());
#else
static_assert(false, "Visit website not implemented for this platform.");
#endif
}
+18
View File
@@ -0,0 +1,18 @@
#pragma once
struct UpdateChecker
{
enum class Result
{
NotStarted,
InProgress,
AlreadyUpToDate,
UpdateAvailable,
Failed
};
static void initialize();
static bool start();
static Result check();
static void visitWebsite();
};
+31 -21
View File
@@ -405,52 +405,62 @@ uint32_t XamInputGetState(uint32_t userIndex, uint32_t flags, XAMINPUT_STATE* st
{ {
memset(state, 0, sizeof(*state)); memset(state, 0, sizeof(*state));
uint32_t result = hid::GetState(userIndex, state); if (hid::IsInputAllowed())
hid::GetState(userIndex, state);
if (GameWindow::s_isFocused)
{
auto keyboardState = SDL_GetKeyboardState(NULL); auto keyboardState = SDL_GetKeyboardState(NULL);
if (keyboardState[SDL_SCANCODE_UP]) if (GameWindow::s_isFocused && !keyboardState[SDL_SCANCODE_LALT])
{
if (keyboardState[Config::Key_LeftStickUp])
state->Gamepad.sThumbLY = 32767; state->Gamepad.sThumbLY = 32767;
if (keyboardState[SDL_SCANCODE_DOWN]) if (keyboardState[Config::Key_LeftStickDown])
state->Gamepad.sThumbLY = -32768; state->Gamepad.sThumbLY = -32768;
if (keyboardState[SDL_SCANCODE_LEFT]) if (keyboardState[Config::Key_LeftStickLeft])
state->Gamepad.sThumbLX = -32768; state->Gamepad.sThumbLX = -32768;
if (keyboardState[SDL_SCANCODE_RIGHT]) if (keyboardState[Config::Key_LeftStickRight])
state->Gamepad.sThumbLX = 32767; state->Gamepad.sThumbLX = 32767;
if (keyboardState[SDL_SCANCODE_1]) if (keyboardState[Config::Key_RightStickUp])
state->Gamepad.sThumbRY = 32767;
if (keyboardState[Config::Key_RightStickDown])
state->Gamepad.sThumbRY = -32768;
if (keyboardState[Config::Key_RightStickLeft])
state->Gamepad.sThumbRX = -32768;
if (keyboardState[Config::Key_RightStickRight])
state->Gamepad.sThumbRX = 32767;
if (keyboardState[Config::Key_LeftTrigger])
state->Gamepad.bLeftTrigger = 0xFF; state->Gamepad.bLeftTrigger = 0xFF;
if (keyboardState[SDL_SCANCODE_3]) if (keyboardState[Config::Key_RightTrigger])
state->Gamepad.bRightTrigger = 0xFF; state->Gamepad.bRightTrigger = 0xFF;
if (keyboardState[SDL_SCANCODE_I]) if (keyboardState[Config::Key_DPadUp])
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_DPAD_UP; state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_DPAD_UP;
if (keyboardState[SDL_SCANCODE_K]) if (keyboardState[Config::Key_DPadDown])
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_DPAD_DOWN; state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_DPAD_DOWN;
if (keyboardState[SDL_SCANCODE_J]) if (keyboardState[Config::Key_DPadLeft])
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_DPAD_LEFT; state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_DPAD_LEFT;
if (keyboardState[SDL_SCANCODE_L]) if (keyboardState[Config::Key_DPadRight])
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_DPAD_RIGHT; state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_DPAD_RIGHT;
if (keyboardState[SDL_SCANCODE_RETURN] && !keyboardState[SDL_SCANCODE_LALT]) if (keyboardState[Config::Key_Start])
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_START; state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_START;
if (keyboardState[SDL_SCANCODE_BACKSPACE]) if (keyboardState[Config::Key_Back])
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_BACK; state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_BACK;
if (keyboardState[SDL_SCANCODE_Q]) if (keyboardState[Config::Key_LeftBumper])
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_LEFT_SHOULDER; state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_LEFT_SHOULDER;
if (keyboardState[SDL_SCANCODE_E]) if (keyboardState[Config::Key_RightBumper])
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_RIGHT_SHOULDER; state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_RIGHT_SHOULDER;
if (keyboardState[SDL_SCANCODE_S]) if (keyboardState[Config::Key_A])
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_A; state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_A;
if (keyboardState[SDL_SCANCODE_D]) if (keyboardState[Config::Key_B])
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_B; state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_B;
if (keyboardState[SDL_SCANCODE_A]) if (keyboardState[Config::Key_X])
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_X; state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_X;
if (keyboardState[SDL_SCANCODE_W]) if (keyboardState[Config::Key_Y])
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_Y; state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_Y;
} }
+61 -15
View File
@@ -49,6 +49,52 @@ CONFIG_DEFINE_ENUM_LOCALE(ELanguage)
} }
}; };
CONFIG_DEFINE_ENUM_LOCALE(EVoiceLanguage)
{
{
ELanguage::English,
{
{ EVoiceLanguage::English, { "ENGLISH", "" } },
{ EVoiceLanguage::Japanese, { "JAPANESE", "" } }
}
},
{
ELanguage::Japanese,
{
{ EVoiceLanguage::English, { "英語", "" } },
{ EVoiceLanguage::Japanese, { "日本語", "" } }
}
},
{
ELanguage::German,
{
{ EVoiceLanguage::English, { "ENGLISCH", "" } },
{ EVoiceLanguage::Japanese, { "JAPANISCH", "" } }
}
},
{
ELanguage::French,
{
{ EVoiceLanguage::English, { "ANGLAIS", "" } },
{ EVoiceLanguage::Japanese, { "JAPONAIS", "" } }
}
},
{
ELanguage::Spanish,
{
{ EVoiceLanguage::English, { "INGLÉS", "" } },
{ EVoiceLanguage::Japanese, { "JAPONÉS", "" } }
}
},
{
ELanguage::Italian,
{
{ EVoiceLanguage::English, { "INGLESE", "" } },
{ EVoiceLanguage::Japanese, { "GIAPPONESE", "" } }
}
},
};
CONFIG_DEFINE_LOCALE(Hints) CONFIG_DEFINE_LOCALE(Hints)
{ {
{ ELanguage::English, { "Hints", "Show hint rings in stages." } } { ELanguage::English, { "Hints", "Show hint rings in stages." } }
@@ -97,14 +143,25 @@ CONFIG_DEFINE_ENUM_LOCALE(EControllerIcons)
} }
}; };
CONFIG_DEFINE_LOCALE(InvertCameraX) CONFIG_DEFINE_LOCALE(HorizontalCamera)
{ {
{ ELanguage::English, { "Invert Camera X", "Toggle between inverted left and right camera movement." } } { ELanguage::English, { "Horizontal Camera", "Change how the camera moves left and right." } }
}; };
CONFIG_DEFINE_LOCALE(InvertCameraY) CONFIG_DEFINE_LOCALE(VerticalCamera)
{ {
{ ELanguage::English, { "Invert Camera Y", "Toggle between inverted up and down camera movement." } } { ELanguage::English, { "Vertical Camera", "Change how the camera moves up and down." } }
};
CONFIG_DEFINE_ENUM_LOCALE(ECameraRotationMode)
{
{
ELanguage::English,
{
{ ECameraRotationMode::Normal, { "NORMAL", "" } },
{ ECameraRotationMode::Reverse, { "REVERSE", "" } }
}
}
}; };
CONFIG_DEFINE_LOCALE(Vibration) CONFIG_DEFINE_LOCALE(Vibration)
@@ -158,17 +215,6 @@ 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." } }
}; };
CONFIG_DEFINE_ENUM_LOCALE(EVoiceLanguage)
{
{
ELanguage::English,
{
{ EVoiceLanguage::English, { "ENGLISH", "" } },
{ EVoiceLanguage::Japanese, { "日本語", "" } }
}
}
};
CONFIG_DEFINE_LOCALE(Subtitles) CONFIG_DEFINE_LOCALE(Subtitles)
{ {
{ ELanguage::English, { "Subtitles", "Show subtitles during dialogue." } } { ELanguage::English, { "Subtitles", "Show subtitles during dialogue." } }
+40 -7
View File
@@ -24,7 +24,12 @@ std::unordered_map<std::string, std::unordered_map<ELanguage, std::string>> g_lo
{ {
"Options_Header_Name", "Options_Header_Name",
{ {
{ ELanguage::English, "OPTIONS" } { ELanguage::English, "OPTIONS" },
{ ELanguage::Japanese, "OPTION" },
{ ELanguage::German, "OPTIONEN" },
{ ELanguage::French, "OPTIONS" },
{ ELanguage::Spanish, "OPCIONES" },
{ ELanguage::Italian, "OPZIONI" }
} }
}, },
{ {
@@ -165,26 +170,26 @@ std::unordered_map<std::string, std::unordered_map<ELanguage, std::string>> g_lo
{ {
"Installer_Page_Introduction", "Installer_Page_Introduction",
{ {
{ ELanguage::English, "Welcome to Unleashed Recompiled!\n\nYou'll need an Xbox 360 copy\nof Sonic Unleashed in order to proceed with the installation." }, { 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 Unleashed Recompiled!\n\nDovrai avere una copia di\nSonic Unleashed per la Xbox 360\nper proseguire con l'installazione." } { 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", "Installer_Page_SelectGameAndUpdate",
{ {
{ ELanguage::English, "Add the files for the game and its title update. You can use digital dumps or an extracted folder containing the unmodified files." } { ELanguage::English, "Add the sources for the game and its title update." }
} }
}, },
{ {
"Installer_Page_SelectDLC", "Installer_Page_SelectDLC",
{ {
{ ELanguage::English, "Add the files for the DLC. You can use digital dumps or an extracted folder containing the unmodified files." } { ELanguage::English, "Add the sources for the DLC." }
} }
}, },
{ {
"Installer_Page_CheckSpace", "Installer_Page_CheckSpace",
{ {
{ ELanguage::English, "The content will be installed to the program's folder. Please confirm you have enough free space.\n\n" }, { 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::Italian, "Il contenuto verrà installato nella cartella di questo programma. Assicurati di avere abbastanza spazio libero sul tuo hard disk.\n\n" }
} }
}, },
@@ -343,7 +348,7 @@ std::unordered_map<std::string, std::unordered_map<ELanguage, std::string>> g_lo
} }
}, },
{ {
// Notes: message appears when the SYS-DATA is corrupted (mismatching file size). // Notes: message appears when SYS-DATA is corrupted (mismatching file size) upon pressing start at the title screen.
// To make this occur, open the file in any editor and just remove a large chunk of data. // To make this occur, open the file in any editor and just remove a large chunk of data.
// Do not localise this unless absolutely necessary, these strings are from the XEX. // Do not localise this unless absolutely necessary, these strings are from the XEX.
"Title_Message_SaveDataCorrupt", "Title_Message_SaveDataCorrupt",
@@ -356,6 +361,34 @@ std::unordered_map<std::string, std::unordered_map<ELanguage, std::string>> g_lo
{ ELanguage::Italian, "I file di salvataggio sembrano danneggiati\ne non possono essere caricati." } { ELanguage::Italian, "I file di salvataggio sembrano danneggiati\ne non possono essere caricati." }
} }
}, },
{
// Notes: message appears when ACH-DATA is corrupted (mismatching file size, bad signature, incorrect version or invalid checksum) upon pressing start at the title screen.
// 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." }
}
},
{
// Notes: message appears when ACH-DATA cannot be loaded upon pressing start at the title screen.
// 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." }
}
},
{
"Title_Message_UpdateAvailable",
{
{ ELanguage::English, "An update is available!\n\nWould you like to visit the\nreleases page to download it?" }
}
},
{
"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." }
}
},
{ {
"Common_On", "Common_On",
{ {
+39 -5
View File
@@ -10,15 +10,23 @@
#include <xex.h> #include <xex.h>
#include <apu/audio.h> #include <apu/audio.h>
#include <hid/hid.h> #include <hid/hid.h>
#include <user/achievement_data.h>
#include <user/config.h> #include <user/config.h>
#include <user/paths.h> #include <user/paths.h>
#include <user/registry.h>
#include <kernel/xdbf.h> #include <kernel/xdbf.h>
#include <install/installer.h> #include <install/installer.h>
#include <install/update_checker.h>
#include <os/logger.h> #include <os/logger.h>
#include <os/process.h>
#include <os/registry.h>
#include <ui/game_window.h>
#include <ui/installer_wizard.h> #include <ui/installer_wizard.h>
#include <mod/mod_loader.h> #include <mod/mod_loader.h>
#ifdef _WIN32
#include <timeapi.h>
#endif
const size_t XMAIOBegin = 0x7FEA0000; const size_t XMAIOBegin = 0x7FEA0000;
const size_t XMAIOEnd = XMAIOBegin + 0x0000FFFF; const size_t XMAIOEnd = XMAIOBegin + 0x0000FFFF;
@@ -145,6 +153,9 @@ int main(int argc, char *argv[])
timeBeginPeriod(1); timeBeginPeriod(1);
#endif #endif
if (!os::registry::Init())
LOGN_WARNING("OS doesn't support registry");
os::logger::Init(); os::logger::Init();
bool forceInstaller = false; bool forceInstaller = false;
@@ -167,6 +178,21 @@ int main(int argc, char *argv[])
Config::Load(); Config::Load();
// Check the time since the last time an update was checked. Store the new time if the difference is more than six hours.
constexpr double TimeBetweenUpdateChecksInSeconds = 6 * 60 * 60;
time_t timeNow = std::time(nullptr);
double timeDifferenceSeconds = difftime(timeNow, Config::LastChecked);
if (timeDifferenceSeconds > TimeBetweenUpdateChecksInSeconds)
{
UpdateChecker::initialize();
UpdateChecker::start();
Config::LastChecked = timeNow;
Config::Save();
}
if (Config::ShowConsole)
os::process::ShowConsole();
HostStartup(); HostStartup();
std::filesystem::path modulePath; std::filesystem::path modulePath;
@@ -174,7 +200,11 @@ int main(int argc, char *argv[])
bool runInstallerWizard = forceInstaller || forceDLCInstaller || !isGameInstalled; bool runInstallerWizard = forceInstaller || forceDLCInstaller || !isGameInstalled;
if (runInstallerWizard) if (runInstallerWizard)
{ {
Video::CreateHostDevice(sdlVideoDriver); if (!Video::CreateHostDevice(sdlVideoDriver))
{
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, GameWindow::GetTitle(), Localise("Video_BackendError").c_str(), GameWindow::s_pWindow);
return 1;
}
if (!InstallerWizard::Run(GAME_INSTALL_DIRECTORY, isGameInstalled && forceDLCInstaller)) if (!InstallerWizard::Run(GAME_INSTALL_DIRECTORY, isGameInstalled && forceDLCInstaller))
{ {
@@ -184,14 +214,18 @@ int main(int argc, char *argv[])
ModLoader::Init(); ModLoader::Init();
AchievementData::Load();
KiSystemStartup(); KiSystemStartup();
uint32_t entry = LdrLoadModule(modulePath); uint32_t entry = LdrLoadModule(modulePath);
if (!runInstallerWizard) if (!runInstallerWizard)
Video::CreateHostDevice(sdlVideoDriver); {
if (!Video::CreateHostDevice(sdlVideoDriver))
{
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, GameWindow::GetTitle(), Localise("Video_BackendError").c_str(), GameWindow::s_pWindow);
return 1;
}
}
Video::StartPipelinePrecompilation(); Video::StartPipelinePrecompilation();
+16 -5
View File
@@ -6,6 +6,7 @@
#include <kernel/function.h> #include <kernel/function.h>
#include <kernel/heap.h> #include <kernel/heap.h>
#include <user/paths.h> #include <user/paths.h>
#include <os/process.h>
#include <xxHashMap.h> #include <xxHashMap.h>
enum class ModType enum class ModType
@@ -90,7 +91,7 @@ std::vector<std::filesystem::path>* ModLoader::GetIncludeDirectories(size_t modI
void ModLoader::Init() void ModLoader::Init()
{ {
std::filesystem::path userPath = GetUserPath(); const std::filesystem::path& userPath = GetUserPath();
IniFile configIni; IniFile configIni;
if (!configIni.read(userPath / "cpkredir.ini")) if (!configIni.read(userPath / "cpkredir.ini"))
@@ -110,9 +111,14 @@ void ModLoader::Init()
if (!saveFilePathU8.empty()) if (!saveFilePathU8.empty())
ModLoader::s_saveFilePath = std::u8string_view((const char8_t*)saveFilePathU8.c_str()); ModLoader::s_saveFilePath = std::u8string_view((const char8_t*)saveFilePathU8.c_str());
else else
ModLoader::s_saveFilePath = userPath / "mlsave/SYS-DATA"; ModLoader::s_saveFilePath = userPath / "mlsave";
ModLoader::s_saveFilePath /= "SYS-DATA";
} }
if (configIni.getString("CPKREDIR", "LogType", std::string()) == "console")
os::process::ShowConsole();
std::string modsDbIniFilePathU8 = configIni.getString("CPKREDIR", "ModsDbIni", ""); std::string modsDbIniFilePathU8 = configIni.getString("CPKREDIR", "ModsDbIni", "");
if (modsDbIniFilePathU8.empty()) if (modsDbIniFilePathU8.empty())
return; return;
@@ -187,15 +193,20 @@ void ModLoader::Init()
modSaveFilePathU8 = modIni.getString("Main", "SaveFile", std::string()); modSaveFilePathU8 = modIni.getString("Main", "SaveFile", std::string());
} }
if (!mod.includeDirs.empty())
g_mods.emplace_back(std::move(mod));
if (!modSaveFilePathU8.empty()) if (!modSaveFilePathU8.empty())
{ {
std::replace(modSaveFilePathU8.begin(), modSaveFilePathU8.end(), '\\', '/'); std::replace(modSaveFilePathU8.begin(), modSaveFilePathU8.end(), '\\', '/');
ModLoader::s_saveFilePath = modDirectoryPath / std::u8string_view((const char8_t*)modSaveFilePathU8.c_str()); ModLoader::s_saveFilePath = modDirectoryPath / std::u8string_view((const char8_t*)modSaveFilePathU8.c_str());
// Save file paths in HMM mods are treated as folders.
if (mod.type == ModType::HMM)
ModLoader::s_saveFilePath /= "SYS-DATA";
foundModSaveFilePath = true; foundModSaveFilePath = true;
} }
if (!mod.includeDirs.empty())
g_mods.emplace_back(std::move(mod));
} }
} }
@@ -29,6 +29,11 @@ std::filesystem::path os::process::GetWorkingDirectory()
} }
} }
bool os::process::SetWorkingDirectory(const std::filesystem::path& path)
{
return chdir(path.c_str()) == 0;
}
bool os::process::StartProcess(const std::filesystem::path& path, const std::vector<std::string>& args, std::filesystem::path work) bool os::process::StartProcess(const std::filesystem::path& path, const std::vector<std::string>& args, std::filesystem::path work)
{ {
pid_t pid = fork(); pid_t pid = fork();
@@ -55,3 +60,8 @@ bool os::process::StartProcess(const std::filesystem::path& path, const std::vec
return true; return true;
} }
void os::process::ShowConsole()
{
// Unnecessary on Linux.
}
@@ -0,0 +1,21 @@
#include <os/registry.h>
// TODO: Implement
inline bool os::registry::Init()
{
return false;
}
// TODO: read from file?
template<typename T>
bool os::registry::ReadValue(const std::string_view& name, T& data)
{
return false;
}
// TODO: write to file?
template<typename T>
bool os::registry::WriteValue(const std::string_view& name, const T& data)
{
return false;
}
+2
View File
@@ -4,5 +4,7 @@ namespace os::process
{ {
std::filesystem::path GetExecutablePath(); std::filesystem::path GetExecutablePath();
std::filesystem::path GetWorkingDirectory(); std::filesystem::path GetWorkingDirectory();
bool SetWorkingDirectory(const std::filesystem::path& path);
bool StartProcess(const std::filesystem::path& path, const std::vector<std::string>& args, std::filesystem::path work = {}); bool StartProcess(const std::filesystem::path& path, const std::vector<std::string>& args, std::filesystem::path work = {});
void ShowConsole();
} }
+18
View File
@@ -0,0 +1,18 @@
#pragma once
namespace os::registry
{
bool Init();
template<typename T>
bool ReadValue(const std::string_view& name, T& data);
template<typename T>
bool WriteValue(const std::string_view& name, const T& data);
}
#if _WIN32
#include <os/win32/registry_win32.inl>
#elif defined(__linux__)
#include <os/linux/registry_linux.inl>
#endif
@@ -20,6 +20,11 @@ std::filesystem::path os::process::GetWorkingDirectory()
return std::filesystem::path(workPath); return std::filesystem::path(workPath);
} }
bool os::process::SetWorkingDirectory(const std::filesystem::path& path)
{
return SetCurrentDirectoryW(path.c_str());
}
bool os::process::StartProcess(const std::filesystem::path& path, const std::vector<std::string>& args, std::filesystem::path work) bool os::process::StartProcess(const std::filesystem::path& path, const std::vector<std::string>& args, std::filesystem::path work)
{ {
if (path.empty()) if (path.empty())
@@ -46,3 +51,14 @@ bool os::process::StartProcess(const std::filesystem::path& path, const std::vec
return true; return true;
} }
void os::process::ShowConsole()
{
if (GetConsoleWindow() == nullptr)
{
AllocConsole();
freopen("CONIN$", "r", stdin);
freopen("CONOUT$", "w", stderr);
freopen("CONOUT$", "w", stdout);
}
}
+153
View File
@@ -0,0 +1,153 @@
#include <os/registry.h>
#include <unordered_map>
inline const wchar_t* g_registryRoot = L"Software\\UnleashedRecomp";
inline bool os::registry::Init()
{
return true;
}
template<typename T>
bool os::registry::ReadValue(const std::string_view& name, T& data)
{
HKEY hKey;
if (RegOpenKeyExW(HKEY_CURRENT_USER, g_registryRoot, 0, KEY_READ, &hKey) != ERROR_SUCCESS)
return false;
wchar_t wideName[128];
int wideNameSize = MultiByteToWideChar(CP_UTF8, 0, name.data(), name.size(), wideName, sizeof(wideName));
if (wideNameSize == 0)
{
return false;
}
wideName[wideNameSize] = 0;
DWORD bufferSize = 0;
DWORD dataType = 0;
auto result = RegGetValueW(hKey, nullptr, wideName, RRF_RT_ANY, &dataType, nullptr, &bufferSize);
if (result != ERROR_SUCCESS)
{
RegCloseKey(hKey);
return false;
}
result = ERROR_INVALID_FUNCTION;
if constexpr (std::is_same_v<T, std::string>)
{
if (dataType == REG_SZ)
{
std::vector<uint8_t> buffer{};
buffer.reserve(bufferSize);
result = RegGetValueW(hKey, nullptr, wideName, RRF_RT_REG_SZ, nullptr, buffer.data(), &bufferSize);
if (result == ERROR_SUCCESS)
{
int valueSize = WideCharToMultiByte(CP_UTF8, 0, (wchar_t*)buffer.data(), (bufferSize / sizeof(wchar_t)) - 1, nullptr, 0, nullptr, nullptr);
data.resize(valueSize);
WideCharToMultiByte(CP_UTF8, 0, (wchar_t*)buffer.data(), (bufferSize / sizeof(wchar_t)) - 1, data.data(), valueSize, nullptr, nullptr);
}
}
}
else if constexpr (std::is_same_v<T, std::filesystem::path>)
{
if (dataType == REG_SZ)
{
std::vector<uint8_t> buffer{};
buffer.reserve(bufferSize);
result = RegGetValueW(hKey, nullptr, wideName, RRF_RT_REG_SZ, nullptr, buffer.data(), &bufferSize);
if (result == ERROR_SUCCESS)
{
data = reinterpret_cast<wchar_t*>(buffer.data());
}
}
}
else if constexpr (std::is_same_v<T, uint32_t>)
{
result = RegGetValueW(hKey, nullptr, wideName, RRF_RT_DWORD, nullptr, (BYTE*)&data, &bufferSize);
}
else if constexpr (std::is_same_v<T, uint64_t>)
{
result = RegGetValueW(hKey, nullptr, wideName, RRF_RT_QWORD, nullptr, (BYTE*)&data, &bufferSize);
}
else
{
static_assert(false, "Unsupported data type.");
}
RegCloseKey(hKey);
return result == ERROR_SUCCESS;
}
template<typename T>
bool os::registry::WriteValue(const std::string_view& name, const T& data)
{
HKEY hKey;
if (RegCreateKeyExW(HKEY_CURRENT_USER, g_registryRoot, 0, nullptr, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL) != ERROR_SUCCESS)
return false;
BYTE* pData = nullptr;
DWORD dataSize = 0;
DWORD dataType = 0;
bool wideString = false;
if constexpr (std::is_same_v<T, std::string>)
{
pData = (BYTE*)data.c_str();
dataSize = data.size() + 1;
dataType = REG_SZ;
}
else if constexpr (std::is_same_v<T, uint32_t>)
{
pData = &data;
dataSize = sizeof(T);
dataType = REG_DWORD;
}
else if constexpr (std::is_same_v<T, uint64_t>)
{
pData = &data;
dataSize = sizeof(T);
dataType = REG_QWORD;
}
else if constexpr (std::is_same_v<T, std::filesystem::path>)
{
pData = (BYTE*)data.c_str();
dataSize = (wcslen((const wchar_t*)pData) + 1) * sizeof(wchar_t);
dataType = REG_SZ;
wideString = true;
}
else
{
static_assert(false, "Unsupported data type.");
}
LSTATUS result = ERROR_INVALID_FUNCTION;
if (wideString)
{
wchar_t wideName[128];
int wideNameSize = MultiByteToWideChar(CP_UTF8, 0, name.data(), name.size(), wideName, sizeof(wideName));
if (wideNameSize == 0)
{
return false;
}
wideName[wideNameSize] = 0;
result = RegSetValueExW(hKey, wideName, 0, dataType, pData, dataSize);
}
else
{
result = RegSetValueExA(hKey, name.data(), 0, dataType, pData, dataSize);
}
RegCloseKey(hKey);
if (result != ERROR_SUCCESS)
return false;
return true;
}
@@ -1,5 +1,5 @@
#include <api/SWA.h> #include <api/SWA.h>
#include <patches/ui/CTitleStateIntro_patches.h> #include <patches/CTitleStateIntro_patches.h>
// SWA::CGameModeStageTitle::Update // SWA::CGameModeStageTitle::Update
PPC_FUNC_IMPL(__imp__sub_825518B8); PPC_FUNC_IMPL(__imp__sub_825518B8);
@@ -135,7 +135,6 @@ PPC_FUNC(sub_824B0930)
} }
} }
// TODO: disable Start button closing menu.
if (AchievementMenu::s_isVisible) if (AchievementMenu::s_isVisible)
{ {
// HACK: wait for transition to finish before restoring control. // HACK: wait for transition to finish before restoring control.
@@ -154,7 +153,6 @@ PPC_FUNC(sub_824B0930)
if (OptionsMenu::CanClose() && pInputState->GetPadState().IsTapped(SWA::eKeyState_B)) if (OptionsMenu::CanClose() && pInputState->GetPadState().IsTapped(SWA::eKeyState_B))
{ {
OptionsMenu::Close(); OptionsMenu::Close();
GuestToHostFunction<int>(sub_824AFD28, pHudPause, 0, 0, 0, 1); GuestToHostFunction<int>(sub_824AFD28, pHudPause, 0, 0, 0, 1);
__imp__sub_824B0930(ctx, base); __imp__sub_824B0930(ctx, base);
} }
@@ -163,9 +161,7 @@ PPC_FUNC(sub_824B0930)
{ {
g_achievementMenuIntroTime = 0; g_achievementMenuIntroTime = 0;
const auto ms_IsRenderHud = (bool*)g_memory.Translate(0x8328BB26); if (*SWA::SGlobals::ms_IsRenderHud && pHudPause->m_IsShown && !pHudPause->m_Submenu && pHudPause->m_Transition == SWA::eTransitionType_Undefined)
if (*ms_IsRenderHud && pHudPause->m_IsShown && pHudPause->m_Transition == SWA::eTransitionType_Undefined)
{ {
ButtonGuide::Open(Button(Localise("Achievements_Name"), EButtonIcon::Back, EButtonAlignment::Left, EFontQuality::Low)); ButtonGuide::Open(Button(Localise("Achievements_Name"), EButtonIcon::Back, EButtonAlignment::Left, EFontQuality::Low));
g_isClosed = false; g_isClosed = false;
@@ -0,0 +1,168 @@
#include "CTitleStateIntro_patches.h"
#include <api/SWA.h>
#include <install/update_checker.h>
#include <locale/locale.h>
#include <ui/fader.h>
#include <ui/message_window.h>
#include <user/achievement_manager.h>
#include <user/paths.h>
#include <app.h>
static std::atomic<bool> g_faderBegun = false;
bool g_quitMessageOpen = false;
static int g_quitMessageResult = -1;
static std::atomic<bool> g_corruptSaveMessageOpen = false;
static int g_corruptSaveMessageResult = -1;
static std::atomic<bool> g_corruptAchievementsMessageOpen = false;
static int g_corruptAchievementsMessageResult = -1;
static std::atomic<bool> g_updateAvailableMessageOpen = false;
static int g_updateAvailableMessageResult = -1;
static bool ProcessQuitMessage()
{
if (!g_quitMessageOpen)
return false;
std::array<std::string, 2> options = { Localise("Common_Yes"), Localise("Common_No") };
if (MessageWindow::Open(Localise("Title_Message_Quit"), &g_quitMessageResult, options, 1) == MSG_CLOSED)
{
if (!g_quitMessageResult)
{
Fader::FadeOut(1, []() { App::Exit(); });
g_faderBegun = true;
}
g_quitMessageOpen = false;
g_quitMessageResult = -1;
}
return true;
}
static bool ProcessCorruptSaveMessage()
{
if (!g_corruptSaveMessageOpen)
return false;
if (MessageWindow::Open(Localise("Title_Message_SaveDataCorrupt"), &g_corruptSaveMessageResult) == MSG_CLOSED)
{
g_corruptSaveMessageOpen = false;
g_corruptSaveMessageOpen.notify_one();
g_corruptSaveMessageResult = -1;
}
return true;
}
static bool ProcessCorruptAchievementsMessage()
{
if (!g_corruptAchievementsMessageOpen)
return false;
auto message = AchievementManager::Status == EAchStatus::IOError
? Localise("Title_Message_AchievementDataIOError")
: Localise("Title_Message_AchievementDataCorrupt");
if (MessageWindow::Open(message, &g_corruptAchievementsMessageResult) == MSG_CLOSED)
{
// Allow user to proceed if the achievement data couldn't be loaded.
// Restarting may fix this error, so it isn't worth clearing the data for.
if (AchievementManager::Status != EAchStatus::IOError)
AchievementManager::Save(true);
g_corruptAchievementsMessageOpen = false;
g_corruptAchievementsMessageOpen.notify_one();
g_corruptAchievementsMessageResult = -1;
}
return true;
}
static bool ProcessUpdateAvailableMessage()
{
if (!g_updateAvailableMessageOpen)
return false;
std::array<std::string, 2> options = { Localise("Common_Yes"), Localise("Common_No") };
if (MessageWindow::Open(Localise("Title_Message_UpdateAvailable"), &g_updateAvailableMessageResult, options) == MSG_CLOSED)
{
if (!g_updateAvailableMessageResult)
{
Fader::FadeOut(1,
//
[]()
{
UpdateChecker::visitWebsite();
App::Exit();
}
);
g_faderBegun = true;
}
g_updateAvailableMessageOpen = false;
g_updateAvailableMessageOpen.notify_one();
g_updateAvailableMessageResult = -1;
}
return true;
}
void StorageDevicePromptMidAsmHook() {}
// Save data validation hook.
PPC_FUNC_IMPL(__imp__sub_822C55B0);
PPC_FUNC(sub_822C55B0)
{
App::s_isSaveDataCorrupt = true;
g_corruptSaveMessageOpen = true;
g_corruptSaveMessageOpen.wait(true);
ctx.r3.u32 = 0;
}
void PressStartSaveLoadThreadMidAsmHook()
{
if (UpdateChecker::check() == UpdateChecker::Result::UpdateAvailable)
{
g_updateAvailableMessageOpen = true;
g_updateAvailableMessageOpen.wait(true);
g_faderBegun.wait(true);
}
AchievementManager::Load();
if (AchievementManager::Status != EAchStatus::Success)
{
g_corruptAchievementsMessageOpen = true;
g_corruptAchievementsMessageOpen.wait(true);
}
}
// SWA::CTitleStateIntro::Update
PPC_FUNC_IMPL(__imp__sub_82587E50);
PPC_FUNC(sub_82587E50)
{
auto isAutoSaveWarningShown = *(bool*)g_memory.Translate(0x83367BC1);
if (isAutoSaveWarningShown)
{
__imp__sub_82587E50(ctx, base);
}
else if (!ProcessUpdateAvailableMessage() && !ProcessCorruptSaveMessage() && !ProcessCorruptAchievementsMessage() && !g_faderBegun)
{
if (auto pInputState = SWA::CInputState::GetInstance())
{
if (pInputState->GetPadState().IsTapped(SWA::eKeyState_B))
g_quitMessageOpen = true;
}
if (!ProcessQuitMessage())
__imp__sub_82587E50(ctx, base);
}
}
+274 -15
View File
@@ -335,6 +335,10 @@ enum
OFFSET_SCALE_LEFT = 1 << 13, OFFSET_SCALE_LEFT = 1 << 13,
OFFSET_SCALE_RIGHT = 1 << 14, OFFSET_SCALE_RIGHT = 1 << 14,
REPEAT_LEFT = 1 << 15,
TORNADO_DEFENSE = 1 << 16,
}; };
struct CsdModifier struct CsdModifier
@@ -364,6 +368,9 @@ static const ankerl::unordered_dense::map<XXH64_hash_t, CsdModifier> g_modifiers
{ HashStr("ui_exstage/energy/R_gauge_effect"), { 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/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 } },
{ 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_num"), { ALIGN_RIGHT | SCALE } }, { HashStr("ui_exstage/hit/hit_counter_num"), { ALIGN_RIGHT | SCALE } },
// ui_gate // ui_gate
@@ -389,6 +396,18 @@ static const ankerl::unordered_dense::map<XXH64_hash_t, CsdModifier> g_modifiers
// ui_loading // ui_loading
{ HashStr("ui_loading/bg_1"), { STRETCH } }, { HashStr("ui_loading/bg_1"), { STRETCH } },
{ HashStr("ui_loading/bg_2"), { STRETCH } }, { HashStr("ui_loading/bg_2"), { STRETCH } },
{ 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 } },
// ui_mediaroom
{ HashStr("ui_mediaroom/header/bg/img_1"), { EXTEND_LEFT } },
{ HashStr("ui_mediaroom/header/bg/img_10"), { EXTEND_RIGHT } },
{ HashStr("ui_mediaroom/header/frame/img_1"), { EXTEND_LEFT } },
{ HashStr("ui_mediaroom/header/frame/img_5"), { EXTEND_RIGHT } },
// ui_missionscreen // ui_missionscreen
{ HashStr("ui_missionscreen/player_count"), { ALIGN_TOP_LEFT | SCALE } }, { HashStr("ui_missionscreen/player_count"), { ALIGN_TOP_LEFT | SCALE } },
@@ -419,27 +438,67 @@ static const ankerl::unordered_dense::map<XXH64_hash_t, CsdModifier> g_modifiers
// ui_playscreen // ui_playscreen
{ HashStr("ui_playscreen/player_count"), { ALIGN_TOP_LEFT | SCALE } }, { HashStr("ui_playscreen/player_count"), { ALIGN_TOP_LEFT | SCALE } },
{ HashStr("ui_playscreen/time_count"), { ALIGN_TOP_LEFT | SCALE } }, { HashStr("ui_playscreen/time_count"), { ALIGN_TOP_LEFT | SCALE } },
{ HashStr("ui_playscreen/time_count/position_s/bg_1"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_playscreen/time_count/position_s/bg_1/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_playscreen/time_count/position_s/bg_2"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_playscreen/time_count/position_s/bg_2/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_playscreen/score_count"), { ALIGN_TOP_LEFT | SCALE } }, { HashStr("ui_playscreen/score_count"), { ALIGN_TOP_LEFT | SCALE } },
{ HashStr("ui_playscreen/score_count/position/bg_1"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_playscreen/score_count/position/bg_1/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_playscreen/score_count/position/bg_2"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_playscreen/score_count/position/bg_2/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_playscreen/exp_count"), { ALIGN_TOP_LEFT | SCALE } }, { HashStr("ui_playscreen/exp_count"), { ALIGN_TOP_LEFT | SCALE } },
{ HashStr("ui_playscreen/exp_count/position/bg_1"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_playscreen/exp_count/position/bg_1/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_playscreen/exp_count/position/bg_2"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_playscreen/exp_count/position/bg_2/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_playscreen/so_speed_gauge"), { ALIGN_BOTTOM_LEFT | SCALE } }, { HashStr("ui_playscreen/so_speed_gauge"), { ALIGN_BOTTOM_LEFT | SCALE } },
{ HashStr("ui_playscreen/so_ringenagy_gauge"), { ALIGN_BOTTOM_LEFT | SCALE } }, { HashStr("ui_playscreen/so_ringenagy_gauge"), { ALIGN_BOTTOM_LEFT | SCALE } },
{ HashStr("ui_playscreen/gauge_frame"), { ALIGN_BOTTOM_LEFT | SCALE } }, { HashStr("ui_playscreen/gauge_frame"), { ALIGN_BOTTOM_LEFT | SCALE } },
{ HashStr("ui_playscreen/ring_count"), { ALIGN_BOTTOM_LEFT | SCALE } }, { HashStr("ui_playscreen/ring_count"), { ALIGN_BOTTOM_LEFT | SCALE } },
{ HashStr("ui_playscreen/ring_get"), { ALIGN_BOTTOM_LEFT | SCALE } }, { HashStr("ui_playscreen/ring_get"), { ALIGN_BOTTOM_LEFT | SCALE } },
{ HashStr("ui_playscreen/add/speed_count"), { ALIGN_RIGHT | SCALE } }, { HashStr("ui_playscreen/add/speed_count"), { ALIGN_RIGHT | SCALE } },
{ HashStr("ui_playscreen/add/speed_count/position/bar"), { ALIGN_RIGHT | SCALE | EXTEND_RIGHT } },
{ HashStr("ui_playscreen/add/speed_count/position/bar/R"), { SKIP } },
{ HashStr("ui_playscreen/add/speed_count/position/bar_pale"), { ALIGN_RIGHT | SCALE | EXTEND_RIGHT } },
{ HashStr("ui_playscreen/add/speed_count/position/bar_pale/R"), { SKIP } },
{ HashStr("ui_playscreen/add/u_info"), { ALIGN_BOTTOM_RIGHT | SCALE } }, { HashStr("ui_playscreen/add/u_info"), { ALIGN_BOTTOM_RIGHT | SCALE } },
{ HashStr("ui_playscreen/add/u_info/position/bar"), { ALIGN_BOTTOM_RIGHT | SCALE | EXTEND_RIGHT } },
{ HashStr("ui_playscreen/add/u_info/position/bar/R"), { SKIP } },
{ HashStr("ui_playscreen/add/medal_get_s"), { ALIGN_BOTTOM_RIGHT | SCALE } }, { HashStr("ui_playscreen/add/medal_get_s"), { ALIGN_BOTTOM_RIGHT | SCALE } },
{ HashStr("ui_playscreen/add/medal_get_s/position/bar"), { ALIGN_BOTTOM_RIGHT | SCALE | EXTEND_RIGHT } },
{ HashStr("ui_playscreen/add/medal_get_s/position/bar/R"), { SKIP } },
{ HashStr("ui_playscreen/add/medal_get_m"), { ALIGN_BOTTOM_RIGHT | SCALE } }, { HashStr("ui_playscreen/add/medal_get_m"), { ALIGN_BOTTOM_RIGHT | SCALE } },
{ HashStr("ui_playscreen/add/medal_get_m/position/bar"), { ALIGN_BOTTOM_RIGHT | SCALE | EXTEND_RIGHT } },
{ HashStr("ui_playscreen/add/medal_get_m/position/bar/R"), { SKIP } },
// ui_playscreen_ev // ui_playscreen_ev
{ HashStr("ui_playscreen_ev/player_count"), { ALIGN_TOP_LEFT | SCALE } }, { HashStr("ui_playscreen_ev/player_count"), { ALIGN_TOP_LEFT | SCALE } },
{ HashStr("ui_playscreen_ev/score_count"), { ALIGN_TOP_LEFT | SCALE } }, { HashStr("ui_playscreen_ev/score_count"), { ALIGN_TOP_LEFT | SCALE } },
{ HashStr("ui_playscreen_ev/score_count/position/bg_1"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_playscreen_ev/score_count/position/bg_1/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_playscreen_ev/score_count/position/bg_2"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_playscreen_ev/score_count/position/bg_2/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_playscreen_ev/ring_count"), { ALIGN_TOP_LEFT | SCALE } }, { HashStr("ui_playscreen_ev/ring_count"), { ALIGN_TOP_LEFT | SCALE } },
{ HashStr("ui_playscreen_ev/ring_count/position/bg_1"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_playscreen_ev/ring_count/position/bg_1/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_playscreen_ev/ring_count/position/bg_2"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_playscreen_ev/ring_count/position/bg_2/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_playscreen_ev/ring_get"), { ALIGN_TOP_LEFT | SCALE } }, { HashStr("ui_playscreen_ev/ring_get"), { ALIGN_TOP_LEFT | SCALE } },
{ HashStr("ui_playscreen_ev/exp_count"), { ALIGN_TOP_LEFT | SCALE } }, { HashStr("ui_playscreen_ev/exp_count"), { ALIGN_TOP_LEFT | SCALE } },
{ HashStr("ui_playscreen_ev/exp_count/position/bg_1"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_playscreen_ev/exp_count/position/bg_1/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_playscreen_ev/exp_count/position/bg_2"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_playscreen_ev/exp_count/position/bg_2/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
{ HashStr("ui_playscreen_ev/add/u_info"), { ALIGN_BOTTOM_RIGHT | SCALE } }, { HashStr("ui_playscreen_ev/add/u_info"), { ALIGN_BOTTOM_RIGHT | SCALE } },
{ HashStr("ui_playscreen_ev/add/u_info/position/bar"), { ALIGN_BOTTOM_RIGHT | SCALE | EXTEND_RIGHT } },
{ HashStr("ui_playscreen_ev/add/u_info/position/bar/R"), { SKIP } },
{ HashStr("ui_playscreen_ev/add/medal_get_s"), { ALIGN_BOTTOM_RIGHT | SCALE } }, { HashStr("ui_playscreen_ev/add/medal_get_s"), { ALIGN_BOTTOM_RIGHT | SCALE } },
{ HashStr("ui_playscreen_ev/add/medal_get_s/position/bar"), { ALIGN_BOTTOM_RIGHT | SCALE | EXTEND_RIGHT } },
{ HashStr("ui_playscreen_ev/add/medal_get_s/position/bar/R"), { SKIP } },
{ HashStr("ui_playscreen_ev/add/medal_get_m"), { ALIGN_BOTTOM_RIGHT | SCALE } }, { HashStr("ui_playscreen_ev/add/medal_get_m"), { ALIGN_BOTTOM_RIGHT | SCALE } },
{ HashStr("ui_playscreen_ev/add/medal_get_m/position/bar"), { ALIGN_BOTTOM_RIGHT | SCALE | EXTEND_RIGHT } },
{ HashStr("ui_playscreen_ev/add/medal_get_m/position/bar/R"), { SKIP } },
{ HashStr("ui_playscreen_ev/gauge/unleash_bg"), { ALIGN_BOTTOM_LEFT | SCALE } }, { HashStr("ui_playscreen_ev/gauge/unleash_bg"), { ALIGN_BOTTOM_LEFT | SCALE } },
{ HashStr("ui_playscreen_ev/gauge/life_bg"), { ALIGN_BOTTOM_LEFT | SCALE } }, { HashStr("ui_playscreen_ev/gauge/life_bg"), { ALIGN_BOTTOM_LEFT | SCALE } },
{ HashStr("ui_playscreen_ev/gauge/unleash_body"), { ALIGN_BOTTOM_LEFT | SCALE } }, { HashStr("ui_playscreen_ev/gauge/unleash_body"), { ALIGN_BOTTOM_LEFT | SCALE } },
@@ -468,6 +527,9 @@ static const ankerl::unordered_dense::map<XXH64_hash_t, CsdModifier> g_modifiers
// ui_playscreen_ev_hit // ui_playscreen_ev_hit
{ HashStr("ui_playscreen_ev_hit/hit_counter_bg"), { ALIGN_RIGHT | SCALE } }, { HashStr("ui_playscreen_ev_hit/hit_counter_bg"), { ALIGN_RIGHT | SCALE } },
{ HashStr("ui_playscreen_ev_hit/hit_counter_bg/C"), { ALIGN_RIGHT | SCALE | EXTEND_RIGHT } },
{ HashStr("ui_playscreen_ev_hit/hit_counter_bg/C/L"), { ALIGN_RIGHT | SCALE } },
{ HashStr("ui_playscreen_ev_hit/hit_counter_bg/C/R"), { SKIP } },
{ HashStr("ui_playscreen_ev_hit/hit_counter_num"), { ALIGN_RIGHT | SCALE } }, { HashStr("ui_playscreen_ev_hit/hit_counter_num"), { ALIGN_RIGHT | SCALE } },
{ HashStr("ui_playscreen_ev_hit/hit_counter_txt_1"), { ALIGN_RIGHT | SCALE } }, { HashStr("ui_playscreen_ev_hit/hit_counter_txt_1"), { ALIGN_RIGHT | SCALE } },
{ HashStr("ui_playscreen_ev_hit/hit_counter_txt_2"), { ALIGN_RIGHT | SCALE } }, { HashStr("ui_playscreen_ev_hit/hit_counter_txt_2"), { ALIGN_RIGHT | SCALE } },
@@ -479,12 +541,20 @@ static const ankerl::unordered_dense::map<XXH64_hash_t, CsdModifier> g_modifiers
{ HashStr("ui_playscreen_su/footer"), { ALIGN_BOTTOM_RIGHT | SCALE } }, { HashStr("ui_playscreen_su/footer"), { ALIGN_BOTTOM_RIGHT | SCALE } },
// ui_prov_playscreen // ui_prov_playscreen
{ HashStr("ui_prov_playscreen/so_speed_gauge"), { ALIGN_BOTTOM_LEFT | SCALE } }, { HashStr("ui_prov_playscreen/so_speed_gauge"), { ALIGN_BOTTOM_LEFT | SCALE | TORNADO_DEFENSE } },
{ HashStr("ui_prov_playscreen/so_ringenagy_gauge"), { ALIGN_BOTTOM_LEFT | SCALE } }, { HashStr("ui_prov_playscreen/so_ringenagy_gauge"), { ALIGN_BOTTOM_LEFT | SCALE | TORNADO_DEFENSE } },
{ HashStr("ui_prov_playscreen/bg"), { ALIGN_TOP_LEFT | SCALE } }, { HashStr("ui_prov_playscreen/bg"), { ALIGN_TOP_LEFT | SCALE | TORNADO_DEFENSE } },
{ HashStr("ui_prov_playscreen/info_1"), { ALIGN_TOP_LEFT | SCALE } }, { HashStr("ui_prov_playscreen/info_1"), { ALIGN_TOP_LEFT | SCALE | TORNADO_DEFENSE } },
{ HashStr("ui_prov_playscreen/info_2"), { ALIGN_TOP_LEFT | SCALE } }, { HashStr("ui_prov_playscreen/info_1/position/bg_1"), { ALIGN_TOP_LEFT | SCALE | TORNADO_DEFENSE | EXTEND_LEFT } },
{ HashStr("ui_prov_playscreen/ring_get_effect"), { ALIGN_BOTTOM_LEFT | SCALE } }, { HashStr("ui_prov_playscreen/info_1/position/bg_1/C_h"), { ALIGN_TOP_LEFT | SCALE | TORNADO_DEFENSE | EXTEND_LEFT } },
{ HashStr("ui_prov_playscreen/info_1/position/bg_2"), { ALIGN_TOP_LEFT | SCALE | TORNADO_DEFENSE | EXTEND_LEFT } },
{ HashStr("ui_prov_playscreen/info_1/position/bg_2/C_h"), { ALIGN_TOP_LEFT | SCALE | TORNADO_DEFENSE | EXTEND_LEFT } },
{ HashStr("ui_prov_playscreen/info_2"), { ALIGN_TOP_LEFT | SCALE | TORNADO_DEFENSE } },
{ HashStr("ui_prov_playscreen/info_2/position/bg_1"), { ALIGN_TOP_LEFT | SCALE | TORNADO_DEFENSE | EXTEND_LEFT } },
{ HashStr("ui_prov_playscreen/info_2/position/bg_1/C_h"), { ALIGN_TOP_LEFT | SCALE | TORNADO_DEFENSE | EXTEND_LEFT } },
{ HashStr("ui_prov_playscreen/info_2/position/bg_2"), { ALIGN_TOP_LEFT | SCALE | TORNADO_DEFENSE | EXTEND_LEFT } },
{ HashStr("ui_prov_playscreen/info_2/position/bg_2/C_h"), { ALIGN_TOP_LEFT | SCALE | TORNADO_DEFENSE | EXTEND_LEFT } },
{ HashStr("ui_prov_playscreen/ring_get_effect"), { ALIGN_BOTTOM_LEFT | SCALE | TORNADO_DEFENSE } },
// ui_result // ui_result
{ HashStr("ui_result/footer/result_footer"), { ALIGN_BOTTOM } }, { HashStr("ui_result/footer/result_footer"), { ALIGN_BOTTOM } },
@@ -592,6 +662,8 @@ static const ankerl::unordered_dense::map<XXH64_hash_t, CsdModifier> g_modifiers
{ HashStr("ui_status/header/status_title/title_bg/right"), { ALIGN_TOP | STORE_RIGHT_CORNER } }, { HashStr("ui_status/header/status_title/title_bg/right"), { ALIGN_TOP | STORE_RIGHT_CORNER } },
{ HashStr("ui_status/logo/logo/bg_position/c_1"), { STRETCH_HORIZONTAL } }, { HashStr("ui_status/logo/logo/bg_position/c_1"), { STRETCH_HORIZONTAL } },
{ HashStr("ui_status/logo/logo/bg_position/c_2"), { STRETCH_HORIZONTAL } }, { HashStr("ui_status/logo/logo/bg_position/c_2"), { STRETCH_HORIZONTAL } },
{ HashStr("ui_status/main/arrow_effect/a_efc_1"), { OFFSET_SCALE_LEFT, 866.0f } },
{ HashStr("ui_status/main/arrow_effect/a_efc_1/position/img_01"), { STORE_RIGHT_CORNER } },
{ HashStr("ui_status/main/progless/bg/prgs_bg_1"), { OFFSET_SCALE_LEFT, 714.0f } }, { HashStr("ui_status/main/progless/bg/prgs_bg_1"), { OFFSET_SCALE_LEFT, 714.0f } },
{ HashStr("ui_status/main/progless/bg/prgs_bg_1/position/center/right"), { STORE_RIGHT_CORNER } }, { HashStr("ui_status/main/progless/bg/prgs_bg_1/position/center/right"), { STORE_RIGHT_CORNER } },
{ HashStr("ui_status/main/progless/prgs/prgs_bar_1"), { OFFSET_SCALE_LEFT, 586.0f } }, { HashStr("ui_status/main/progless/prgs/prgs_bar_1"), { OFFSET_SCALE_LEFT, 586.0f } },
@@ -623,6 +695,22 @@ static const ankerl::unordered_dense::map<XXH64_hash_t, CsdModifier> g_modifiers
{ HashStr("ui_worldmap/contents/choices/cts_choices_bg"), { STRETCH } }, { HashStr("ui_worldmap/contents/choices/cts_choices_bg"), { STRETCH } },
{ HashStr("ui_worldmap/contents/info/bg/cts_info_bg"), { ALIGN_TOP_LEFT | WORLD_MAP } }, { HashStr("ui_worldmap/contents/info/bg/cts_info_bg"), { ALIGN_TOP_LEFT | WORLD_MAP } },
{ HashStr("ui_worldmap/contents/info/bg/info_bg_1"), { ALIGN_TOP_LEFT | WORLD_MAP } }, { HashStr("ui_worldmap/contents/info/bg/info_bg_1"), { ALIGN_TOP_LEFT | WORLD_MAP } },
{ HashStr("ui_worldmap/contents/info/bg/info_bg_1/position_01/row_01/img_12"), { ALIGN_TOP_LEFT | WORLD_MAP | REPEAT_LEFT } },
{ HashStr("ui_worldmap/contents/info/bg/info_bg_1/position_01/row_02/img_12"), { ALIGN_TOP_LEFT | WORLD_MAP | REPEAT_LEFT } },
{ HashStr("ui_worldmap/contents/info/bg/info_bg_1/position_01/row_03/img_12"), { ALIGN_TOP_LEFT | WORLD_MAP | REPEAT_LEFT } },
{ HashStr("ui_worldmap/contents/info/bg/info_bg_1/position_01/row_04/img_12"), { ALIGN_TOP_LEFT | WORLD_MAP | REPEAT_LEFT } },
{ HashStr("ui_worldmap/contents/info/bg/info_bg_1/position_02/row_01/img_12"), { ALIGN_TOP_LEFT | WORLD_MAP | REPEAT_LEFT } },
{ HashStr("ui_worldmap/contents/info/bg/info_bg_1/position_02/row_02/img_12"), { ALIGN_TOP_LEFT | WORLD_MAP | REPEAT_LEFT } },
{ HashStr("ui_worldmap/contents/info/bg/info_bg_1/position_02/row_03/img_12"), { ALIGN_TOP_LEFT | WORLD_MAP | REPEAT_LEFT } },
{ HashStr("ui_worldmap/contents/info/bg/info_bg_1/position_02/row_04/img_12"), { ALIGN_TOP_LEFT | WORLD_MAP | REPEAT_LEFT } },
{ HashStr("ui_worldmap/contents/info/bg/info_bg_1/position_03/row_01/img_12"), { ALIGN_TOP_LEFT | WORLD_MAP | REPEAT_LEFT } },
{ HashStr("ui_worldmap/contents/info/bg/info_bg_1/position_03/row_02/img_12"), { ALIGN_TOP_LEFT | WORLD_MAP | REPEAT_LEFT } },
{ HashStr("ui_worldmap/contents/info/bg/info_bg_1/position_03/row_03/img_12"), { ALIGN_TOP_LEFT | WORLD_MAP | REPEAT_LEFT } },
{ HashStr("ui_worldmap/contents/info/bg/info_bg_1/position_03/row_04/img_12"), { ALIGN_TOP_LEFT | WORLD_MAP | REPEAT_LEFT } },
{ HashStr("ui_worldmap/contents/info/bg/info_bg_1/position_04/row_01/img_12"), { ALIGN_TOP_LEFT | WORLD_MAP | REPEAT_LEFT } },
{ HashStr("ui_worldmap/contents/info/bg/info_bg_1/position_04/row_02/img_12"), { ALIGN_TOP_LEFT | WORLD_MAP | REPEAT_LEFT } },
{ HashStr("ui_worldmap/contents/info/bg/info_bg_1/position_04/row_03/img_12"), { ALIGN_TOP_LEFT | WORLD_MAP | REPEAT_LEFT } },
{ HashStr("ui_worldmap/contents/info/bg/info_bg_1/position_04/row_04/img_12"), { ALIGN_TOP_LEFT | WORLD_MAP | REPEAT_LEFT } },
{ HashStr("ui_worldmap/contents/info/img/info_img_1"), { ALIGN_TOP_LEFT | WORLD_MAP } }, { HashStr("ui_worldmap/contents/info/img/info_img_1"), { ALIGN_TOP_LEFT | WORLD_MAP } },
{ HashStr("ui_worldmap/contents/info/img/info_img_2"), { ALIGN_TOP_LEFT | WORLD_MAP } }, { HashStr("ui_worldmap/contents/info/img/info_img_2"), { ALIGN_TOP_LEFT | WORLD_MAP } },
{ HashStr("ui_worldmap/contents/info/img/info_img_3"), { ALIGN_TOP_LEFT | WORLD_MAP } }, { HashStr("ui_worldmap/contents/info/img/info_img_3"), { ALIGN_TOP_LEFT | WORLD_MAP } },
@@ -630,7 +718,12 @@ static const ankerl::unordered_dense::map<XXH64_hash_t, CsdModifier> g_modifiers
{ HashStr("ui_worldmap/footer/worldmap_footer_bg"), { ALIGN_BOTTOM } }, { HashStr("ui_worldmap/footer/worldmap_footer_bg"), { ALIGN_BOTTOM } },
{ HashStr("ui_worldmap/footer/worldmap_footer_img_A"), { ALIGN_BOTTOM } }, { HashStr("ui_worldmap/footer/worldmap_footer_img_A"), { ALIGN_BOTTOM } },
{ HashStr("ui_worldmap/header/worldmap_header_bg"), { ALIGN_TOP } }, { HashStr("ui_worldmap/header/worldmap_header_bg"), { ALIGN_TOP } },
{ HashStr("ui_worldmap/header/worldmap_header_img"), { ALIGN_TOP_LEFT | WORLD_MAP } } { HashStr("ui_worldmap/header/worldmap_header_img"), { ALIGN_TOP_LEFT | WORLD_MAP } },
{ HashStr("ui_worldmap/header/worldmap_header_img/head_icon"), { SKIP } },
// ui_worldmap_help
{ HashStr("ui_worldmap_help/balloon/help_window/position/msg_bg_l"), { EXTEND_LEFT } },
{ HashStr("ui_worldmap_help/balloon/help_window/position/msg_bg_r"), { EXTEND_RIGHT } },
}; };
static std::optional<CsdModifier> FindModifier(uint32_t data) static std::optional<CsdModifier> FindModifier(uint32_t data)
@@ -659,13 +752,53 @@ static bool g_cornerExtract;
//#define CORNER_DEBUG //#define CORNER_DEBUG
// Explicit translations don't get affected by gameplay UI downscaling.
// This is required for the medal info in pause menu.
static float g_scenePositionX;
static float g_scenePositionY;
// Chao::CSD::CScene::Render
PPC_FUNC_IMPL(__imp__sub_830BC640);
PPC_FUNC(sub_830BC640)
{
g_scenePositionX = 0.0f;
g_scenePositionY = 0.0f;
uint32_t motionPattern = PPC_LOAD_U32(ctx.r3.u32 + 0x98);
if (motionPattern != NULL)
{
uint32_t member = PPC_LOAD_U32(motionPattern + 0xC);
if (member != NULL)
{
uint32_t x = PPC_LOAD_U32(member + 0x2C);
uint32_t y = PPC_LOAD_U32(member + 0x30);
g_scenePositionX = 1280.0f * reinterpret_cast<float&>(x);
g_scenePositionY = 720.0f * reinterpret_cast<float&>(y);
}
}
__imp__sub_830BC640(ctx, base);
}
// Chao::CSD::Scene::Render // Chao::CSD::Scene::Render
PPC_FUNC_IMPL(__imp__sub_830C6A00); PPC_FUNC_IMPL(__imp__sub_830C6A00);
PPC_FUNC(sub_830C6A00) PPC_FUNC(sub_830C6A00)
{ {
g_sceneModifier = FindModifier(ctx.r3.u32); g_sceneModifier = FindModifier(ctx.r3.u32);
if (g_sceneModifier.has_value() && (g_sceneModifier->flags & (OFFSET_SCALE_LEFT | OFFSET_SCALE_RIGHT)) != 0) if (g_sceneModifier.has_value())
{
// 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.
if ((g_sceneModifier->flags & TORNADO_DEFENSE) != 0)
{
g_scenePositionX = 0.0f;
g_scenePositionY = 0.0f;
}
if ((g_sceneModifier->flags & (OFFSET_SCALE_LEFT | OFFSET_SCALE_RIGHT)) != 0)
{ {
auto r3 = ctx.r3; auto r3 = ctx.r3;
auto r4 = ctx.r4; auto r4 = ctx.r4;
@@ -693,6 +826,7 @@ PPC_FUNC(sub_830C6A00)
ctx.r5 = r5; ctx.r5 = r5;
ctx.r6 = r6; ctx.r6 = r6;
} }
}
__imp__sub_830C6A00(ctx, base); __imp__sub_830C6A00(ctx, base);
} }
@@ -750,18 +884,28 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str
modifier.flags &= ~(ALIGN_LEFT | ALIGN_RIGHT); modifier.flags &= ~(ALIGN_LEFT | ALIGN_RIGHT);
} }
// Tornado Defense UI is squashed at 4:3, presumably to work around an explicit translation issue.
bool squash = Config::AspectRatio == EAspectRatio::OriginalNarrow && (modifier.flags & TORNADO_DEFENSE) != 0;
uint32_t size = ctx.r5.u32 * stride; uint32_t size = ctx.r5.u32 * stride;
ctx.r1.u32 -= size; ctx.r1.u32 -= size;
uint8_t* stack = base + ctx.r1.u32; uint8_t* stack = base + ctx.r1.u32;
memcpy(stack, base + ctx.r4.u32, size); memcpy(stack, base + ctx.r4.u32, size);
auto getPosition = [&](size_t index)
{
return reinterpret_cast<be<float>*>(stack + index * stride);
};
float offsetX = 0.0f; float offsetX = 0.0f;
float offsetY = 0.0f; float offsetY = 0.0f;
float pivotX = 0.0f;
float pivotY = 0.0f;
float scaleX = 1.0f; float scaleX = 1.0f;
float scaleY = 1.0f; float scaleY = 1.0f;
if ((modifier.flags & STRETCH_HORIZONTAL) != 0) if (squash || ((modifier.flags & STRETCH_HORIZONTAL) != 0 && g_aspectRatio >= WIDE_ASPECT_RATIO))
{ {
scaleX = Video::s_viewportWidth / 1280.0f; scaleX = Video::s_viewportWidth / 1280.0f;
} }
@@ -777,11 +921,14 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str
if ((modifier.flags & SCALE) != 0) if ((modifier.flags & SCALE) != 0)
{ {
scaleX *= g_aspectRatioGameplayScale; scaleX *= g_aspectRatioGameplayScale;
pivotX = g_scenePositionX;
if ((modifier.flags & ALIGN_RIGHT) != 0) if ((modifier.flags & ALIGN_RIGHT) != 0)
offsetX += 1280.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale; offsetX += 1280.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale;
else if ((modifier.flags & ALIGN_LEFT) == 0) else if ((modifier.flags & ALIGN_LEFT) == 0)
offsetX += 640.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale; offsetX += 640.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale;
offsetX += pivotX * g_aspectRatioScale;
} }
if ((modifier.flags & WORLD_MAP) != 0) if ((modifier.flags & WORLD_MAP) != 0)
@@ -791,7 +938,7 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str
} }
} }
if ((modifier.flags & STRETCH_VERTICAL) != 0) if (squash || ((modifier.flags & STRETCH_VERTICAL) != 0))
{ {
scaleY = Video::s_viewportHeight / 720.0f; scaleY = Video::s_viewportHeight / 720.0f;
} }
@@ -807,11 +954,14 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str
if ((modifier.flags & SCALE) != 0) if ((modifier.flags & SCALE) != 0)
{ {
scaleY *= g_aspectRatioGameplayScale; scaleY *= g_aspectRatioGameplayScale;
pivotY = g_scenePositionY;
if ((modifier.flags & ALIGN_BOTTOM) != 0) if ((modifier.flags & ALIGN_BOTTOM) != 0)
offsetY += 720.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale; offsetY += 720.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale;
else if ((modifier.flags & ALIGN_TOP) == 0) else if ((modifier.flags & ALIGN_TOP) == 0)
offsetY += 360.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale; offsetY += 360.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale;
offsetY += pivotY * g_aspectRatioScale;
} }
} }
@@ -825,7 +975,7 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str
offsetScaleModifier = g_castModifier.value(); offsetScaleModifier = g_castModifier.value();
uint32_t vertexIndex = ((offsetScaleModifier.flags & STORE_LEFT_CORNER) != 0) ? 0 : 3; uint32_t vertexIndex = ((offsetScaleModifier.flags & STORE_LEFT_CORNER) != 0) ? 0 : 3;
corner = *reinterpret_cast<be<float>*>(base + ctx.r4.u32 + vertexIndex * stride); corner = *getPosition(vertexIndex);
} }
if (offsetScaleModifier.cornerMax == 0.0f && g_castNodeModifier.has_value()) if (offsetScaleModifier.cornerMax == 0.0f && g_castNodeModifier.has_value())
@@ -853,10 +1003,10 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str
for (size_t i = 0; i < ctx.r5.u32; i++) for (size_t i = 0; i < ctx.r5.u32; i++)
{ {
auto position = reinterpret_cast<be<float>*>(stack + i * stride); auto position = getPosition(i);
float x = offsetX + position[0] * scaleX; float x = offsetX + (position[0] - pivotX) * scaleX;
float y = offsetY + position[1] * scaleY; float y = offsetY + (position[1] - pivotY) * scaleY;
if ((modifier.flags & EXTEND_LEFT) != 0 && (i == 0 || i == 1)) if ((modifier.flags & EXTEND_LEFT) != 0 && (i == 0 || i == 1))
{ {
@@ -871,10 +1021,39 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str
position[1] = round(y); position[1] = round(y);
} }
if ((modifier.flags & REPEAT_LEFT) != 0)
{
float width = *getPosition(2) - *getPosition(0);
auto r3 = ctx.r3;
auto r5 = ctx.r5;
auto r6 = ctx.r6;
auto r7 = ctx.r7;
auto r8 = ctx.r8;
while (*getPosition(2) > 0.0f)
{
ctx.r3 = r3;
ctx.r4 = ctx.r1;
ctx.r5 = r5;
ctx.r6 = r6;
ctx.r7 = r7;
ctx.r8 = r8;
original(ctx, base);
for (size_t i = 0; i < ctx.r5.u32; i++)
*getPosition(i) = *getPosition(i) - width;
}
ctx.r1.u32 += size;
}
else
{
ctx.r4.u32 = ctx.r1.u32; ctx.r4.u32 = ctx.r1.u32;
original(ctx, base); original(ctx, base);
ctx.r1.u32 += size; ctx.r1.u32 += size;
} }
}
// SWA::CCsdPlatformMirage::Draw // SWA::CCsdPlatformMirage::Draw
PPC_FUNC_IMPL(__imp__sub_825E2E70); PPC_FUNC_IMPL(__imp__sub_825E2E70);
@@ -1114,10 +1293,14 @@ PPC_FUNC(sub_826906A8)
} }
void WorldMapProjectionMidAsmHook(PPCVRegister& v63, PPCVRegister& v62) void WorldMapProjectionMidAsmHook(PPCVRegister& v63, PPCVRegister& v62)
{
// The world map icons are actually broken at 4:3 in the original game!!!
if (Config::AspectRatio != EAspectRatio::OriginalNarrow)
{ {
v63.f32[3] *= std::max(NARROW_ASPECT_RATIO, g_aspectRatio) / WIDE_ASPECT_RATIO; v63.f32[3] *= std::max(NARROW_ASPECT_RATIO, g_aspectRatio) / WIDE_ASPECT_RATIO;
v62.f32[2] *= NARROW_ASPECT_RATIO / std::min(NARROW_ASPECT_RATIO, g_aspectRatio); v62.f32[2] *= NARROW_ASPECT_RATIO / std::min(NARROW_ASPECT_RATIO, g_aspectRatio);
} }
}
// CViewRing has the same exact incorrect math as CObjGetItem. // CViewRing has the same exact incorrect math as CObjGetItem.
void ViewRingFieldOfViewMidAsmHook(PPCRegister& r1, PPCRegister& f1) void ViewRingFieldOfViewMidAsmHook(PPCRegister& r1, PPCRegister& f1)
@@ -1146,11 +1329,22 @@ void ViewRingXMidAsmHook(PPCRegister& f1, PPCVRegister& v62)
} }
} }
// SWA::Inspire::CLetterbox::CLetterbox
PPC_FUNC_IMPL(__imp__sub_82B8A8F8);
PPC_FUNC(sub_82B8A8F8)
{
// Permanently store the letterbox bool instead of letting the game set it to false from widescreen check.
bool letterbox = PPC_LOAD_U8(ctx.r4.u32);
__imp__sub_82B8A8F8(ctx, base);
PPC_STORE_U8(ctx.r3.u32, letterbox);
}
// SWA::Inspire::CLetterbox::Draw // SWA::Inspire::CLetterbox::Draw
PPC_FUNC_IMPL(__imp__sub_82B8AA40); PPC_FUNC_IMPL(__imp__sub_82B8AA40);
PPC_FUNC(sub_82B8AA40) PPC_FUNC(sub_82B8AA40)
{ {
bool shouldDrawLetterbox = Config::CutsceneAspectRatio != ECutsceneAspectRatio::Unlocked && g_aspectRatio < WIDE_ASPECT_RATIO; bool letterbox = PPC_LOAD_U8(ctx.r3.u32);
bool shouldDrawLetterbox = letterbox && Config::CutsceneAspectRatio != ECutsceneAspectRatio::Unlocked && g_aspectRatio < WIDE_ASPECT_RATIO;
PPC_STORE_U8(ctx.r3.u32, shouldDrawLetterbox); PPC_STORE_U8(ctx.r3.u32, shouldDrawLetterbox);
if (shouldDrawLetterbox) if (shouldDrawLetterbox)
@@ -1163,7 +1357,11 @@ PPC_FUNC(sub_82B8AA40)
PPC_STORE_U32(ctx.r3.u32 + 0x14, (720 - width * 9 / 16) / 2); PPC_STORE_U32(ctx.r3.u32 + 0x14, (720 - width * 9 / 16) / 2);
} }
auto r3 = ctx.r3;
__imp__sub_82B8AA40(ctx, base); __imp__sub_82B8AA40(ctx, base);
// Restore the original letterbox value.
PPC_STORE_U8(r3.u32, letterbox);
} }
void InspireLetterboxTopMidAsmHook(PPCRegister& r3) void InspireLetterboxTopMidAsmHook(PPCRegister& r3)
@@ -1185,3 +1383,64 @@ void InspireSubtitleMidAsmHook(PPCRegister& r3)
*reinterpret_cast<be<float>*>(g_memory.base + r3.u32 + 0x3C) = NARROW_OFFSET + (WIDE_OFFSET - NARROW_OFFSET) * g_aspectRatioNarrowScale; *reinterpret_cast<be<float>*>(g_memory.base + r3.u32 + 0x3C) = NARROW_OFFSET + (WIDE_OFFSET - NARROW_OFFSET) * g_aspectRatioNarrowScale;
} }
enum class FadeTextureMode
{
Unknown,
Letterbox,
SideCrop
};
static FadeTextureMode g_fadeTextureMode;
void FxFadePreRenderQuadMidAsmHook(PPCRegister& r31)
{
g_fadeTextureMode = *(g_memory.base + r31.u32 + 0x44) ? FadeTextureMode::Letterbox : FadeTextureMode::SideCrop;
}
void FxFadePostRenderQuadMidAsmHook()
{
g_fadeTextureMode = FadeTextureMode::Unknown;
}
void YggdrasillRenderQuadMidAsmHook(PPCRegister& r3, PPCRegister& r6)
{
if (g_fadeTextureMode != FadeTextureMode::Unknown)
{
float scaleX = 1.0f;
float scaleY = 1.0f;
// Fade textures are slightly squashed in the original game at 4:3.
if (Config::AspectRatio == EAspectRatio::OriginalNarrow)
{
if (g_fadeTextureMode == FadeTextureMode::Letterbox)
scaleY = NARROW_ASPECT_RATIO;
else
scaleX = 0.8f;
}
else
{
if (g_fadeTextureMode == FadeTextureMode::Letterbox && g_aspectRatio < WIDE_ASPECT_RATIO)
scaleY = WIDE_ASPECT_RATIO / g_aspectRatio;
else
scaleX = g_aspectRatio / WIDE_ASPECT_RATIO;
}
struct Vertex
{
be<float> x;
be<float> y;
be<float> z;
be<float> u;
be<float> v;
};
auto vertex = reinterpret_cast<Vertex*>(g_memory.base + r6.u32);
for (size_t i = 0; i < 6; i++)
{
vertex[i].u = (vertex[i].u - 0.5f) * scaleX + 0.5f;
vertex[i].v = (vertex[i].v - 0.5f) * scaleY + 0.5f;
}
}
}
+31 -3
View File
@@ -52,16 +52,44 @@ void AudioPatches::Update(float deltaTime)
} }
else else
{ {
*pMusicVolume = std::lerp(*pMusicVolume, Config::MusicVolume * Config::MasterVolume, time); *pMusicVolume = std::lerp(*pMusicVolume, Config::MusicVolume, time);
} }
} }
else else
{ {
*pMusicVolume = Config::MusicVolume * Config::MasterVolume; *pMusicVolume = Config::MusicVolume;
} }
*pEffectsVolume = Config::EffectsVolume * Config::MasterVolume; *pEffectsVolume = Config::EffectsVolume;
} }
// Stub volume setter. // Stub volume setter.
GUEST_FUNCTION_STUB(sub_82E58728); GUEST_FUNCTION_STUB(sub_82E58728);
// HORRIBLE HACK ZONE
// The options menu uses se_system_worldmap.csb, which is stored in Title.ar.00.
// This archive gets unloaded in stages, which causes sounds to not play in the options menu.
// To solve this, once the CSB gets loaded at title, we'll keep it PERMANENTLY loaded.
// This'll make the SFX not work if Title never gets loaded, but that'll only happen when quick booting to stages.
static bool g_loadedWorldMapCsb;
bool MakeCueSheetDataMidAsmHook(PPCRegister& r31)
{
uint8_t* base = g_memory.base;
uint32_t str = PPC_LOAD_U32(r31.u32);
if (str != NULL && strcmp(reinterpret_cast<const char*>(base + str), "se_system_worldmap") == 0)
{
if (!g_loadedWorldMapCsb)
{
g_loadedWorldMapCsb = true;
return false; // Allow load for the first and only time.
}
// Already loaded before, skip all the loading and name assignment code.
// Not assigning the name prevents it from unloading the CSB file.
return true;
}
return false;
}
+19 -2
View File
@@ -43,8 +43,25 @@ PPC_FUNC(sub_824697B0)
{ {
auto pCamera = (SWA::CCamera*)g_memory.Translate(ctx.r3.u32); auto pCamera = (SWA::CCamera*)g_memory.Translate(ctx.r3.u32);
pCamera->m_InvertX = Config::InvertCameraX; pCamera->m_InvertX = Config::HorizontalCamera == ECameraRotationMode::Reverse;
pCamera->m_InvertY = Config::InvertCameraY; pCamera->m_InvertY = Config::VerticalCamera == ECameraRotationMode::Reverse;
__imp__sub_824697B0(ctx, base); __imp__sub_824697B0(ctx, base);
} }
// SWA::CCamera::UpdateSerial
PPC_FUNC_IMPL(__imp__sub_82467890);
PPC_FUNC(sub_82467890)
{
if (g_needsResize)
{
// Recompute the projection matrix for one frame to fix stretching on pause menu.
auto r3 = ctx.r3;
auto r4 = ctx.r4;
sub_82468E38(ctx, base);
ctx.r3 = r3;
ctx.r4 = r4;
}
__imp__sub_82467890(ctx, base);
}
+9 -1
View File
@@ -94,7 +94,7 @@ bool LoadingUpdateMidAsmHook(PPCRegister& r31)
g_ppcContext->f1.f64 = deltaTime; g_ppcContext->f1.f64 = deltaTime;
g_memory.FindFunction(update)(*g_ppcContext, base); g_memory.FindFunction(update)(*g_ppcContext, base);
bool loading = PPC_LOAD_U8(0x83367A4C); bool loading = *SWA::SGlobals::ms_IsLoading;
if (loading) if (loading)
{ {
now = std::chrono::steady_clock::now(); now = std::chrono::steady_clock::now();
@@ -120,3 +120,11 @@ PPC_FUNC(sub_8312DBF8)
while (std::chrono::steady_clock::now() < next) while (std::chrono::steady_clock::now() < next)
std::this_thread::yield(); std::this_thread::yield();
} }
void WaitVsyncMidAsmHook()
{
}
void ApplicationFrameLimiterMidAsmHook()
{
}
@@ -0,0 +1,39 @@
#include <kernel/memory.h>
#include <ui/options_menu.h>
#include <os/logger.h>
#include <user/config.h>
#include <sdl_listener.h>
static class FrontendListener : public SDLEventListener
{
bool m_isF8KeyDown = false;
public:
void OnSDLEvent(SDL_Event* event) override
{
if (!Config::HUDToggleHotkey || OptionsMenu::s_isVisible)
return;
switch (event->type)
{
case SDL_KEYDOWN:
{
if (event->key.keysym.sym != SDLK_F8 || m_isF8KeyDown)
break;
*SWA::SGlobals::ms_IsRenderHud = !*SWA::SGlobals::ms_IsRenderHud;
LOGFN("HUD {}", *SWA::SGlobals::ms_IsRenderHud ? "ON" : "OFF");
m_isF8KeyDown = true;
break;
}
case SDL_KEYUP:
m_isF8KeyDown = event->key.keysym.sym != SDLK_F8;
break;
}
}
}
g_frontendListener;
+171 -97
View File
@@ -1,13 +1,15 @@
#include <api/SWA.h> #include <api/SWA.h>
#include <hid/hid.h> #include <hid/hid.h>
#include <ui/sdl_listener.h>
#include <app.h> #include <app.h>
#include <exports.h> #include <exports.h>
#include <sdl_listener.h>
class WorldMapTouchParams constexpr double WORLD_MAP_ROTATE_DEADZONE = 0.69999999;
constexpr double WORLD_MAP_CURSOR_DEADZONE = 0.30000001;
class WorldMapCursorParams
{ {
public: public:
float CancelDeadzone{ 0.31f };
float Damping{ 0.99f }; float Damping{ 0.99f };
float FlickAccelX{ 0.25f }; float FlickAccelX{ 0.25f };
float FlickAccelY{ 0.1f }; float FlickAccelY{ 0.1f };
@@ -18,45 +20,63 @@ public:
float Smoothing{ 0.8f }; float Smoothing{ 0.8f };
}; };
class WorldMapTouchParamsProspero : public WorldMapTouchParams class WorldMapCursorParamsProspero : public WorldMapCursorParams
{ {
public: public:
WorldMapTouchParamsProspero() WorldMapCursorParamsProspero()
{ {
SensitivityX = 1.15f; SensitivityX = 1.15f;
SensitivityY = 1.05f; SensitivityY = 1.05f;
} }
} }
g_worldMapTouchParamsProspero; g_worldMapCursorParamsProspero;
class WorldMapTouchParamsOrbis : public WorldMapTouchParams class WorldMapCursorParamsOrbis : public WorldMapCursorParams
{ {
public: public:
WorldMapTouchParamsOrbis() WorldMapCursorParamsOrbis()
{ {
SensitivityX = 0.95f; SensitivityX = 0.95f;
SensitivityY = 1.0f; SensitivityY = 1.0f;
} }
} }
g_worldMapTouchParamsOrbis; g_worldMapCursorParamsOrbis;
WorldMapTouchParams g_worldMapTouchParams{}; #ifdef UNLEASHED_RECOMP_UI_KBM_SUPPORT
class WorldMapCursorParamsMouse : public WorldMapCursorParams
{
public:
WorldMapCursorParamsMouse()
{
FlickAccelX = 0.025f;
FlickAccelY = 0.025f;
FlickThreshold = 7.5f;
SensitivityX = 0.15f;
SensitivityY = 0.15f;
}
}
g_worldMapCursorParamsMouse;
#endif
static bool g_isTouchActive; WorldMapCursorParams g_worldMapCursorParams{};
static float g_worldMapTouchVelocityX; static bool g_isCursorActive;
static float g_worldMapTouchVelocityY;
static float g_worldMapCursorVelocityX;
static float g_worldMapCursorVelocityY;
class SDLEventListenerForInputPatches : public SDLEventListener class SDLEventListenerForInputPatches : public SDLEventListener
{ {
static inline bool ms_isMouseDown;
static inline int ms_touchpadFingerCount; static inline int ms_touchpadFingerCount;
static inline float ms_touchpadX; static inline float ms_cursorX;
static inline float ms_touchpadY; static inline float ms_cursorY;
static inline float ms_touchpadDeltaX; static inline float ms_cursorDeltaX;
static inline float ms_touchpadDeltaY; static inline float ms_cursorDeltaY;
static inline float ms_touchpadPrevX; static inline float ms_cursorPrevX;
static inline float ms_touchpadPrevY; static inline float ms_cursorPrevY;
public: public:
static void Update(float deltaTime) static void Update(float deltaTime)
@@ -67,79 +87,118 @@ public:
all the constants that I had tuned. */ all the constants that I had tuned. */
constexpr auto referenceDeltaTime = 1.0f / 144.0f; constexpr auto referenceDeltaTime = 1.0f / 144.0f;
if (g_isTouchActive) if (g_isCursorActive)
{ {
auto dxNorm = ms_touchpadDeltaX / referenceDeltaTime; auto dxNorm = ms_cursorDeltaX / referenceDeltaTime;
auto dyNorm = ms_touchpadDeltaY / referenceDeltaTime; auto dyNorm = ms_cursorDeltaY / referenceDeltaTime;
auto dxSens = dxNorm * g_worldMapTouchParams.SensitivityX; auto dxSens = dxNorm * g_worldMapCursorParams.SensitivityX;
auto dySens = dyNorm * g_worldMapTouchParams.SensitivityY; auto dySens = dyNorm * g_worldMapCursorParams.SensitivityY;
auto smoothing = powf(g_worldMapTouchParams.Smoothing, deltaTime / referenceDeltaTime); auto smoothing = powf(g_worldMapCursorParams.Smoothing, deltaTime / referenceDeltaTime);
g_worldMapTouchVelocityX = smoothing * g_worldMapTouchVelocityX + (1.0f - smoothing) * dxSens; g_worldMapCursorVelocityX = smoothing * g_worldMapCursorVelocityX + (1.0f - smoothing) * dxSens;
g_worldMapTouchVelocityY = smoothing * g_worldMapTouchVelocityY + (1.0f - smoothing) * dySens; g_worldMapCursorVelocityY = smoothing * g_worldMapCursorVelocityY + (1.0f - smoothing) * dySens;
auto flickThreshold = g_worldMapTouchParams.FlickThreshold; auto flickThreshold = g_worldMapCursorParams.FlickThreshold;
if (fabs(dxSens) > flickThreshold || fabs(dySens) > flickThreshold) if (fabs(dxSens) > flickThreshold || fabs(dySens) > flickThreshold)
{ {
g_worldMapTouchVelocityX += dxNorm * g_worldMapTouchParams.FlickAccelX * (deltaTime / referenceDeltaTime); g_worldMapCursorVelocityX += dxNorm * g_worldMapCursorParams.FlickAccelX * (deltaTime / referenceDeltaTime);
g_worldMapTouchVelocityY += dyNorm * g_worldMapTouchParams.FlickAccelY * (deltaTime / referenceDeltaTime); g_worldMapCursorVelocityY += dyNorm * g_worldMapCursorParams.FlickAccelY * (deltaTime / referenceDeltaTime);
} }
auto terminalVelocity = g_worldMapTouchParams.FlickTerminalVelocity; auto terminalVelocity = g_worldMapCursorParams.FlickTerminalVelocity;
g_worldMapTouchVelocityX = std::clamp(g_worldMapTouchVelocityX, -terminalVelocity, terminalVelocity); g_worldMapCursorVelocityX = std::clamp(g_worldMapCursorVelocityX, -terminalVelocity, terminalVelocity);
g_worldMapTouchVelocityY = std::clamp(g_worldMapTouchVelocityY, -terminalVelocity, terminalVelocity); g_worldMapCursorVelocityY = std::clamp(g_worldMapCursorVelocityY, -terminalVelocity, terminalVelocity);
} }
else else
{ {
auto dampingFactor = powf(g_worldMapTouchParams.Damping, deltaTime / referenceDeltaTime); auto dampingFactor = powf(g_worldMapCursorParams.Damping, deltaTime / referenceDeltaTime);
g_worldMapTouchVelocityX *= dampingFactor; g_worldMapCursorVelocityX *= dampingFactor;
g_worldMapTouchVelocityY *= dampingFactor; g_worldMapCursorVelocityY *= dampingFactor;
} }
} }
void OnSDLEvent(SDL_Event* event) override void OnSDLEvent(SDL_Event* event) override
{ {
if (!hid::IsInputAllowed())
return;
switch (event->type) switch (event->type)
{ {
case SDL_CONTROLLERTOUCHPADMOTION: #ifdef UNLEASHED_RECOMP_UI_KBM_SUPPORT
case SDL_MOUSEMOTION:
{ {
g_isTouchActive = true; if (!ms_isMouseDown)
break;
g_isCursorActive = true;
ms_cursorDeltaX = (float)event->motion.xrel / 100.0f;
ms_cursorDeltaY = (float)event->motion.yrel / 100.0f;
if (ms_touchpadFingerCount > 1)
{
g_isTouchActive = false;
break; break;
} }
ms_touchpadX = event->ctouchpad.x; case SDL_MOUSEBUTTONDOWN:
ms_touchpadY = event->ctouchpad.y; {
ms_touchpadDeltaX = ms_touchpadX - ms_touchpadPrevX; if (event->button.button == SDL_BUTTON_LEFT)
ms_touchpadDeltaY = ms_touchpadY - ms_touchpadPrevY; {
ms_touchpadPrevX = ms_touchpadX; g_worldMapCursorParams = g_worldMapCursorParamsMouse;
ms_touchpadPrevY = ms_touchpadY; ms_isMouseDown = true;
}
break;
}
case SDL_MOUSEBUTTONUP:
{
if (event->button.button == SDL_BUTTON_LEFT)
{
g_isCursorActive = false;
ms_isMouseDown = false;
}
break;
}
#endif
case SDL_CONTROLLERTOUCHPADMOTION:
{
g_isCursorActive = true;
if (ms_touchpadFingerCount > 1)
{
g_isCursorActive = false;
break;
}
ms_cursorX = event->ctouchpad.x;
ms_cursorY = event->ctouchpad.y;
ms_cursorDeltaX = ms_cursorX - ms_cursorPrevX;
ms_cursorDeltaY = ms_cursorY - ms_cursorPrevY;
ms_cursorPrevX = ms_cursorX;
ms_cursorPrevY = ms_cursorY;
break; break;
} }
case SDL_CONTROLLERTOUCHPADDOWN: case SDL_CONTROLLERTOUCHPADDOWN:
{ {
g_worldMapTouchParams = hid::g_inputDeviceExplicit == hid::EInputDeviceExplicit::DualSense g_worldMapCursorParams = hid::g_inputDeviceExplicit == hid::EInputDeviceExplicit::DualSense
? (WorldMapTouchParams)g_worldMapTouchParamsProspero ? (WorldMapCursorParams)g_worldMapCursorParamsProspero
: (WorldMapTouchParams)g_worldMapTouchParamsOrbis; : (WorldMapCursorParams)g_worldMapCursorParamsOrbis;
ms_touchpadFingerCount++; ms_touchpadFingerCount++;
ms_touchpadPrevX = event->ctouchpad.x; ms_cursorPrevX = event->ctouchpad.x;
ms_touchpadPrevY = event->ctouchpad.y; ms_cursorPrevY = event->ctouchpad.y;
break; break;
} }
case SDL_CONTROLLERTOUCHPADUP: case SDL_CONTROLLERTOUCHPADUP:
g_isTouchActive = false; g_isCursorActive = false;
ms_touchpadFingerCount--; ms_touchpadFingerCount--;
break; break;
} }
@@ -149,7 +208,7 @@ g_sdlEventListenerForInputPatches;
// -------------- COMMON --------------- // // -------------- COMMON --------------- //
static bool IsDPadActive(SWA::SPadState* pPadState) static bool IsDPadThreshold(const SWA::SPadState* pPadState)
{ {
return pPadState->IsDown(SWA::eKeyState_DpadUp) || return pPadState->IsDown(SWA::eKeyState_DpadUp) ||
pPadState->IsDown(SWA::eKeyState_DpadDown) || pPadState->IsDown(SWA::eKeyState_DpadDown) ||
@@ -157,6 +216,23 @@ static bool IsDPadActive(SWA::SPadState* pPadState)
pPadState->IsDown(SWA::eKeyState_DpadRight); pPadState->IsDown(SWA::eKeyState_DpadRight);
} }
static bool IsLeftStickThreshold(const SWA::SPadState* pPadState, double deadzone = 0)
{
return sqrtl((pPadState->LeftStickHorizontal * pPadState->LeftStickHorizontal) +
(pPadState->LeftStickVertical * pPadState->LeftStickVertical)) > deadzone;
}
static bool IsCursorThreshold(double deadzone = 0, bool isBelowThreshold = false)
{
auto sqrt = sqrtl((g_worldMapCursorVelocityX * g_worldMapCursorVelocityX) +
(g_worldMapCursorVelocityY * g_worldMapCursorVelocityY));
if (isBelowThreshold)
return sqrt < deadzone;
return sqrt >= deadzone;
}
static void SetDPadAnalogDirectionX(PPCRegister& pPadState, PPCRegister& x, bool invert, float max = 1.0f) static void SetDPadAnalogDirectionX(PPCRegister& pPadState, PPCRegister& x, bool invert, float max = 1.0f)
{ {
auto pGuestPadState = (SWA::SPadState*)g_memory.Translate(pPadState.u32); auto pGuestPadState = (SWA::SPadState*)g_memory.Translate(pPadState.u32);
@@ -227,71 +303,69 @@ void PostureSpaceHurrierDPadSupportYMidAsmHook(PPCRegister& pPadState, PPCVRegis
// ------------- WORLD MAP ------------- // // ------------- WORLD MAP ------------- //
bool WorldMapTouchSupportMidAsmHook() bool WorldMapDeadzoneMidAsmHook(PPCRegister& pPadState)
{
auto pGuestPadState = (SWA::SPadState*)g_memory.Translate(pPadState.u32);
if (IsDPadThreshold(pGuestPadState) || IsLeftStickThreshold(pGuestPadState))
{
g_worldMapCursorVelocityX = 0;
g_worldMapCursorVelocityY = 0;
}
else
{ {
SDLEventListenerForInputPatches::Update(App::s_deltaTime); SDLEventListenerForInputPatches::Update(App::s_deltaTime);
auto vxAbs = fabs(g_worldMapTouchVelocityX); /* Reduce noise if the cursor is resting in
auto vyAbs = fabs(g_worldMapTouchVelocityY); place, but allow much precise values for
proper interpolation to zero. */
if (IsCursorThreshold(0.05, true))
return !g_isCursorActive;
/* Reduce touch noise if the player has return IsCursorThreshold();
their finger resting on the touchpad,
but allow much precise values without
touch for proper interpolation to zero. */
if (vxAbs < 0.05f || vyAbs < 0.05f)
return !g_isTouchActive;
return vxAbs > 0 || vyAbs > 0;
} }
bool WorldMapTouchMagnetismSupportMidAsmHook(PPCRegister& f0) return IsDPadThreshold(pGuestPadState) || IsLeftStickThreshold(pGuestPadState, WORLD_MAP_ROTATE_DEADZONE);
}
bool WorldMapMagnetismMidAsmHook(PPCRegister& f0)
{ {
return fabs(g_worldMapTouchVelocityX) > f0.f64 || fabs(g_worldMapTouchVelocityY) > f0.f64; if (IsCursorThreshold(f0.f64, true))
{
g_worldMapCursorVelocityX = 0;
g_worldMapCursorVelocityY = 0;
return true;
} }
void TouchAndDPadSupportWorldMapXMidAsmHook(PPCRegister& pPadState, PPCRegister& x) return false;
}
void WorldMapHidSupportXMidAsmHook(PPCRegister& pPadState, PPCRegister& x)
{ {
auto pGuestPadState = (SWA::SPadState*)g_memory.Translate(pPadState.u32); auto pGuestPadState = (SWA::SPadState*)g_memory.Translate(pPadState.u32);
if (fabs(pGuestPadState->LeftStickHorizontal) > g_worldMapTouchParams.CancelDeadzone || if (IsDPadThreshold(pGuestPadState))
fabs(pGuestPadState->LeftStickVertical) > g_worldMapTouchParams.CancelDeadzone)
{ {
g_worldMapTouchVelocityX = 0;
}
if (IsDPadActive(pGuestPadState))
{
g_worldMapTouchVelocityX = 0;
SetDPadAnalogDirectionX(pPadState, x, false); SetDPadAnalogDirectionX(pPadState, x, false);
} }
else else if (fabs(g_worldMapCursorVelocityX) > 0)
{ {
if (fabs(g_worldMapTouchVelocityX) > 0) x.f64 = -g_worldMapCursorVelocityX;
x.f64 = -g_worldMapTouchVelocityX;
} }
} }
void TouchAndDPadSupportWorldMapYMidAsmHook(PPCRegister& pPadState, PPCRegister& y) void WorldMapHidSupportYMidAsmHook(PPCRegister& pPadState, PPCRegister& y)
{ {
auto pGuestPadState = (SWA::SPadState*)g_memory.Translate(pPadState.u32); auto pGuestPadState = (SWA::SPadState*)g_memory.Translate(pPadState.u32);
if (fabs(pGuestPadState->LeftStickHorizontal) > g_worldMapTouchParams.CancelDeadzone || if (IsDPadThreshold(pGuestPadState))
fabs(pGuestPadState->LeftStickVertical) > g_worldMapTouchParams.CancelDeadzone)
{ {
g_worldMapTouchVelocityY = 0;
}
if (IsDPadActive(pGuestPadState))
{
g_worldMapTouchVelocityY = 0;
SetDPadAnalogDirectionY(pPadState, y, false); SetDPadAnalogDirectionY(pPadState, y, false);
} }
else else if (fabs(g_worldMapCursorVelocityY) > 0)
{ {
if (fabs(g_worldMapTouchVelocityY) > 0) y.f64 = g_worldMapCursorVelocityY;
y.f64 = g_worldMapTouchVelocityY;
} }
} }
@@ -303,7 +377,7 @@ PPC_FUNC(sub_82486968)
// Reset vertical velocity if maximum pitch reached. // Reset vertical velocity if maximum pitch reached.
if (fabs(pWorldMapCamera->m_Pitch) >= 80.0f) if (fabs(pWorldMapCamera->m_Pitch) >= 80.0f)
g_worldMapTouchVelocityY = 0; g_worldMapCursorVelocityY = 0;
__imp__sub_82486968(ctx, base); __imp__sub_82486968(ctx, base);
} }
@@ -313,7 +387,7 @@ PPC_FUNC(sub_8256C938)
{ {
auto pWorldMapCursor = (SWA::CWorldMapCursor*)g_memory.Translate(ctx.r3.u32); auto pWorldMapCursor = (SWA::CWorldMapCursor*)g_memory.Translate(ctx.r3.u32);
pWorldMapCursor->m_IsCursorMoving = g_isTouchActive; pWorldMapCursor->m_IsCursorMoving = g_isCursorActive && IsCursorThreshold(1.0);
if (ctx.r4.u8) if (ctx.r4.u8)
{ {
@@ -339,8 +413,8 @@ PPC_FUNC(sub_8256C938)
if (rPadState.IsDown(SWA::eKeyState_DpadRight)) if (rPadState.IsDown(SWA::eKeyState_DpadRight))
pWorldMapCursor->m_LeftStickHorizontal = 1.0f; pWorldMapCursor->m_LeftStickHorizontal = 1.0f;
if (sqrtf((pWorldMapCursor->m_LeftStickHorizontal * pWorldMapCursor->m_LeftStickHorizontal) + if (sqrtl((pWorldMapCursor->m_LeftStickHorizontal * pWorldMapCursor->m_LeftStickHorizontal) +
(pWorldMapCursor->m_LeftStickVertical * pWorldMapCursor->m_LeftStickVertical)) > 0.7f) (pWorldMapCursor->m_LeftStickVertical * pWorldMapCursor->m_LeftStickVertical)) > WORLD_MAP_ROTATE_DEADZONE)
{ {
pWorldMapCursor->m_IsCursorMoving = true; pWorldMapCursor->m_IsCursorMoving = true;
} }
+1 -1
View File
@@ -1,9 +1,9 @@
#include "inspire_patches.h" #include "inspire_patches.h"
#include <api/SWA.h> #include <api/SWA.h>
#include <ui/game_window.h> #include <ui/game_window.h>
#include <ui/window_events.h>
#include <os/logger.h> #include <os/logger.h>
#include <app.h> #include <app.h>
#include <sdl_events.h>
static SWA::Inspire::CScene* g_pScene; static SWA::Inspire::CScene* g_pScene;
static std::string g_sceneName; static std::string g_sceneName;
+2 -2
View File
@@ -1,11 +1,11 @@
#include <api/SWA.h> #include <api/SWA.h>
#include <ui/game_window.h> #include <ui/game_window.h>
#include <user/achievement_data.h> #include <user/achievement_manager.h>
#include <user/config.h> #include <user/config.h>
void AchievementManagerUnlockMidAsmHook(PPCRegister& id) void AchievementManagerUnlockMidAsmHook(PPCRegister& id)
{ {
AchievementData::Unlock(id.u32); AchievementManager::Unlock(id.u32);
} }
bool DisableHintsMidAsmHook() bool DisableHintsMidAsmHook()
@@ -1,5 +1,6 @@
#include <user/config.h> #include <user/config.h>
#include <SWA/CharacterUtility/CharacterProxy.h> #include <SWA/CharacterUtility/CharacterProxy.h>
#include <hid/hid.h>
// CObjFlame::CObjFlame // CObjFlame::CObjFlame
// A field is not zero initialized, // A field is not zero initialized,
@@ -67,3 +68,33 @@ void ObjBigBarrelSetPositionMidAsmHook(PPCRegister& r3, PPCRegister& r4)
position->Z = position->Z - characterProxy->m_Velocity.Z * factor; position->Z = position->Z - characterProxy->m_Velocity.Z * factor;
} }
} }
// Tornado Defense bullet particles are colored by the button prompt, which differs on PlayStation 3.
// Luckily, the PS3 particles are left in the files, and they get spawned by name when a bullet gets created.
// SWA::CExBullet::AddCallback
PPC_FUNC_IMPL(__imp__sub_82B14CC0);
PPC_FUNC(sub_82B14CC0)
{
auto isPlayStation = Config::ControllerIcons == EControllerIcons::PlayStation;
if (Config::ControllerIcons == EControllerIcons::Auto)
isPlayStation = hid::g_inputDeviceController == hid::EInputDevice::PlayStation;
if (isPlayStation)
{
PPC_STORE_U8(0x820C2A0B, 'b'); // Cross
PPC_STORE_U8(0x820C29C3, 'r'); // Circle
PPC_STORE_U8(0x820C29DB, 'p'); // Square
PPC_STORE_U8(0x820C29F3, 'g'); // Triangle
}
else
{
PPC_STORE_U8(0x820C2A0B, 'g'); // A
PPC_STORE_U8(0x820C29C3, 'r'); // B
PPC_STORE_U8(0x820C29DB, 'b'); // X
PPC_STORE_U8(0x820C29F3, 'o'); // Y
}
__imp__sub_82B14CC0(ctx, base);
}
+3 -6
View File
@@ -1,9 +1,9 @@
#include <api/SWA.h> #include <api/SWA.h>
#include <ui/game_window.h> #include <ui/game_window.h>
#include <ui/window_events.h>
#include <user/config.h> #include <user/config.h>
#include <os/logger.h> #include <os/logger.h>
#include <app.h> #include <app.h>
#include <sdl_events.h>
static uint32_t g_lastEnemyScore; static uint32_t g_lastEnemyScore;
static uint32_t g_lastTrickScore; static uint32_t g_lastTrickScore;
@@ -50,11 +50,8 @@ PPC_FUNC(sub_8245F048)
void ResetScoreOnRestartMidAsmHook() void ResetScoreOnRestartMidAsmHook()
{ {
if (auto pGameDocument = SWA::CGameDocument::GetInstance()) g_lastEnemyScore = 0;
{ g_lastTrickScore = 0;
pGameDocument->m_pMember->m_ScoreInfo.EnemyScore = 0;
pGameDocument->m_pMember->m_ScoreInfo.TrickScore = 0;
}
} }
// Dark Gaia energy change hook. // Dark Gaia energy change hook.
+30 -8
View File
@@ -1,8 +1,8 @@
#include <user/achievement_data.h>
#include <user/config.h>
#include <api/SWA.h> #include <api/SWA.h>
#include <os/logger.h>
#include <hid/hid.h> #include <hid/hid.h>
#include <os/logger.h>
#include <user/achievement_manager.h>
#include <user/config.h>
#include <app.h> #include <app.h>
bool m_isSavedAchievementData = false; bool m_isSavedAchievementData = false;
@@ -11,13 +11,17 @@ bool m_isSavedAchievementData = false;
PPC_FUNC_IMPL(__imp__sub_824DCF38); PPC_FUNC_IMPL(__imp__sub_824DCF38);
PPC_FUNC(sub_824DCF38) PPC_FUNC(sub_824DCF38)
{ {
auto pLoading = (SWA::CLoading*)g_memory.Translate(ctx.r3.u32);
App::s_isLoading = true; App::s_isLoading = true;
// TODO: use the actual PS3 loading screen ("n_2_d").
if (Config::TimeOfDayTransition == ETimeOfDayTransition::PlayStation) if (Config::TimeOfDayTransition == ETimeOfDayTransition::PlayStation)
{ {
if (ctx.r4.u32 == SWA::eLoadingDisplayType_WerehogMovie) if (ctx.r4.u32 == SWA::eLoadingDisplayType_WerehogMovie)
ctx.r4.u32 = SWA::eLoadingDisplayType_Arrows; {
ctx.r4.u32 = SWA::eLoadingDisplayType_ChangeTimeOfDay;
pLoading->m_IsNightToDay = App::s_isWerehog;
}
} }
if (auto pGameDocument = SWA::CGameDocument::GetInstance()) if (auto pGameDocument = SWA::CGameDocument::GetInstance())
@@ -36,6 +40,26 @@ PPC_FUNC(sub_824DCF38)
__imp__sub_824DCF38(ctx, base); __imp__sub_824DCF38(ctx, base);
} }
// The game checks for a bool to render the PS3 transition animation. It's never set so it's presumably a "is PS3" bool.
bool LoadingRenderMidAsmHook()
{
return Config::TimeOfDayTransition == ETimeOfDayTransition::PlayStation;
}
// Patch "ui_loading.yncp" to remove the medal swinging animation.
// SWA::CCsdProject::Make
PPC_FUNC_IMPL(__imp__sub_825E4068);
PPC_FUNC(sub_825E4068)
{
if (ctx.r4.u32 != NULL && ctx.r5.u32 == 0x65C0C && XXH3_64bits(base + ctx.r4.u32, ctx.r5.u32) == 0xD4DA1A9BE4D79BED)
{
// Keyframe count. First keyframe is at the center of the screen.
PPC_STORE_U32(ctx.r4.u32 + 0x2794C, 1);
}
__imp__sub_825E4068(ctx, base);
}
// SWA::CLoading::Update // SWA::CLoading::Update
PPC_FUNC_IMPL(__imp__sub_824DAB60); PPC_FUNC_IMPL(__imp__sub_824DAB60);
PPC_FUNC(sub_824DAB60) PPC_FUNC(sub_824DAB60)
@@ -73,9 +97,7 @@ PPC_FUNC(sub_824E5170)
if (!m_isSavedAchievementData) if (!m_isSavedAchievementData)
{ {
LOGN("Saving achievements..."); AchievementManager::Save();
AchievementData::Save();
m_isSavedAchievementData = true; m_isSavedAchievementData = true;
} }
@@ -1,95 +0,0 @@
#include "CTitleStateIntro_patches.h"
#include <api/SWA.h>
#include <locale/locale.h>
#include <ui/fader.h>
#include <ui/message_window.h>
#include <user/paths.h>
#include <app.h>
bool g_quitMessageOpen = false;
static bool g_quitMessageFaderBegun = false;
static int g_quitMessageResult = -1;
static std::atomic<bool> g_corruptSaveMessageOpen = false;
static int g_corruptSaveMessageResult = -1;
static bool ProcessQuitMessage()
{
if (g_corruptSaveMessageOpen)
return false;
if (!g_quitMessageOpen)
return false;
if (g_quitMessageFaderBegun)
return true;
std::array<std::string, 2> options = { Localise("Common_Yes"), Localise("Common_No") };
if (MessageWindow::Open(Localise("Title_Message_Quit"), &g_quitMessageResult, options, 1) == MSG_CLOSED)
{
switch (g_quitMessageResult)
{
case 0:
Fader::FadeOut(1, []() { App::Exit(); });
g_quitMessageFaderBegun = true;
break;
case 1:
g_quitMessageOpen = false;
g_quitMessageResult = -1;
break;
}
}
return true;
}
static bool ProcessCorruptSaveMessage()
{
if (!g_corruptSaveMessageOpen)
return false;
if (MessageWindow::Open(Localise("Title_Message_SaveDataCorrupt"), &g_corruptSaveMessageResult) == MSG_CLOSED)
{
g_corruptSaveMessageOpen = false;
g_corruptSaveMessageOpen.notify_one();
g_corruptSaveMessageResult = -1;
}
return true;
}
void StorageDevicePromptMidAsmHook() {}
// Save data validation hook.
PPC_FUNC_IMPL(__imp__sub_822C55B0);
PPC_FUNC(sub_822C55B0)
{
App::s_isSaveDataCorrupt = true;
g_corruptSaveMessageOpen = true;
g_corruptSaveMessageOpen.wait(true);
ctx.r3.u32 = 0;
}
// SWA::CTitleStateIntro::Update
PPC_FUNC_IMPL(__imp__sub_82587E50);
PPC_FUNC(sub_82587E50)
{
auto isAutoSaveWarningShown = *(bool*)g_memory.Translate(0x83367BC1);
if (isAutoSaveWarningShown)
{
__imp__sub_82587E50(ctx, base);
}
else if (!ProcessCorruptSaveMessage())
{
auto pInputState = SWA::CInputState::GetInstance();
if (pInputState && pInputState->GetPadState().IsTapped(SWA::eKeyState_B))
g_quitMessageOpen = true;
if (!ProcessQuitMessage())
__imp__sub_82587E50(ctx, base);
}
}
@@ -1,3 +0,0 @@
#include "frontend_listener.h"
FrontendListener m_frontendListener;
@@ -1,42 +0,0 @@
#pragma once
#include <kernel/memory.h>
#include <ui/sdl_listener.h>
#include <ui/options_menu.h>
#include <os/logger.h>
class FrontendListener : public SDLEventListener
{
bool m_isF8KeyDown = false;
public:
void OnSDLEvent(SDL_Event* event) override
{
if (OptionsMenu::s_isVisible)
return;
switch (event->type)
{
case SDL_KEYDOWN:
{
if (event->key.keysym.sym != SDLK_F8 || m_isF8KeyDown)
break;
// アプリケーション設定 / 開発用 / デバッグ / HUD / 全 HUD 描画
const auto ms_IsRenderHud = (bool*)g_memory.Translate(0x8328BB26);
*ms_IsRenderHud = !*ms_IsRenderHud;
LOGFN("HUD {}", *ms_IsRenderHud ? "ON" : "OFF");
m_isF8KeyDown = true;
break;
}
case SDL_KEYUP:
m_isF8KeyDown = event->key.keysym.sym != SDLK_F8;
break;
}
}
};
+45
View File
@@ -68,3 +68,48 @@ bool MotionBlurMidAsmHook()
{ {
return Config::MotionBlur != EMotionBlur::Off; return Config::MotionBlur != EMotionBlur::Off;
} }
// Hedgehog::MirageDebug::PrepareRenderPrimitive2D
PPC_FUNC_IMPL(__imp__sub_830D25D8);
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;
__imp__sub_830D25D8(ctx, base);
}
// Rope renderables sometimes get bogus colors due to the material parameters of whatever
// was rendered last leaking into their render state. We can reset them to fix it.
static void SetDefaultMaterialParameters(GuestDevice* device)
{
const be<float> diffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f };
const be<float> ambient[] = { 1.0f, 1.0f, 1.0f, 0.0f };
const be<float> specular[] = { 0.9f, 0.9f, 0.9f, 0.0f };
memcpy(&device->pixelShaderFloatConstants[64], diffuse, sizeof(diffuse)); // g_Diffuse
memcpy(&device->pixelShaderFloatConstants[68], ambient, sizeof(ambient)); // g_Ambient
memcpy(&device->pixelShaderFloatConstants[72], specular, sizeof(specular)); // g_Specular
device->dirtyFlags[1] = ~0ull;
}
// SWA::CRopeRenderable::Render
PPC_FUNC_IMPL(__imp__sub_827CBF68);
PPC_FUNC(sub_827CBF68)
{
SetDefaultMaterialParameters(reinterpret_cast<GuestDevice*>(base + PPC_LOAD_U32(PPC_LOAD_U32(ctx.r4.u32))));
__imp__sub_827CBF68(ctx, base);
}
// SWA::CObjUpReel::CPrimitiveReel::Render
PPC_FUNC_IMPL(__imp__sub_8260BBF8);
PPC_FUNC(sub_8260BBF8)
{
SetDefaultMaterialParameters(reinterpret_cast<GuestDevice*>(base + PPC_LOAD_U32(PPC_LOAD_U32(ctx.r4.u32))));
__imp__sub_8260BBF8(ctx, base);
}
+1
View File
@@ -1,3 +1,4 @@
![Ww][Ii][Nn]32/ ![Ww][Ii][Nn]32/
*.c *.c
*.h *.h
!credits.h
+20
View File
@@ -0,0 +1,20 @@
#pragma once
inline const char* g_credits[] =
{
"Skyth",
"Hyper",
"Darío",
"Sajid",
"RadiantDerg",
"PTKay",
"DeaThProj",
"SuperSonic16",
"NextinHKRY",
"M&M",
"saguinee",
"LadyLunanova",
"LJSTAR"
};
inline size_t g_creditsSize = 12;
+40 -1
View File
@@ -1 +1,40 @@
IDI_ICON1 ICON "@ICON_PATH@" #include <windows.h>
#define UNLEASHED_RECOMP_VERSION_BIN @WIN32_VERSION_BINARY@
#define UNLEASHED_RECOMP_VERSION_STR "@WIN32_VERSION_STRING@"
#ifdef _DEBUG
#define UNLEASHED_RECOMP_FILE_FLAGS VS_FF_DEBUG
#else
#define UNLEASHED_RECOMP_FILE_FLAGS 0
#endif
IDI_ICON1 ICON "@WIN32_ICON_PATH@"
VS_VERSION_INFO VERSIONINFO
FILEVERSION UNLEASHED_RECOMP_VERSION_BIN
PRODUCTVERSION UNLEASHED_RECOMP_VERSION_BIN
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
FILEFLAGS UNLEASHED_RECOMP_FILE_FLAGS
FILEOS VOS_NT_WINDOWS32
FILETYPE VFT_APP
FILESUBTYPE VFT2_UNKNOWN
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "080904B0" // English (UK), Unicode
BEGIN
VALUE "ProductName", "Unleashed Recompiled"
VALUE "FileDescription", "Unleashed Recompiled"
VALUE "CompanyName", "hedge-dev"
VALUE "FileVersion", UNLEASHED_RECOMP_VERSION_STR
VALUE "ProductVersion", UNLEASHED_RECOMP_VERSION_STR
VALUE "InternalName", "UnleashedRecomp"
VALUE "OriginalFilename", "SWA.exe"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x0809, 0x04B0
END
END
@@ -1,7 +1,7 @@
#pragma once #pragma once
#include <SDL.h> #include <SDL.h>
#include "ui/game_window.h" #include <ui/game_window.h>
#define SDL_USER_EVILSONIC (SDL_USEREVENT + 1) #define SDL_USER_EVILSONIC (SDL_USEREVENT + 1)
+16 -50
View File
@@ -1,20 +1,20 @@
#include "achievement_menu.h" #include "achievement_menu.h"
#include "imgui_utils.h"
#include <api/SWA.h> #include <api/SWA.h>
#include <gpu/imgui/imgui_snapshot.h>
#include <gpu/video.h> #include <gpu/video.h>
#include <hid/hid.h>
#include <kernel/xdbf.h> #include <kernel/xdbf.h>
#include <locale/locale.h> #include <locale/locale.h>
#include <patches/aspect_ratio_patches.h>
#include <ui/button_guide.h> #include <ui/button_guide.h>
#include <user/achievement_data.h> #include <ui/imgui_utils.h>
#include <user/achievement_manager.h>
#include <user/config.h> #include <user/config.h>
#include <app.h> #include <app.h>
#include <exports.h> #include <exports.h>
#include <decompressor.h> #include <decompressor.h>
#include <res/images/achievements_menu/trophy.dds.h> #include <res/images/achievements_menu/trophy.dds.h>
#include <res/images/common/general_window.dds.h>
#include <res/images/common/select_fill.dds.h>
#include <gpu/imgui/imgui_snapshot.h>
#include <hid/hid.h>
constexpr double HEADER_CONTAINER_INTRO_MOTION_START = 0; constexpr double HEADER_CONTAINER_INTRO_MOTION_START = 0;
constexpr double HEADER_CONTAINER_INTRO_MOTION_END = 15; constexpr double HEADER_CONTAINER_INTRO_MOTION_END = 15;
@@ -44,8 +44,6 @@ static ImFont* g_fntNewRodinDB;
static ImFont* g_fntNewRodinUB; static ImFont* g_fntNewRodinUB;
static std::unique_ptr<GuestTexture> g_upTrophyIcon; static std::unique_ptr<GuestTexture> g_upTrophyIcon;
static std::unique_ptr<GuestTexture> g_upSelectionCursor;
static std::unique_ptr<GuestTexture> g_upWindow;
static int g_firstVisibleRowIndex; static int g_firstVisibleRowIndex;
static int g_selectedRowIndex; static int g_selectedRowIndex;
@@ -71,43 +69,11 @@ static void DrawContainer(ImVec2 min, ImVec2 max, ImU32 gradientTop, ImU32 gradi
{ {
auto drawList = ImGui::GetForegroundDrawList(); auto drawList = ImGui::GetForegroundDrawList();
DrawPauseContainer(g_upWindow.get(), min, max, alpha); DrawPauseContainer(min, max, alpha);
drawList->PushClipRect({ min.x, min.y + Scale(20) }, { max.x, max.y - Scale(5) }); drawList->PushClipRect({ min.x, min.y + Scale(20) }, { max.x, max.y - Scale(5) });
} }
static void DrawSelectionContainer(ImVec2 min, ImVec2 max)
{
auto drawList = ImGui::GetForegroundDrawList();
static auto breatheStart = ImGui::GetTime();
auto alpha = Lerp(1.0f, 0.65f, (sin((ImGui::GetTime() - breatheStart) * (2.0f * M_PI / (55.0f / 60.0f))) + 1.0f) / 2.0f);
auto colour = IM_COL32(255, 255, 255, 255 * alpha);
auto commonWidth = Scale(11);
auto commonHeight = Scale(24);
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);
drawList->AddImage(g_upSelectionCursor.get(), min, { min.x + commonWidth, min.y + commonHeight }, GET_UV_COORDS(tl), colour);
drawList->AddImage(g_upSelectionCursor.get(), { min.x + commonWidth, min.y }, { max.x - commonWidth, min.y + commonHeight }, GET_UV_COORDS(tc), colour);
drawList->AddImage(g_upSelectionCursor.get(), { max.x - commonWidth, min.y }, { max.x, min.y + commonHeight }, GET_UV_COORDS(tr), colour);
drawList->AddImage(g_upSelectionCursor.get(), { min.x, min.y + commonHeight }, { min.x + commonWidth, max.y - commonHeight }, GET_UV_COORDS(cl), colour);
drawList->AddImage(g_upSelectionCursor.get(), { min.x + commonWidth, min.y + commonHeight }, { max.x - commonWidth, max.y - commonHeight }, GET_UV_COORDS(cc), colour);
drawList->AddImage(g_upSelectionCursor.get(), { max.x - commonWidth, min.y + commonHeight }, { max.x, max.y - commonHeight }, GET_UV_COORDS(cr), colour);
drawList->AddImage(g_upSelectionCursor.get(), { min.x, max.y - commonHeight }, { min.x + commonWidth, max.y }, GET_UV_COORDS(bl), colour);
drawList->AddImage(g_upSelectionCursor.get(), { min.x + commonWidth, max.y - commonHeight }, { max.x - commonWidth, max.y }, GET_UV_COORDS(bc), colour);
drawList->AddImage(g_upSelectionCursor.get(), { max.x - commonWidth, max.y - commonHeight }, { max.x, max.y }, GET_UV_COORDS(br), colour);
}
static void DrawHeaderContainer(const char* text) static void DrawHeaderContainer(const char* text)
{ {
auto drawList = ImGui::GetForegroundDrawList(); auto drawList = ImGui::GetForegroundDrawList();
@@ -137,7 +103,7 @@ static void DrawHeaderContainer(const char* text)
ImVec2 min = { g_aspectRatioOffsetX + Scale(containerMarginX), g_aspectRatioOffsetY + Scale(136) }; 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 = { min.x + textMarginX * 2 + textSize.x + Scale(5), g_aspectRatioOffsetY + Scale(196) };
DrawPauseHeaderContainer(g_upWindow.get(), min, max, alpha); DrawPauseHeaderContainer(min, max, alpha);
SetTextSkew((min.y + max.y) / 2.0f, Scale(3.0f)); SetTextSkew((min.y + max.y) / 2.0f, Scale(3.0f));
@@ -231,7 +197,7 @@ static void DrawAchievement(int rowIndex, float yOffset, Achievement& achievemen
ImVec2 marqueeMin = { textMarqueeX, min.y }; ImVec2 marqueeMin = { textMarqueeX, min.y };
ImVec2 marqueeMax = { max.x - Scale(10) /* timestamp margin X */, max.y }; ImVec2 marqueeMax = { max.x - Scale(10) /* timestamp margin X */, max.y };
SetMarqueeFade(marqueeMin, marqueeMax, Scale(32)); SetHorizontalMarqueeFade(marqueeMin, marqueeMax, Scale(32));
if (isSelected && textX + textSize.x >= max.x - Scale(10)) if (isSelected && textX + textSize.x >= max.x - Scale(10))
{ {
@@ -276,7 +242,7 @@ static void DrawAchievement(int rowIndex, float yOffset, Achievement& achievemen
if (!isUnlocked) if (!isUnlocked)
return; return;
auto timestamp = AchievementData::GetTimestamp(achievement.ID); auto timestamp = AchievementManager::GetTimestamp(achievement.ID);
if (!timestamp) if (!timestamp)
return; return;
@@ -485,7 +451,7 @@ static void DrawAchievementTotal(ImVec2 min, ImVec2 max)
auto uv1 = ImVec2((columnIndex + 1) * spriteSize / textureWidth, (rowIndex + 1) * spriteSize / textureHeight); auto uv1 = ImVec2((columnIndex + 1) * spriteSize / textureWidth, (rowIndex + 1) * spriteSize / textureHeight);
constexpr auto recordsHalfTotal = ACH_RECORDS / 2; constexpr auto recordsHalfTotal = ACH_RECORDS / 2;
auto records = AchievementData::GetTotalRecords(); auto records = AchievementManager::GetTotalRecords();
ImVec4 colBronze = ImGui::ColorConvertU32ToFloat4(IM_COL32(198, 105, 15, 255 * alpha)); ImVec4 colBronze = ImGui::ColorConvertU32ToFloat4(IM_COL32(198, 105, 15, 255 * alpha));
ImVec4 colSilver = ImGui::ColorConvertU32ToFloat4(IM_COL32(220, 220, 220, 255 * alpha)); ImVec4 colSilver = ImGui::ColorConvertU32ToFloat4(IM_COL32(220, 220, 220, 255 * alpha));
@@ -580,11 +546,13 @@ static void DrawContentContainer()
if (motion < 1.0f) if (motion < 1.0f)
{ {
drawList->PopClipRect();
return; return;
} }
else if (g_isClosing) else if (g_isClosing)
{ {
AchievementMenu::s_isVisible = false; AchievementMenu::s_isVisible = false;
drawList->PopClipRect();
return; return;
} }
@@ -613,7 +581,7 @@ static void DrawContentContainer()
{ {
auto achievement = std::get<0>(tpl); auto achievement = std::get<0>(tpl);
if (AchievementData::IsUnlocked(achievement.ID)) if (AchievementManager::IsUnlocked(achievement.ID))
DrawAchievement(rowCount++, yOffset, achievement, true); DrawAchievement(rowCount++, yOffset, achievement, true);
} }
@@ -621,7 +589,7 @@ static void DrawContentContainer()
{ {
auto achievement = std::get<0>(tpl); auto achievement = std::get<0>(tpl);
if (!AchievementData::IsUnlocked(achievement.ID)) if (!AchievementManager::IsUnlocked(achievement.ID))
DrawAchievement(rowCount++, yOffset, achievement, false); DrawAchievement(rowCount++, yOffset, achievement, false);
} }
@@ -778,8 +746,6 @@ void AchievementMenu::Init()
g_fntNewRodinUB = ImFontAtlasSnapshot::GetFont("FOT-NewRodinPro-UB.otf"); g_fntNewRodinUB = ImFontAtlasSnapshot::GetFont("FOT-NewRodinPro-UB.otf");
g_upTrophyIcon = LOAD_ZSTD_TEXTURE(g_trophy); g_upTrophyIcon = LOAD_ZSTD_TEXTURE(g_trophy);
g_upSelectionCursor = LOAD_ZSTD_TEXTURE(g_select_fill);
g_upWindow = LOAD_ZSTD_TEXTURE(g_general_window);
} }
void AchievementMenu::Draw() void AchievementMenu::Draw()
@@ -804,7 +770,7 @@ void AchievementMenu::Open()
if (Config::Language == ELanguage::English) if (Config::Language == ELanguage::English)
achievement.Name = xdbf::FixInvalidSequences(achievement.Name); achievement.Name = xdbf::FixInvalidSequences(achievement.Name);
g_achievements.push_back(std::make_tuple(achievement, AchievementData::GetTimestamp(achievement.ID))); g_achievements.push_back(std::make_tuple(achievement, AchievementManager::GetTimestamp(achievement.ID)));
} }
std::sort(g_achievements.begin(), g_achievements.end(), [](const auto& a, const auto& b) std::sort(g_achievements.begin(), g_achievements.end(), [](const auto& a, const auto& b)
+15 -15
View File
@@ -1,16 +1,15 @@
#include "achievement_overlay.h" #include "achievement_overlay.h"
#include "imgui_utils.h" #include <gpu/imgui/imgui_snapshot.h>
#include <gpu/video.h> #include <gpu/video.h>
#include <kernel/memory.h> #include <kernel/memory.h>
#include <kernel/xdbf.h> #include <kernel/xdbf.h>
#include <locale/locale.h> #include <locale/locale.h>
#include <user/config.h> #include <ui/imgui_utils.h>
#include <user/achievement_data.h> #include <user/achievement_data.h>
#include <user/config.h>
#include <app.h> #include <app.h>
#include <exports.h> #include <exports.h>
#include <decompressor.h> #include <decompressor.h>
#include <res/images/common/general_window.dds.h>
#include <gpu/imgui/imgui_snapshot.h>
constexpr double OVERLAY_CONTAINER_COMMON_MOTION_START = 0; constexpr double OVERLAY_CONTAINER_COMMON_MOTION_START = 0;
constexpr double OVERLAY_CONTAINER_COMMON_MOTION_END = 11; constexpr double OVERLAY_CONTAINER_COMMON_MOTION_END = 11;
@@ -29,8 +28,6 @@ static Achievement g_achievement;
static ImFont* g_fntSeurat; static ImFont* g_fntSeurat;
static std::unique_ptr<GuestTexture> g_upWindow;
static bool DrawContainer(ImVec2 min, ImVec2 max, float cornerRadius = 25) static bool DrawContainer(ImVec2 min, ImVec2 max, float cornerRadius = 25)
{ {
auto drawList = ImGui::GetForegroundDrawList(); auto drawList = ImGui::GetForegroundDrawList();
@@ -65,11 +62,15 @@ static bool DrawContainer(ImVec2 min, ImVec2 max, float cornerRadius = 25)
? Hermite(1, 0, colourMotion) ? Hermite(1, 0, colourMotion)
: Hermite(0, 1, colourMotion); : Hermite(0, 1, colourMotion);
DrawPauseContainer(g_upWindow.get(), min, max, alpha); DrawPauseContainer(min, max, alpha);
if (containerMotion >= 1.0f)
{
drawList->PushClipRect(min, max); drawList->PushClipRect(min, max);
return true;
}
return containerMotion >= 1.0f; return false;
} }
void AchievementOverlay::Init() void AchievementOverlay::Init()
@@ -77,8 +78,6 @@ void AchievementOverlay::Init()
auto& io = ImGui::GetIO(); auto& io = ImGui::GetIO();
g_fntSeurat = ImFontAtlasSnapshot::GetFont("FOT-SeuratPro-M.otf"); g_fntSeurat = ImFontAtlasSnapshot::GetFont("FOT-SeuratPro-M.otf");
g_upWindow = LOAD_ZSTD_TEXTURE(g_general_window);
} }
void AchievementOverlay::Draw() void AchievementOverlay::Draw()
@@ -117,12 +116,8 @@ void AchievementOverlay::Draw()
if (DrawContainer(min, max)) if (DrawContainer(min, max))
{ {
if (g_isClosing) if (!g_isClosing)
{ {
s_isVisible = false;
return;
}
// Draw achievement icon. // Draw achievement icon.
drawList->AddImage drawList->AddImage
( (
@@ -159,6 +154,11 @@ void AchievementOverlay::Draw()
1.0f, // radius 1.0f, // radius
IM_COL32(0, 0, 0, 255) // shadowColour IM_COL32(0, 0, 0, 255) // shadowColour
); );
}
else
{
s_isVisible = false;
}
// Pop clip rect from DrawContainer. // Pop clip rect from DrawContainer.
drawList->PopClipRect(); drawList->PopClipRect();
+2
View File
@@ -3,9 +3,11 @@
#include <gpu/imgui/imgui_snapshot.h> #include <gpu/imgui/imgui_snapshot.h>
#include <gpu/video.h> #include <gpu/video.h>
#include <hid/hid.h> #include <hid/hid.h>
#include <patches/aspect_ratio_patches.h>
#include <user/config.h> #include <user/config.h>
#include <app.h> #include <app.h>
#include <decompressor.h> #include <decompressor.h>
#include <res/images/common/controller.dds.h> #include <res/images/common/controller.dds.h>
#include <res/images/common/kbm.dds.h> #include <res/images/common/kbm.dds.h>
+7 -1
View File
@@ -3,8 +3,8 @@
#include <os/logger.h> #include <os/logger.h>
#include <os/user.h> #include <os/user.h>
#include <os/version.h> #include <os/version.h>
#include <ui/sdl_listener.h>
#include <app.h> #include <app.h>
#include <sdl_listener.h>
#include <SDL_syswm.h> #include <SDL_syswm.h>
#if _WIN32 #if _WIN32
@@ -231,6 +231,12 @@ void GameWindow::Init(const char* sdlVideoDriver)
#if defined(_WIN32) #if defined(_WIN32)
s_renderWindow = info.info.win.window; s_renderWindow = info.info.win.window;
if (Config::DisableDWMRoundedCorners)
{
DWM_WINDOW_CORNER_PREFERENCE wcp = DWMWCP_DONOTROUND;
DwmSetWindowAttribute(s_renderWindow, DWMWA_WINDOW_CORNER_PREFERENCE, &wcp, sizeof(wcp));
}
#elif defined(SDL_VULKAN_ENABLED) #elif defined(SDL_VULKAN_ENABLED)
s_renderWindow = s_pWindow; s_renderWindow = s_pWindow;
#elif defined(__linux__) #elif defined(__linux__)
+2 -2
View File
@@ -1,8 +1,8 @@
#pragma once #pragma once
#include <gpu/rhi/plume_render_interface_types.h> #include <gpu/rhi/plume_render_interface_types.h>
#include <ui/window_events.h>
#include <user/config.h> #include <user/config.h>
#include <sdl_events.h>
#define DEFAULT_WIDTH 1280 #define DEFAULT_WIDTH 1280
#define DEFAULT_HEIGHT 720 #define DEFAULT_HEIGHT 720
@@ -12,7 +12,7 @@
class GameWindow class GameWindow
{ {
public: public:
static inline SDL_Window* s_pWindow; static inline SDL_Window* s_pWindow = nullptr;
static inline plume::RenderWindow s_renderWindow; static inline plume::RenderWindow s_renderWindow;
static inline int s_x; static inline int s_x;
+815
View File
@@ -0,0 +1,815 @@
#include "imgui_utils.h"
#include <patches/aspect_ratio_patches.h>
#include <app.h>
#include <decompressor.h>
#include <version.h>
#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>
std::unique_ptr<GuestTexture> g_texGeneralWindow;
std::unique_ptr<GuestTexture> g_texLight;
std::unique_ptr<GuestTexture> g_texSelectFade;
std::unique_ptr<GuestTexture> g_texSelectFill;
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);
}
void SetGradient(const ImVec2& min, const ImVec2& max, ImU32 top, ImU32 bottom)
{
SetGradient(min, max, top, top, bottom, bottom);
}
void SetGradient(const ImVec2& min, const ImVec2& max, ImU32 topLeft, ImU32 topRight, ImU32 bottomRight, ImU32 bottomLeft)
{
auto callbackData = AddImGuiCallback(ImGuiCallback::SetGradient);
callbackData->setGradient.boundsMin[0] = min.x;
callbackData->setGradient.boundsMin[1] = min.y;
callbackData->setGradient.boundsMax[0] = max.x;
callbackData->setGradient.boundsMax[1] = max.y;
callbackData->setGradient.gradientTopLeft = topLeft;
callbackData->setGradient.gradientTopRight = topRight;
callbackData->setGradient.gradientBottomRight = bottomRight;
callbackData->setGradient.gradientBottomLeft = bottomLeft;
}
void ResetGradient()
{
auto callbackData = AddImGuiCallback(ImGuiCallback::SetGradient);
memset(&callbackData->setGradient, 0, sizeof(callbackData->setGradient));
}
void SetShaderModifier(uint32_t shaderModifier)
{
auto callbackData = AddImGuiCallback(ImGuiCallback::SetShaderModifier);
callbackData->setShaderModifier.shaderModifier = shaderModifier;
}
void SetOrigin(ImVec2 origin)
{
auto callbackData = AddImGuiCallback(ImGuiCallback::SetOrigin);
callbackData->setOrigin.origin[0] = origin.x;
callbackData->setOrigin.origin[1] = origin.y;
}
void SetScale(ImVec2 scale)
{
auto callbackData = AddImGuiCallback(ImGuiCallback::SetScale);
callbackData->setScale.scale[0] = scale.x;
callbackData->setScale.scale[1] = scale.y;
}
void SetTextSkew(float yCenter, float skewScale)
{
SetShaderModifier(IMGUI_SHADER_MODIFIER_TEXT_SKEW);
SetOrigin({ 0.0f, yCenter });
SetScale({ skewScale, 1.0f });
}
void ResetTextSkew()
{
SetShaderModifier(IMGUI_SHADER_MODIFIER_NONE);
SetOrigin({ 0.0f, 0.0f });
SetScale({ 1.0f, 1.0f });
}
void SetHorizontalMarqueeFade(ImVec2 min, ImVec2 max, float fadeScale)
{
auto callbackData = AddImGuiCallback(ImGuiCallback::SetMarqueeFade);
callbackData->setMarqueeFade.boundsMin[0] = min.x;
callbackData->setMarqueeFade.boundsMin[1] = min.y;
callbackData->setMarqueeFade.boundsMax[0] = max.x;
callbackData->setMarqueeFade.boundsMax[1] = max.y;
SetShaderModifier(IMGUI_SHADER_MODIFIER_HORIZONTAL_MARQUEE_FADE);
SetScale({ fadeScale, 1.0f });
}
void SetVerticalMarqueeFade(ImVec2 min, ImVec2 max, float fadeScale)
{
auto callbackData = AddImGuiCallback(ImGuiCallback::SetMarqueeFade);
callbackData->setMarqueeFade.boundsMin[0] = min.x;
callbackData->setMarqueeFade.boundsMin[1] = min.y;
callbackData->setMarqueeFade.boundsMax[0] = max.x;
callbackData->setMarqueeFade.boundsMax[1] = max.y;
SetShaderModifier(IMGUI_SHADER_MODIFIER_VERTICAL_MARQUEE_FADE);
SetScale({ 1.0f, fadeScale });
}
void ResetMarqueeFade()
{
ResetGradient();
SetShaderModifier(IMGUI_SHADER_MODIFIER_NONE);
SetScale({ 1.0f, 1.0f });
}
void SetOutline(float outline)
{
auto callbackData = AddImGuiCallback(ImGuiCallback::SetOutline);
callbackData->setOutline.outline = outline;
}
void ResetOutline()
{
SetOutline(0.0f);
}
void SetProceduralOrigin(ImVec2 proceduralOrigin)
{
auto callbackData = AddImGuiCallback(ImGuiCallback::SetProceduralOrigin);
callbackData->setProceduralOrigin.proceduralOrigin[0] = proceduralOrigin.x;
callbackData->setProceduralOrigin.proceduralOrigin[1] = proceduralOrigin.y;
}
void ResetProceduralOrigin()
{
SetProceduralOrigin({ 0.0f, 0.0f });
}
void SetAdditive(bool enabled)
{
auto callbackData = AddImGuiCallback(ImGuiCallback::SetAdditive);
callbackData->setAdditive.enabled = enabled;
}
void ResetAdditive()
{
SetAdditive(false);
}
float Scale(float size)
{
return size * g_aspectRatioScale;
}
double ComputeLinearMotion(double duration, double offset, double total)
{
return std::clamp((ImGui::GetTime() - duration - offset / 60.0) / total * 60.0, 0.0, 1.0);
}
double ComputeMotion(double duration, double offset, double total)
{
return sqrt(ComputeLinearMotion(duration, offset, total));
}
void DrawPauseContainer(ImVec2 min, ImVec2 max, float alpha)
{
auto drawList = ImGui::GetForegroundDrawList();
auto commonWidth = Scale(35);
auto commonHeight = Scale(35);
auto bottomHeight = Scale(5);
auto tl = PIXELS_TO_UV_COORDS(128, 512, 0, 0, 35, 35);
auto tc = PIXELS_TO_UV_COORDS(128, 512, 51, 0, 5, 35);
auto tr = PIXELS_TO_UV_COORDS(128, 512, 70, 0, 35, 35);
auto cl = PIXELS_TO_UV_COORDS(128, 512, 0, 35, 35, 235);
auto cc = PIXELS_TO_UV_COORDS(128, 512, 51, 35, 5, 235);
auto cr = PIXELS_TO_UV_COORDS(128, 512, 70, 35, 35, 235);
auto bl = PIXELS_TO_UV_COORDS(128, 512, 0, 270, 35, 40);
auto bc = PIXELS_TO_UV_COORDS(128, 512, 51, 270, 5, 40);
auto br = PIXELS_TO_UV_COORDS(128, 512, 70, 270, 35, 40);
auto colour = IM_COL32(255, 255, 255, 255 * alpha);
drawList->AddImage(g_texGeneralWindow.get(), min, { min.x + commonWidth, min.y + commonHeight }, GET_UV_COORDS(tl), colour);
drawList->AddImage(g_texGeneralWindow.get(), { min.x + commonWidth, min.y }, { max.x - commonWidth, min.y + commonHeight }, GET_UV_COORDS(tc), colour);
drawList->AddImage(g_texGeneralWindow.get(), { max.x - commonWidth, min.y }, { max.x, min.y + commonHeight }, GET_UV_COORDS(tr), colour);
drawList->AddImage(g_texGeneralWindow.get(), { min.x, min.y + commonHeight }, { min.x + commonWidth, max.y - commonHeight }, GET_UV_COORDS(cl), colour);
drawList->AddImage(g_texGeneralWindow.get(), { min.x + commonWidth, min.y + commonHeight }, { max.x - commonWidth, max.y - commonHeight }, GET_UV_COORDS(cc), colour);
drawList->AddImage(g_texGeneralWindow.get(), { max.x - commonWidth, min.y + commonHeight }, { max.x, max.y - commonHeight }, GET_UV_COORDS(cr), colour);
drawList->AddImage(g_texGeneralWindow.get(), { min.x, max.y - commonHeight }, { min.x + commonWidth, max.y + bottomHeight }, GET_UV_COORDS(bl), colour);
drawList->AddImage(g_texGeneralWindow.get(), { min.x + commonWidth, max.y - commonHeight }, { max.x - commonWidth, max.y + bottomHeight }, GET_UV_COORDS(bc), colour);
drawList->AddImage(g_texGeneralWindow.get(), { max.x - commonWidth, max.y - commonHeight }, { max.x, max.y + bottomHeight }, GET_UV_COORDS(br), colour);
}
void DrawPauseHeaderContainer(ImVec2 min, ImVec2 max, float alpha)
{
auto drawList = ImGui::GetForegroundDrawList();
auto commonWidth = Scale(35);
auto left = PIXELS_TO_UV_COORDS(128, 512, 0, 314, 35, 60);
auto centre = PIXELS_TO_UV_COORDS(128, 512, 51, 314, 5, 60);
auto right = PIXELS_TO_UV_COORDS(128, 512, 70, 314, 35, 60);
auto colour = IM_COL32(255, 255, 255, 255 * alpha);
drawList->AddImage(g_texGeneralWindow.get(), min, { min.x + commonWidth, max.y }, GET_UV_COORDS(left), colour);
drawList->AddImage(g_texGeneralWindow.get(), { min.x + commonWidth, min.y }, { max.x - commonWidth, max.y }, GET_UV_COORDS(centre), colour);
drawList->AddImage(g_texGeneralWindow.get(), { max.x - commonWidth, min.y }, max, GET_UV_COORDS(right), colour);
}
void DrawTextBasic(const ImFont* font, float fontSize, const ImVec2& pos, ImU32 colour, const char* text)
{
auto drawList = ImGui::GetForegroundDrawList();
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 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);
drawList->PushClipRect(min, max, true);
if (textX <= position.x)
{
DrawRubyAnnotatedText
(
font,
fontSize,
FLT_MAX,
{ textX, position.y },
0.0f,
text,
[=](const char* str, ImVec2 pos)
{
DrawTextBasic(font, fontSize, pos, color, str);
},
[=](const char* str, float size, ImVec2 pos)
{
DrawTextBasic(font, size, pos, color, str);
}
);
}
if (textX + textSize.x < position.x)
{
DrawRubyAnnotatedText
(
font,
fontSize,
FLT_MAX,
{ textX + textSize.x + rectWidth, position.y },
0.0f,
text,
[=](const char* str, ImVec2 pos)
{
DrawTextBasic(font, fontSize, pos, color, str);
},
[=](const char* str, float size, ImVec2 pos)
{
DrawTextBasic(font, size, pos, color, str);
}
);
}
drawList->PopClipRect();
}
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 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);
drawList->PushClipRect(min, max, true);
if (textX <= pos.x)
DrawTextWithShadow(font, fontSize, { textX, pos.y }, colour, text, offset, radius, shadowColour);
if (textX + textSize.x < pos.x)
DrawTextWithShadow(font, fontSize, { textX + textSize.x + rectWidth, pos.y }, colour, text, offset, radius, shadowColour);
drawList->PopClipRect();
}
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();
SetOutline(outlineSize);
drawList->AddText(font, fontSize, pos, outlineColor, text);
ResetOutline();
if (shaderModifier != IMGUI_SHADER_MODIFIER_NONE)
SetShaderModifier(shaderModifier);
drawList->AddText(font, fontSize, pos, color, text);
if (shaderModifier != IMGUI_SHADER_MODIFIER_NONE)
SetShaderModifier(IMGUI_SHADER_MODIFIER_NONE);
}
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();
offset = Scale(offset);
SetOutline(radius);
drawList->AddText(font, fontSize, { pos.x + offset, pos.y + offset }, shadowColour, text);
ResetOutline();
drawList->AddText(font, fontSize, pos, colour, text, nullptr);
}
float CalcWidestTextSize(const ImFont* font, float fontSize, std::span<std::string> strs)
{
auto result = 0.0f;
for (auto& str : strs)
result = std::max(result, font->CalcTextSizeA(fontSize, FLT_MAX, 0, str.c_str()).x);
return result;
}
std::string Truncate(const std::string& input, size_t maxLength, bool useEllipsis, bool usePrefixEllipsis)
{
const std::string ellipsis = "...";
if (input.length() > maxLength)
{
if (useEllipsis && maxLength > ellipsis.length())
{
if (usePrefixEllipsis)
{
return ellipsis + input.substr(0, maxLength - ellipsis.length());
}
else
{
return input.substr(0, maxLength - ellipsis.length()) + ellipsis;
}
}
else
{
return input.substr(0, maxLength);
}
}
return 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;
size_t idx = 0;
while (input[idx] != '\0')
{
if (input[idx] == '[')
{
idx++;
currentMain.clear();
currentRuby.clear();
while (input[idx] != ':' && input[idx] != ']' && input[idx] != '\0')
{
currentMain += input[idx++];
}
if (input[idx] == ':')
{
idx++;
while (input[idx] != ']' && input[idx] != '\0')
{
currentRuby += input[idx++];
}
}
if (input[idx] == ']')
{
idx++;
}
if (!currentMain.empty() && !currentRuby.empty())
{
rubyMap[currentMain] = currentRuby;
}
output += currentMain;
}
else
{
output += input[idx++];
}
}
return { output, 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();
while (idx < length) {
bool matched = false;
for (const auto& [mainText, rubyText] : rubyMap)
{
if (wrappedText.substr(idx, mainText.length()) == mainText)
{
annotatedText += "[";
annotatedText += mainText;
annotatedText += ":";
annotatedText += rubyText;
annotatedText += "]";
idx += mainText.length();
matched = true;
break;
}
}
if (!matched)
{
annotatedText += wrappedText[idx++];
}
}
return annotatedText;
}
std::vector<std::string> Split(const char* strStart, const ImFont *font, float fontSize, float maxWidth)
{
if (!strStart)
return {};
std::vector<std::string> result;
float textWidth = 0.0f;
float lineWidth = 0.0f;
const float scale = fontSize / font->FontSize;
const char *str = strStart;
const char *strEnd = strStart + strlen(strStart);
const char *lineStart = strStart;
const bool wordWrapEnabled = (maxWidth > 0.0f);
const char *wordWrapEOL = nullptr;
while (*str != 0)
{
if (wordWrapEnabled)
{
if (wordWrapEOL == nullptr)
{
wordWrapEOL = font->CalcWordWrapPositionA(scale, str, strEnd, maxWidth - lineWidth);
}
if (str >= wordWrapEOL)
{
if (textWidth < lineWidth)
textWidth = lineWidth;
result.emplace_back(lineStart, str);
lineWidth = 0.0f;
wordWrapEOL = nullptr;
while (str < strEnd && ImCharIsBlankA(*str))
str++;
if (*str == '\n')
str++;
lineStart = str;
continue;
}
}
const char *prevStr = str;
unsigned int c = (unsigned int)*str;
if (c < 0x80)
str += 1;
else
str += ImTextCharFromUtf8(&c, str, strEnd);
if (c < 32)
{
if (c == '\n')
{
result.emplace_back(lineStart, str - 1);
lineStart = str;
textWidth = ImMax(textWidth, lineWidth);
lineWidth = 0.0f;
continue;
}
if (c == '\r')
{
lineStart = str;
continue;
}
}
const float charWidth = ((int)c < font->IndexAdvanceX.Size ? font->IndexAdvanceX.Data[c] : font->FallbackAdvanceX) * scale;
if (lineWidth + charWidth >= maxWidth)
{
str = prevStr;
break;
}
lineWidth += charWidth;
}
if (str != lineStart)
{
result.emplace_back(lineStart, str);
}
return result;
}
Paragraph CalculateAnnotatedParagraph(const std::vector<std::string>& lines)
{
Paragraph result;
for (const auto& line : lines)
{
std::vector<TextSegment> segments;
size_t pos = 0;
size_t start = 0;
while ((start = line.find('[', pos)) != std::string::npos)
{
size_t end = line.find(']', start);
if (end == std::string::npos)
break;
size_t colon = line.find(':', start);
if (colon != std::string::npos && colon < end)
{
if (start != pos)
{
segments.push_back({ false, line.substr(pos, start - pos), "" });
}
segments.push_back({ true, line.substr(start + 1, colon - start - 1), line.substr(colon + 1, end - colon - 1) });
result.annotated = true;
pos = end + 1;
}
else
{
pos = start + 1;
}
}
if (pos < line.size())
{
segments.push_back({ false, line.substr(pos), "" });
}
result.lines.push_back(segments);
}
return result;
}
std::vector<std::string> RemoveAnnotationFromParagraph(const std::vector<std::string>& lines)
{
std::vector<std::string> result;
const auto paragraph = CalculateAnnotatedParagraph(lines);
for (auto& annotatedLine : paragraph.lines)
{
std::string annotationRemovedLine = "";
for (const auto& segment : annotatedLine)
{
annotationRemovedLine += segment.text;
}
result.push_back(annotationRemovedLine);
}
return result;
}
std::string RemoveAnnotationFromParagraphLine(const std::vector<TextSegment>& annotatedLine)
{
std::string result = "";
for (auto& segment : annotatedLine)
{
result += segment.text;
}
return result;
}
ImVec2 MeasureCentredParagraph(const ImFont* font, float fontSize, float lineMargin, const std::vector<std::string>& lines)
{
auto x = 0.0f;
auto y = 0.0f;
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, "");
x = std::max(x, textSize.x);
y += textSize.y + Scale(lineMargin);
if (paragraph.annotated)
{
y += annotationSize.y;
}
}
return { x, y };
}
ImVec2 MeasureCentredParagraph(const ImFont* font, float fontSize, float maxWidth, float lineMargin, const char* text)
{
return MeasureCentredParagraph(font, fontSize, lineMargin, Split(text, font, fontSize, maxWidth));
}
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;
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;
const auto paragraph = CalculateAnnotatedParagraph(lines);
for (const auto& annotatedLine : paragraph.lines)
{
const auto annotationRemovedLine = RemoveAnnotationFromParagraphLine(annotatedLine);
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;
if (isCentred)
{
textX = annotationRemovedLine.starts_with("- ")
? pos.x - paragraphSize.x / 2
: pos.x - textSize.x / 2;
textY = pos.y - paragraphSize.y / 2 + offsetY;
}
for (const auto& segment : annotatedLine)
{
textSize = font->CalcTextSizeA(fontSize, FLT_MAX, 0, segment.text.c_str());
if (segment.annotated)
{
annotationSize = font->CalcTextSizeA(annotationFontSize, FLT_MAX, 0, segment.annotation.c_str());
float annotationX = textX + (textSize.x - annotationSize.x) / 2.0f;
annotationDrawMethod(segment.annotation.c_str(), annotationFontSize, { annotationX, textY - annotationFontSize });
}
drawMethod(segment.text.c_str(), { textX, textY });
textX += textSize.x;
}
offsetY += textSize.y + Scale(lineMargin);
if (paragraph.annotated)
{
offsetY += annotationSize.y;
}
}
}
float Lerp(float a, float b, float t)
{
return a + (b - a) * t;
}
float Cubic(float a, float b, float t)
{
return a + (b - a) * (t * t * t);
}
float Hermite(float a, float b, float t)
{
return a + (b - a) * (t * t * (3 - 2 * t));
}
ImVec2 Lerp(const ImVec2& a, const ImVec2& b, float t)
{
return { a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t };
}
ImU32 ColourLerp(ImU32 c0, ImU32 c1, float t)
{
auto a = ImGui::ColorConvertU32ToFloat4(c0);
auto b = ImGui::ColorConvertU32ToFloat4(c1);
ImVec4 result;
result.x = a.x + (b.x - a.x) * t;
result.y = a.y + (b.y - a.y) * t;
result.z = a.z + (b.z - a.z) * t;
result.w = a.w + (b.w - a.w) * t;
return ImGui::ColorConvertFloat4ToU32(result);
}
void DrawVersionString(const ImFont* font, const ImU32 col)
{
auto drawList = ImGui::GetForegroundDrawList();
auto& res = ImGui::GetIO().DisplaySize;
auto fontSize = Scale(12);
auto textMargin = Scale(2);
auto textSize = font->CalcTextSizeA(fontSize, FLT_MAX, 0, g_versionString);
drawList->AddText(font, fontSize, { res.x - textSize.x - textMargin, res.y - textSize.y - textMargin }, col, g_versionString);
}
void DrawSelectionContainer(ImVec2 min, ImVec2 max, bool fadeTop)
{
auto drawList = ImGui::GetForegroundDrawList();
static auto breatheStart = ImGui::GetTime();
auto alpha = BREATHE_MOTION(1.0f, 0.55f, breatheStart, 0.92f);
auto colour = IM_COL32(255, 255, 255, 255 * alpha);
auto commonWidth = Scale(11);
auto commonHeight = Scale(24);
if (fadeTop)
{
auto left = PIXELS_TO_UV_COORDS(64, 64, 0, 0, 11, 50);
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);
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);
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);
}
void DrawToggleLight(ImVec2 pos, bool isEnabled, float alpha)
{
auto drawList = ImGui::GetForegroundDrawList();
auto lightSize = Scale(14);
auto lightCol = IM_COL32(255, 255, 255, 255 * alpha);
ImVec2 min = { pos.x, pos.y };
ImVec2 max = { min.x + lightSize, min.y + lightSize };
if (isEnabled)
{
auto lightGlowSize = Scale(24);
auto lightGlowUVs = PIXELS_TO_UV_COORDS(64, 64, 31, 31, 32, 32);
ImVec2 lightGlowMin = { min.x - (lightGlowSize / 2) + Scale(2), min.y - lightGlowSize / 2 };
ImVec2 lightGlowMax = { min.x + lightGlowSize, min.y + lightGlowSize };
SetAdditive(true);
drawList->AddImage(g_texLight.get(), lightGlowMin, lightGlowMax, GET_UV_COORDS(lightGlowUVs), IM_COL32(255, 255, 0, 127 * alpha));
SetAdditive(false);
auto lightOnUVs = PIXELS_TO_UV_COORDS(64, 64, 14, 0, 14, 14);
drawList->AddImage(g_texLight.get(), min, max, GET_UV_COORDS(lightOnUVs), lightCol);
}
else
{
auto lightOffUVs = PIXELS_TO_UV_COORDS(64, 64, 0, 0, 14, 14);
drawList->AddImage(g_texLight.get(), min, max, GET_UV_COORDS(lightOffUVs), lightCol);
}
}
+68 -441
View File
@@ -2,9 +2,6 @@
#include <gpu/imgui/imgui_common.h> #include <gpu/imgui/imgui_common.h>
#include <gpu/video.h> #include <gpu/video.h>
#include <app.h>
#include <version.h>
#include <patches/aspect_ratio_patches.h>
#define PIXELS_TO_UV_COORDS(textureWidth, textureHeight, x, y, width, height) \ #define PIXELS_TO_UV_COORDS(textureWidth, textureHeight, x, y, width, height) \
std::make_tuple(ImVec2((float)x / (float)textureWidth, (float)y / (float)textureHeight), \ std::make_tuple(ImVec2((float)x / (float)textureWidth, (float)y / (float)textureHeight), \
@@ -15,441 +12,71 @@
#define CENTRE_TEXT_HORZ(min, max, textSize) min.x + ((max.x - min.x) - textSize.x) / 2 #define CENTRE_TEXT_HORZ(min, max, textSize) min.x + ((max.x - min.x) - textSize.x) / 2
#define CENTRE_TEXT_VERT(min, max, textSize) min.y + ((max.y - min.y) - textSize.y) / 2 #define CENTRE_TEXT_VERT(min, max, textSize) min.y + ((max.y - min.y) - textSize.y) / 2
inline void SetGradient(const ImVec2& min, const ImVec2& max, ImU32 top, ImU32 bottom) #define BREATHE_MOTION(start, end, time, rate) Lerp(start, end, (sin((ImGui::GetTime() - time) * (2.0f * M_PI / rate)) + 1.0f) / 2.0f)
{
auto callbackData = AddImGuiCallback(ImGuiCallback::SetGradient); constexpr float ANNOTATION_FONT_SIZE_MODIFIER = 0.6f;
callbackData->setGradient.boundsMin[0] = min.x;
callbackData->setGradient.boundsMin[1] = min.y; extern std::unique_ptr<GuestTexture> g_texGeneralWindow;
callbackData->setGradient.boundsMax[0] = max.x; extern std::unique_ptr<GuestTexture> g_texLight;
callbackData->setGradient.boundsMax[1] = max.y; extern std::unique_ptr<GuestTexture> g_texSelectFade;
callbackData->setGradient.gradientTop = top; extern std::unique_ptr<GuestTexture> g_texSelectFill;
callbackData->setGradient.gradientBottom = bottom;
} struct TextSegment {
bool annotated;
inline void ResetGradient() std::string text;
{ std::string annotation;
auto callbackData = AddImGuiCallback(ImGuiCallback::SetGradient); };
memset(&callbackData->setGradient, 0, sizeof(callbackData->setGradient));
} struct Paragraph {
bool annotated = false;
inline void SetShaderModifier(uint32_t shaderModifier) std::vector<std::vector<TextSegment>> lines;
{ };
auto callbackData = AddImGuiCallback(ImGuiCallback::SetShaderModifier);
callbackData->setShaderModifier.shaderModifier = shaderModifier; void InitImGuiUtils();
}
void SetGradient(const ImVec2& min, const ImVec2& max, ImU32 top, ImU32 bottom);
inline void SetOrigin(ImVec2 origin) void SetGradient(const ImVec2& min, const ImVec2& max, ImU32 topLeft, ImU32 topRight, ImU32 bottomRight, ImU32 bottomLeft);
{ void ResetGradient();
auto callbackData = AddImGuiCallback(ImGuiCallback::SetOrigin); void SetShaderModifier(uint32_t shaderModifier);
callbackData->setOrigin.origin[0] = origin.x; void SetOrigin(ImVec2 origin);
callbackData->setOrigin.origin[1] = origin.y; void SetScale(ImVec2 scale);
} void SetTextSkew(float yCenter, float skewScale);
void ResetTextSkew();
inline void SetScale(ImVec2 scale) void SetHorizontalMarqueeFade(ImVec2 min, ImVec2 max, float fadeScale);
{ void SetVerticalMarqueeFade(ImVec2 min, ImVec2 max, float fadeScale);
auto callbackData = AddImGuiCallback(ImGuiCallback::SetScale); void ResetMarqueeFade();
callbackData->setScale.scale[0] = scale.x; void SetOutline(float outline);
callbackData->setScale.scale[1] = scale.y; void ResetOutline();
} void SetProceduralOrigin(ImVec2 proceduralOrigin);
void ResetProceduralOrigin();
inline void SetTextSkew(float yCenter, float skewScale) void SetAdditive(bool enabled);
{ void ResetAdditive();
SetShaderModifier(IMGUI_SHADER_MODIFIER_TEXT_SKEW); float Scale(float size);
SetOrigin({ 0.0f, yCenter }); double ComputeLinearMotion(double duration, double offset, double total);
SetScale({ skewScale, 1.0f }); double ComputeMotion(double duration, double offset, double total);
} void DrawPauseContainer(ImVec2 min, ImVec2 max, float alpha = 1);
void DrawTextBasic(const ImFont* font, float fontSize, const ImVec2& pos, ImU32 colour, const char* text);
inline void ResetTextSkew() void DrawPauseHeaderContainer(ImVec2 min, ImVec2 max, float alpha = 1);
{ 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);
SetShaderModifier(IMGUI_SHADER_MODIFIER_NONE); 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 = 2.0f, float radius = 1.0f, ImU32 shadowColour = IM_COL32(0, 0, 0, 255));
SetOrigin({ 0.0f, 0.0f }); void DrawTextWithOutline(const ImFont* font, float fontSize, const ImVec2& pos, ImU32 color, const char* text, float outlineSize, ImU32 outlineColor, uint32_t shaderModifier = IMGUI_SHADER_MODIFIER_NONE);
SetScale({ 1.0f, 1.0f }); void DrawTextWithShadow(const ImFont* font, float fontSize, const ImVec2& pos, ImU32 colour, const char* text, float offset = 2.0f, float radius = 1.0f, ImU32 shadowColour = IM_COL32(0, 0, 0, 255));
} float CalcWidestTextSize(const ImFont* font, float fontSize, std::span<std::string> strs);
std::string Truncate(const std::string& input, size_t maxLength, bool useEllipsis = true, bool usePrefixEllipsis = false);
inline void SetMarqueeFade(ImVec2 min, ImVec2 max, float fadeScale) std::pair<std::string, std::map<std::string, std::string>> RemoveRubyAnnotations(const char* input);
{ std::string ReAddRubyAnnotations(const std::string_view& wrappedText, const std::map<std::string, std::string>& rubyMap);
auto callbackData = AddImGuiCallback(ImGuiCallback::SetMarqueeFade); std::vector<std::string> Split(const char* strStart, const ImFont* font, float fontSize, float maxWidth);
callbackData->setMarqueeFade.boundsMin[0] = min.x; Paragraph CalculateAnnotatedParagraph(const std::vector<std::string>& lines);
callbackData->setMarqueeFade.boundsMin[1] = min.y; std::vector<std::string> RemoveAnnotationFromParagraph(const std::vector<std::string>& lines);
callbackData->setMarqueeFade.boundsMax[0] = max.x; std::string RemoveAnnotationFromParagraphLine(const std::vector<TextSegment>& annotatedLine);
callbackData->setMarqueeFade.boundsMax[1] = max.y; ImVec2 MeasureCentredParagraph(const ImFont* font, float fontSize, float lineMargin, const std::vector<std::string>& lines);
ImVec2 MeasureCentredParagraph(const ImFont* font, float fontSize, float maxWidth, float lineMargin, const char* text);
SetShaderModifier(IMGUI_SHADER_MODIFIER_MARQUEE_FADE); void DrawRubyAnnotatedText(const ImFont* font, float fontSize, float maxWidth, const ImVec2& pos, float lineMargin, const char* text, std::function<void(const char*, ImVec2)> drawMethod, std::function<void(const char*, float, ImVec2)> annotationDrawMethod, bool isCentred = false);
SetScale({ fadeScale, 1.0f }); float Lerp(float a, float b, float t);
} float Cubic(float a, float b, float t);
float Hermite(float a, float b, float t);
inline void ResetMarqueeFade() ImVec2 Lerp(const ImVec2& a, const ImVec2& b, float t);
{ ImU32 ColourLerp(ImU32 c0, ImU32 c1, float t);
ResetGradient(); void DrawVersionString(const ImFont* font, const ImU32 col = IM_COL32(255, 255, 255, 70));
SetShaderModifier(IMGUI_SHADER_MODIFIER_NONE); void DrawSelectionContainer(ImVec2 min, ImVec2 max, bool fadeTop = false);
SetScale({ 1.0f, 1.0f }); void DrawToggleLight(ImVec2 pos, bool isEnabled, float alpha = 1.0f);
}
inline void SetOutline(float outline)
{
auto callbackData = AddImGuiCallback(ImGuiCallback::SetOutline);
callbackData->setOutline.outline = outline;
}
inline void ResetOutline()
{
SetOutline(0.0f);
}
inline void SetProceduralOrigin(ImVec2 proceduralOrigin)
{
auto callbackData = AddImGuiCallback(ImGuiCallback::SetProceduralOrigin);
callbackData->setProceduralOrigin.proceduralOrigin[0] = proceduralOrigin.x;
callbackData->setProceduralOrigin.proceduralOrigin[1] = proceduralOrigin.y;
}
inline void ResetProceduralOrigin()
{
SetProceduralOrigin({ 0.0f, 0.0f });
}
inline void SetAdditive(bool enabled)
{
auto callbackData = AddImGuiCallback(ImGuiCallback::SetAdditive);
callbackData->setAdditive.enabled = enabled;
}
inline void ResetAdditive()
{
SetAdditive(false);
}
inline float Scale(float size)
{
return size * g_aspectRatioScale;
}
inline double ComputeMotion(double duration, double offset, double total)
{
return sqrt(std::clamp((ImGui::GetTime() - duration - offset / 60.0) / total * 60.0, 0.0, 1.0));
}
inline void DrawPauseContainer(GuestTexture* texture, ImVec2 min, ImVec2 max, float alpha = 1)
{
auto drawList = ImGui::GetForegroundDrawList();
auto commonWidth = Scale(35);
auto commonHeight = Scale(35);
auto bottomHeight = Scale(5);
auto tl = PIXELS_TO_UV_COORDS(128, 512, 0, 0, 35, 35);
auto tc = PIXELS_TO_UV_COORDS(128, 512, 51, 0, 5, 35);
auto tr = PIXELS_TO_UV_COORDS(128, 512, 70, 0, 35, 35);
auto cl = PIXELS_TO_UV_COORDS(128, 512, 0, 35, 35, 235);
auto cc = PIXELS_TO_UV_COORDS(128, 512, 51, 35, 5, 235);
auto cr = PIXELS_TO_UV_COORDS(128, 512, 70, 35, 35, 235);
auto bl = PIXELS_TO_UV_COORDS(128, 512, 0, 270, 35, 40);
auto bc = PIXELS_TO_UV_COORDS(128, 512, 51, 270, 5, 40);
auto br = PIXELS_TO_UV_COORDS(128, 512, 70, 270, 35, 40);
auto colour = IM_COL32(255, 255, 255, 255 * alpha);
drawList->AddImage(texture, min, { min.x + commonWidth, min.y + commonHeight }, GET_UV_COORDS(tl), colour);
drawList->AddImage(texture, { min.x + commonWidth, min.y }, { max.x - commonWidth, min.y + commonHeight }, GET_UV_COORDS(tc), colour);
drawList->AddImage(texture, { max.x - commonWidth, min.y }, { max.x, min.y + commonHeight }, GET_UV_COORDS(tr), colour);
drawList->AddImage(texture, { min.x, min.y + commonHeight }, { min.x + commonWidth, max.y - commonHeight }, GET_UV_COORDS(cl), colour);
drawList->AddImage(texture, { min.x + commonWidth, min.y + commonHeight }, { max.x - commonWidth, max.y - commonHeight }, GET_UV_COORDS(cc), colour);
drawList->AddImage(texture, { max.x - commonWidth, min.y + commonHeight }, { max.x, max.y - commonHeight }, GET_UV_COORDS(cr), colour);
drawList->AddImage(texture, { min.x, max.y - commonHeight }, { min.x + commonWidth, max.y + bottomHeight }, GET_UV_COORDS(bl), colour);
drawList->AddImage(texture, { min.x + commonWidth, max.y - commonHeight }, { max.x - commonWidth, max.y + bottomHeight }, GET_UV_COORDS(bc), colour);
drawList->AddImage(texture, { max.x - commonWidth, max.y - commonHeight }, { max.x, max.y + bottomHeight }, GET_UV_COORDS(br), colour);
}
inline void DrawPauseHeaderContainer(GuestTexture* texture, ImVec2 min, ImVec2 max, float alpha = 1)
{
auto drawList = ImGui::GetForegroundDrawList();
auto commonWidth = Scale(35);
auto left = PIXELS_TO_UV_COORDS(128, 512, 0, 314, 35, 60);
auto centre = PIXELS_TO_UV_COORDS(128, 512, 51, 314, 5, 60);
auto right = PIXELS_TO_UV_COORDS(128, 512, 70, 314, 35, 60);
auto colour = IM_COL32(255, 255, 255, 255 * alpha);
drawList->AddImage(texture, min, { min.x + commonWidth, max.y }, GET_UV_COORDS(left), colour);
drawList->AddImage(texture, { min.x + commonWidth, min.y }, { max.x - commonWidth, max.y }, GET_UV_COORDS(centre), colour);
drawList->AddImage(texture, { max.x - commonWidth, min.y }, max, GET_UV_COORDS(right), colour);
}
inline void DrawTextWithMarquee(const ImFont* font, float fontSize, const ImVec2& pos, const ImVec2& min, const ImVec2& max, ImU32 color, const char* text, double time, double delay, double speed)
{
auto drawList = ImGui::GetForegroundDrawList();
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);
drawList->PushClipRect(min, max, true);
if (textX <= pos.x)
drawList->AddText(font, fontSize, { textX, pos.y }, color, text);
if (textX + textSize.x < pos.x)
drawList->AddText(font, fontSize, { textX + textSize.x + rectWidth, pos.y }, color, text);
drawList->PopClipRect();
}
inline void DrawTextWithOutline(const ImFont* font, float fontSize, const ImVec2& pos, ImU32 color, const char* text, float outlineSize, ImU32 outlineColor, uint32_t shaderModifier = IMGUI_SHADER_MODIFIER_NONE)
{
auto drawList = ImGui::GetForegroundDrawList();
SetOutline(outlineSize);
drawList->AddText(font, fontSize, pos, outlineColor, text);
ResetOutline();
if (shaderModifier != IMGUI_SHADER_MODIFIER_NONE)
SetShaderModifier(shaderModifier);
drawList->AddText(font, fontSize, pos, color, text);
if (shaderModifier != IMGUI_SHADER_MODIFIER_NONE)
SetShaderModifier(IMGUI_SHADER_MODIFIER_NONE);
}
inline void DrawTextWithShadow(const ImFont* font, float fontSize, const ImVec2& pos, ImU32 colour, const char* text, float offset = 2.0f, float radius = 1.0f, ImU32 shadowColour = IM_COL32(0, 0, 0, 255))
{
auto drawList = ImGui::GetForegroundDrawList();
offset = Scale(offset);
SetOutline(radius);
drawList->AddText(font, fontSize, { pos.x + offset, pos.y + offset }, shadowColour, text);
ResetOutline();
drawList->AddText(font, fontSize, pos, colour, text, nullptr);
}
inline float CalcWidestTextSize(const ImFont* font, float fontSize, std::span<std::string> strs)
{
auto result = 0.0f;
for (auto& str : strs)
result = std::max(result, font->CalcTextSizeA(fontSize, FLT_MAX, 0, str.c_str()).x);
return result;
}
inline std::string Truncate(const std::string& input, size_t maxLength, bool useEllipsis = true, bool usePrefixEllipsis = false)
{
const std::string ellipsis = "...";
if (input.length() > maxLength)
{
if (useEllipsis && maxLength > ellipsis.length())
{
if (usePrefixEllipsis)
{
return ellipsis + input.substr(0, maxLength - ellipsis.length());
}
else
{
return input.substr(0, maxLength - ellipsis.length()) + ellipsis;
}
}
else
{
return input.substr(0, maxLength);
}
}
return input;
}
inline std::vector<std::string> Split(const char* strStart, const ImFont *font, float fontSize, float maxWidth)
{
if (!strStart)
return {};
std::vector<std::string> result;
float textWidth = 0.0f;
float lineWidth = 0.0f;
const float scale = fontSize / font->FontSize;
const char *str = strStart;
const char *strEnd = strStart + strlen(strStart);
const char *lineStart = strStart;
const bool wordWrapEnabled = (maxWidth > 0.0f);
const char *wordWrapEOL = nullptr;
while (*str != 0)
{
if (wordWrapEnabled)
{
if (wordWrapEOL == nullptr)
{
wordWrapEOL = font->CalcWordWrapPositionA(scale, str, strEnd, maxWidth - lineWidth);
}
if (str >= wordWrapEOL)
{
if (textWidth < lineWidth)
textWidth = lineWidth;
result.emplace_back(lineStart, str);
lineWidth = 0.0f;
wordWrapEOL = nullptr;
while (str < strEnd && ImCharIsBlankA(*str))
str++;
if (*str == '\n')
str++;
lineStart = str;
continue;
}
}
const char *prevStr = str;
unsigned int c = (unsigned int)*str;
if (c < 0x80)
str += 1;
else
str += ImTextCharFromUtf8(&c, str, strEnd);
if (c < 32)
{
if (c == '\n')
{
result.emplace_back(lineStart, str - 1);
lineStart = str;
textWidth = ImMax(textWidth, lineWidth);
lineWidth = 0.0f;
continue;
}
if (c == '\r')
{
lineStart = str;
continue;
}
}
const float charWidth = ((int)c < font->IndexAdvanceX.Size ? font->IndexAdvanceX.Data[c] : font->FallbackAdvanceX) * scale;
if (lineWidth + charWidth >= maxWidth)
{
str = prevStr;
break;
}
lineWidth += charWidth;
}
if (str != lineStart)
{
result.emplace_back(lineStart, str);
}
return result;
}
inline ImVec2 MeasureCentredParagraph(const ImFont* font, float fontSize, float lineMargin, std::vector<std::string> lines)
{
auto x = 0.0f;
auto y = 0.0f;
for (auto& str : lines)
{
auto textSize = font->CalcTextSizeA(fontSize, FLT_MAX, 0, str.c_str());
x = std::max(x, textSize.x);
y += textSize.y + Scale(lineMargin);
}
return { x, y };
}
inline ImVec2 MeasureCentredParagraph(const ImFont* font, float fontSize, float maxWidth, float lineMargin, const char* text)
{
return MeasureCentredParagraph(font, fontSize, lineMargin, Split(text, font, fontSize, maxWidth));
}
inline void DrawCentredParagraph(const ImFont* font, float fontSize, float maxWidth, const ImVec2& centre, float lineMargin, const char* text, std::function<void(const char*, ImVec2)> drawMethod)
{
auto lines = Split(text, font, fontSize, maxWidth);
auto paragraphSize = MeasureCentredParagraph(font, fontSize, lineMargin, lines);
auto offsetY = 0.0f;
for (int i = 0; i < lines.size(); i++)
{
auto& str = lines[i];
auto textSize = font->CalcTextSizeA(fontSize, FLT_MAX, 0, str.c_str());
auto textX = str.starts_with("- ")
? centre.x - paragraphSize.x / 2
: centre.x - textSize.x / 2;
auto textY = centre.y - paragraphSize.y / 2 + offsetY;
drawMethod(str.c_str(), { textX, textY });
offsetY += textSize.y + Scale(lineMargin);
}
}
inline 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 = 2.0f, float radius = 1.0f, ImU32 shadowColour = IM_COL32(0, 0, 0, 255))
{
auto drawList = ImGui::GetForegroundDrawList();
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);
drawList->PushClipRect(min, max, true);
if (textX <= pos.x)
DrawTextWithShadow(font, fontSize, { textX, pos.y }, colour, text, offset, radius, shadowColour);
if (textX + textSize.x < pos.x)
DrawTextWithShadow(font, fontSize, { textX + textSize.x + rectWidth, pos.y }, colour, text, offset, radius, shadowColour);
drawList->PopClipRect();
}
inline float Lerp(float a, float b, float t)
{
return a + (b - a) * t;
}
inline float Cubic(float a, float b, float t)
{
return a + (b - a) * (t * t * t);
}
inline float Hermite(float a, float b, float t)
{
return a + (b - a) * (t * t * (3 - 2 * t));
}
inline ImVec2 Lerp(const ImVec2& a, const ImVec2& b, float t)
{
return { a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t };
}
inline ImU32 ColourLerp(ImU32 c0, ImU32 c1, float t)
{
auto a = ImGui::ColorConvertU32ToFloat4(c0);
auto b = ImGui::ColorConvertU32ToFloat4(c1);
ImVec4 result;
result.x = a.x + (b.x - a.x) * t;
result.y = a.y + (b.y - a.y) * t;
result.z = a.z + (b.z - a.z) * t;
result.w = a.w + (b.w - a.w) * t;
return ImGui::ColorConvertFloat4ToU32(result);
}
inline void DrawVersionString(const ImFont* font, const ImU32 col = IM_COL32(255, 255, 255, 70))
{
auto drawList = ImGui::GetForegroundDrawList();
auto& res = ImGui::GetIO().DisplaySize;
auto fontSize = Scale(12);
auto textMargin = Scale(2);
auto textSize = font->CalcTextSizeA(fontSize, FLT_MAX, 0, g_versionString);
drawList->AddText(font, fontSize, { res.x - textSize.x - textMargin, res.y - textSize.y - textMargin }, col, g_versionString);
}
+156 -65
View File
@@ -12,10 +12,10 @@
#include <ui/imgui_utils.h> #include <ui/imgui_utils.h>
#include <ui/button_guide.h> #include <ui/button_guide.h>
#include <ui/message_window.h> #include <ui/message_window.h>
#include <ui/sdl_listener.h>
#include <ui/game_window.h> #include <ui/game_window.h>
#include <decompressor.h> #include <decompressor.h>
#include <exports.h> #include <exports.h>
#include <sdl_listener.h>
#include <res/images/common/hedge-dev.dds.h> #include <res/images/common/hedge-dev.dds.h>
#include <res/images/installer/install_001.dds.h> #include <res/images/installer/install_001.dds.h>
@@ -29,6 +29,7 @@
#include <res/images/installer/miles_electric_icon.dds.h> #include <res/images/installer/miles_electric_icon.dds.h>
#include <res/images/installer/arrow_circle.dds.h> #include <res/images/installer/arrow_circle.dds.h>
#include <res/images/installer/pulse_install.dds.h> #include <res/images/installer/pulse_install.dds.h>
#include <res/credits.h>
// One Shot Animations Constants // One Shot Animations Constants
static constexpr double SCANLINES_ANIMATION_TIME = 0.0; static constexpr double SCANLINES_ANIMATION_TIME = 0.0;
@@ -64,19 +65,19 @@ static constexpr double PULSE_ANIMATION_LOOP_SPEED = 1.5;
static constexpr double PULSE_ANIMATION_LOOP_DELAY = 0.5; static constexpr double PULSE_ANIMATION_LOOP_DELAY = 0.5;
static constexpr double PULSE_ANIMATION_LOOP_FADE_HIGH_POINT = 0.5; static constexpr double PULSE_ANIMATION_LOOP_FADE_HIGH_POINT = 0.5;
constexpr float IMAGE_X = 165.0f; constexpr float IMAGE_X = 161.5f;
constexpr float IMAGE_Y = 106.0f; constexpr float IMAGE_Y = 103.5f;
constexpr float IMAGE_WIDTH = 512.0f; constexpr float IMAGE_WIDTH = 512.0f;
constexpr float IMAGE_HEIGHT = 512.0f; constexpr float IMAGE_HEIGHT = 512.0f;
constexpr float CONTAINER_X = 510.0f; constexpr float CONTAINER_X = 513.0f;
constexpr float CONTAINER_Y = 225.0f; constexpr float CONTAINER_Y = 226.0f;
constexpr float CONTAINER_WIDTH = 528.0f; constexpr float CONTAINER_WIDTH = 526.5f;
constexpr float CONTAINER_HEIGHT = 245.0f; constexpr float CONTAINER_HEIGHT = 246.0f;
constexpr float SIDE_CONTAINER_WIDTH = CONTAINER_WIDTH / 2.0f; constexpr float SIDE_CONTAINER_WIDTH = CONTAINER_WIDTH / 2.0f;
constexpr float BOTTOM_X_GAP = 4.0f; constexpr float BOTTOM_X_GAP = 4.0f;
constexpr float BOTTOM_Y_GAP = 4.0f; constexpr float BOTTOM_Y_GAP = 6.0f;
constexpr float CONTAINER_BUTTON_WIDTH = 250.0f; constexpr float CONTAINER_BUTTON_WIDTH = 250.0f;
constexpr float CONTAINER_BUTTON_GAP = 9.0f; constexpr float CONTAINER_BUTTON_GAP = 9.0f;
constexpr float BUTTON_HEIGHT = 22.0f; constexpr float BUTTON_HEIGHT = 22.0f;
@@ -151,24 +152,25 @@ static int g_currentCursorIndex = -1;
static int g_currentCursorDefault = 0; static int g_currentCursorDefault = 0;
static bool g_currentCursorAccepted = false; static bool g_currentCursorAccepted = false;
static std::vector<std::pair<ImVec2, ImVec2>> g_currentCursorRects; static std::vector<std::pair<ImVec2, ImVec2>> g_currentCursorRects;
static std::string g_creditsStr;
class SDLEventListenerForInstaller : public SDLEventListener class SDLEventListenerForInstaller : public SDLEventListener
{ {
public: public:
void OnSDLEvent(SDL_Event *event) override void OnSDLEvent(SDL_Event *event) override
{ {
if (!InstallerWizard::s_isVisible || !g_currentMessagePrompt.empty() || g_currentPickerVisible || !hid::IsInputAllowed())
return;
constexpr float AxisValueRange = 32767.0f; constexpr float AxisValueRange = 32767.0f;
constexpr float AxisTapRange = 0.5f; constexpr float AxisTapRange = 0.5f;
if (!InstallerWizard::s_isVisible || !g_currentMessagePrompt.empty() || g_currentPickerVisible)
{
return;
}
int newCursorIndex = -1; int newCursorIndex = -1;
ImVec2 tapDirection = {}; ImVec2 tapDirection = {};
switch (event->type) switch (event->type)
{ {
case SDL_KEYDOWN: case SDL_KEYDOWN:
{
switch (event->key.keysym.scancode) switch (event->key.keysym.scancode)
{ {
case SDL_SCANCODE_LEFT: case SDL_SCANCODE_LEFT:
@@ -186,7 +188,10 @@ public:
} }
break; break;
}
case SDL_CONTROLLERBUTTONDOWN: case SDL_CONTROLLERBUTTONDOWN:
{
switch (event->cbutton.button) switch (event->cbutton.button)
{ {
case SDL_CONTROLLER_BUTTON_DPAD_LEFT: case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
@@ -207,6 +212,8 @@ public:
} }
break; break;
}
case SDL_CONTROLLERAXISMOTION: case SDL_CONTROLLERAXISMOTION:
{ {
if (event->caxis.axis < 2) if (event->caxis.axis < 2)
@@ -225,29 +232,27 @@ public:
break; break;
} }
case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEMOTION: case SDL_MOUSEMOTION:
{ {
for (size_t i = 0; i < g_currentCursorRects.size(); i++) for (size_t i = 0; i < g_currentCursorRects.size(); i++)
{ {
auto &currentRect = g_currentCursorRects[i]; auto &currentRect = g_currentCursorRects[i];
if (ImGui::IsMouseHoveringRect(currentRect.first, currentRect.second, false)) if (ImGui::IsMouseHoveringRect(currentRect.first, currentRect.second, false))
{ {
newCursorIndex = int(i); newCursorIndex = int(i);
if (event->type == SDL_MOUSEBUTTONDOWN && event->button.button == SDL_BUTTON_LEFT) if (event->type == SDL_MOUSEBUTTONDOWN && event->button.button == SDL_BUTTON_LEFT)
{
g_currentCursorAccepted = true; g_currentCursorAccepted = true;
}
break; break;
} }
} }
if (newCursorIndex < 0) if (newCursorIndex < 0)
{
g_currentCursorIndex = -1; g_currentCursorIndex = -1;
}
break; break;
} }
@@ -298,18 +303,13 @@ public:
if (newCursorIndex >= 0) if (newCursorIndex >= 0)
{ {
if (g_currentCursorIndex != newCursorIndex) if (g_currentCursorIndex != newCursorIndex)
{
Game_PlaySound("sys_worldmap_cursor"); Game_PlaySound("sys_worldmap_cursor");
}
g_currentCursorIndex = newCursorIndex; g_currentCursorIndex = newCursorIndex;
} }
} }
}; }
g_sdlEventListenerForInstaller;
static SDLEventListenerForInstaller g_eventListener;
const char CREDITS_TEXT[] = "Skyth, Hyper, Darío, Sajid, RadiantDerg, PTKay, DeaThProj, NextinHKRY, M&M, LadyLunanova";
static std::string& GetWizardText(WizardPage page) static std::string& GetWizardText(WizardPage page)
{ {
@@ -512,9 +512,9 @@ static void DrawHeaderIcons()
{ {
auto drawList = ImGui::GetForegroundDrawList(); auto drawList = ImGui::GetForegroundDrawList();
float iconsPosX = 253.0f; float iconsPosX = 256.0f;
float iconsPosY = 79.0f; float iconsPosY = 80.0f;
float iconsScale = 58; float iconsScale = 62.0f;
// Miles Electric Icon // Miles Electric Icon
float milesIconMotion = ComputeMotionInstaller(g_appearTime, g_disappearTime, MILES_ICON_ANIMATION_TIME, MILES_ICON_ANIMATION_DURATION); float milesIconMotion = ComputeMotionInstaller(g_appearTime, g_disappearTime, MILES_ICON_ANIMATION_TIME, MILES_ICON_ANIMATION_DURATION);
@@ -538,7 +538,6 @@ static void DrawScanlineBars()
const uint32_t COLOR1 = IM_COL32(203, 255, 0, 55 * scanlinesAlpha); const uint32_t COLOR1 = IM_COL32(203, 255, 0, 55 * scanlinesAlpha);
const uint32_t FADE_COLOR0 = IM_COL32(0, 0, 0, 255 * scanlinesAlpha); const uint32_t FADE_COLOR0 = IM_COL32(0, 0, 0, 255 * scanlinesAlpha);
const uint32_t FADE_COLOR1 = IM_COL32(0, 0, 0, 0); const uint32_t FADE_COLOR1 = IM_COL32(0, 0, 0, 0);
const uint32_t OUTLINE_COLOR = IM_COL32(115, 178, 104, 255 * scanlinesAlpha);
float height = Scale(105.0f) * ComputeMotionInstaller(g_appearTime, g_disappearTime, 0.0, SCANLINES_ANIMATION_DURATION); float height = Scale(105.0f) * ComputeMotionInstaller(g_appearTime, g_disappearTime, 0.0, SCANLINES_ANIMATION_DURATION);
if (height < 1e-6f) if (height < 1e-6f)
@@ -563,40 +562,80 @@ static void DrawScanlineBars()
); );
// Bottom bar // Bottom bar
ImVec2 max{ 0.0f, res.y - height };
SetProceduralOrigin(max);
drawList->AddRectFilledMultiColor drawList->AddRectFilledMultiColor
( (
{ res.x, res.y }, { res.x, res.y },
{ 0.0f, res.y - height }, max,
COLOR0, COLOR0,
COLOR0, COLOR0,
COLOR1, COLOR1,
COLOR1 COLOR1
); );
ResetProceduralOrigin();
SetShaderModifier(IMGUI_SHADER_MODIFIER_NONE); SetShaderModifier(IMGUI_SHADER_MODIFIER_NONE);
// Installer text // Installer text
const std::string &headerText = Localise(g_currentPage == WizardPage::Installing ? "Installer_Header_Installing" : "Installer_Header_Installer"); auto& headerText = Localise(g_currentPage == WizardPage::Installing ? "Installer_Header_Installing" : "Installer_Header_Installer");
auto alphaMotion = ComputeMotionInstaller(g_appearTime, g_disappearTime, TITLE_ANIMATION_TIME, TITLE_ANIMATION_DURATION); auto alphaMotion = ComputeMotionInstaller(g_appearTime, g_disappearTime, TITLE_ANIMATION_TIME, TITLE_ANIMATION_DURATION);
DrawTextWithOutline(g_dfsogeistdFont, Scale(42.0f), { g_aspectRatioOffsetX + Scale(285.0f), Scale(57.0f) }, IM_COL32(255, 195, 0, 255 * alphaMotion), headerText.c_str(), 4, IM_COL32(0, 0, 0, 255 * alphaMotion), IMGUI_SHADER_MODIFIER_TITLE_BEVEL); auto breatheMotion = 1.0f;
if (g_currentPage == WizardPage::Installing)
{
// Breathing animation
static auto breatheStart = ImGui::GetTime();
breatheMotion = BREATHE_MOTION(1.0f, 0.55f, breatheStart, 1.5f);
}
DrawTextWithOutline
(
g_dfsogeistdFont,
Scale(48.0f),
{ g_aspectRatioOffsetX + Scale(288.0f), Scale(54.5f) },
IM_COL32(255, 195, 0, 255 * alphaMotion * breatheMotion),
headerText.c_str(), 4,
IM_COL32(0, 0, 0, 255 * alphaMotion * breatheMotion),
IMGUI_SHADER_MODIFIER_TITLE_BEVEL
);
auto drawLine = [&](bool top)
{
float y = top ? height : (res.y - height);
const uint32_t TOP_COLOR0 = IM_COL32(222, 255, 189, 7 * scanlinesAlpha);
const uint32_t TOP_COLOR1 = IM_COL32(222, 255, 189, 65 * scanlinesAlpha);
const uint32_t BOTTOM_COLOR0 = IM_COL32(173, 255, 156, 65 * scanlinesAlpha);
const uint32_t BOTTOM_COLOR1 = IM_COL32(173, 255, 156, 7 * scanlinesAlpha);
drawList->AddRectFilledMultiColor(
{ 0.0f, y - Scale(2.0f) },
{ res.x, y },
top ? TOP_COLOR0 : BOTTOM_COLOR1,
top ? TOP_COLOR0 : BOTTOM_COLOR1,
top ? TOP_COLOR1 : BOTTOM_COLOR0,
top ? TOP_COLOR1 : BOTTOM_COLOR0);
drawList->AddRectFilledMultiColor(
{ 0.0f, y + Scale(1.0f) },
{ res.x, y + Scale(3.0f) },
top ? BOTTOM_COLOR0 : TOP_COLOR1,
top ? BOTTOM_COLOR0 : TOP_COLOR1,
top ? BOTTOM_COLOR1 : TOP_COLOR0,
top ? BOTTOM_COLOR1 : TOP_COLOR0);
const uint32_t CENTER_COLOR = IM_COL32(115, 178, 104, 255 * scanlinesAlpha);
drawList->AddRectFilled({ 0.0f, y }, { res.x, y + Scale(1.0f) }, CENTER_COLOR);
};
// Top bar line // Top bar line
drawList->AddLine drawLine(true);
(
{ 0.0f, height },
{ res.x, height },
OUTLINE_COLOR,
Scale(1)
);
// Bottom bar line // Bottom bar line
drawList->AddLine drawLine(false);
(
{ 0.0f, res.y - height },
{ res.x, res.y - height },
OUTLINE_COLOR,
Scale(1)
);
DrawHeaderIcons(); DrawHeaderIcons();
DrawVersionString(g_newRodinFont, IM_COL32(255, 255, 255, 70 * alphaMotion)); DrawVersionString(g_newRodinFont, IM_COL32(255, 255, 255, 70 * alphaMotion));
@@ -630,17 +669,18 @@ static void DrawContainer(ImVec2 min, ImVec2 max, bool isTextArea)
} }
// The draw area // The draw area
drawList->PushClipRect({ min.x + gridSize * 2.0f, min.y + gridSize * 2.0f }, { max.x - gridSize * 2.0f + 1.0f, max.y - gridSize * 2.0f + 1.0f }); drawList->PushClipRect({ min.x - gridSize * 2.0f, min.y + gridSize * 2.0f }, { max.x - gridSize * 2.0f + 1.0f, max.y - gridSize * 2.0f + 1.0f });
} }
static void DrawDescriptionContainer() static void DrawDescriptionContainer()
{ {
auto &res = ImGui::GetIO().DisplaySize; auto &res = ImGui::GetIO().DisplaySize;
auto drawList = ImGui::GetForegroundDrawList(); auto drawList = ImGui::GetForegroundDrawList();
auto fontSize = Scale(26.0f); auto fontSize = Scale(28.0f);
auto annotationFontSize = fontSize * ANNOTATION_FONT_SIZE_MODIFIER;
ImVec2 descriptionMin = { g_aspectRatioOffsetX + Scale(CONTAINER_X), g_aspectRatioOffsetY + Scale(CONTAINER_Y) }; ImVec2 descriptionMin = { round(g_aspectRatioOffsetX + Scale(CONTAINER_X + 0.5f)), round(g_aspectRatioOffsetY + Scale(CONTAINER_Y + 0.5f)) };
ImVec2 descriptionMax = { g_aspectRatioOffsetX + Scale(CONTAINER_X + CONTAINER_WIDTH), g_aspectRatioOffsetY + Scale(CONTAINER_Y + CONTAINER_HEIGHT) }; ImVec2 descriptionMax = { round(g_aspectRatioOffsetX + Scale(CONTAINER_X + 0.5f + CONTAINER_WIDTH)), round(g_aspectRatioOffsetY + Scale(CONTAINER_Y + 0.5f + CONTAINER_HEIGHT)) };
SetProceduralOrigin(descriptionMin); SetProceduralOrigin(descriptionMin);
DrawContainer(descriptionMin, descriptionMax, true); DrawContainer(descriptionMin, descriptionMax, true);
@@ -667,19 +707,58 @@ static void DrawDescriptionContainer()
auto clipRectMin = drawList->GetClipRectMin(); auto clipRectMin = drawList->GetClipRectMin();
auto clipRectMax = drawList->GetClipRectMax(); auto clipRectMax = drawList->GetClipRectMax();
drawList->AddText float textX = clipRectMin.x + fontSize;
float textY = clipRectMin.y - Scale(1.0f);
auto lineWidth = clipRectMax.x - (fontSize / 2.0f) - clipRectMin.x;
clipRectMax.x += fontSize;
clipRectMax.y += Scale(1.0f);
float lineMargin = 5.0f;
if (Config::Language == ELanguage::Japanese)
{
lineMargin = 5.5f;
// Removing some padding of the applied due to the inclusion of annotation for Japanese
textX -= (fontSize + Scale(1.5f));
textY -= Scale(7.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
clipRectMin.x -= annotationFontSize;
clipRectMin.y -= annotationFontSize;
clipRectMax.x += annotationFontSize;
clipRectMax.y += annotationFontSize;
textX += annotationFontSize;
textY += annotationFontSize;
}
drawList->PushClipRect(clipRectMin, clipRectMax, false);
DrawRubyAnnotatedText
( (
g_seuratFont, g_seuratFont,
fontSize, fontSize,
{ clipRectMin.x, clipRectMin.y }, lineWidth,
IM_COL32(255, 255, 255, 255 * textAlpha), { textX, textY },
lineMargin,
descriptionText, descriptionText,
0, [=](const char* str, ImVec2 pos)
clipRectMax.x - clipRectMin.x {
DrawTextBasic(g_seuratFont, fontSize, pos, IM_COL32(255, 255, 255, 255 * textAlpha), str);
},
[=](const char* str, float size, ImVec2 pos)
{
DrawTextBasic(g_seuratFont, size, pos, IM_COL32(255, 255, 255, 255 * textAlpha), str);
}
); );
drawList->PopClipRect(); drawList->PopClipRect();
drawList->PopClipRect();
if (g_currentPage == WizardPage::InstallSucceeded) if (g_currentPage == WizardPage::InstallSucceeded)
{ {
auto hedgeDevStr = "hedge-dev"; auto hedgeDevStr = "hedge-dev";
@@ -710,7 +789,7 @@ static void DrawDescriptionContainer()
hedgeDevStr hedgeDevStr
); );
auto marqueeTextSize = g_seuratFont->CalcTextSizeA(fontSize, FLT_MAX, 0, CREDITS_TEXT); auto marqueeTextSize = g_seuratFont->CalcTextSizeA(fontSize, FLT_MAX, 0, g_creditsStr.c_str());
auto marqueeTextMarginX = Scale(5); auto marqueeTextMarginX = Scale(5);
auto marqueeTextMarginY = Scale(15); auto marqueeTextMarginY = Scale(15);
@@ -718,8 +797,8 @@ static void DrawDescriptionContainer()
ImVec2 textMin = { g_aspectRatioOffsetX + Scale(CONTAINER_X), textPos.y }; 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) }; ImVec2 textMax = { g_aspectRatioOffsetX + Scale(CONTAINER_X) + Scale(CONTAINER_WIDTH), g_aspectRatioOffsetY + Scale(CONTAINER_Y) + Scale(CONTAINER_HEIGHT) };
SetMarqueeFade(textMin, textMax, Scale(32)); SetHorizontalMarqueeFade(textMin, textMax, Scale(32));
DrawTextWithMarquee(g_seuratFont, fontSize, textPos, textMin, textMax, colWhite, CREDITS_TEXT, g_appearTime, 0.9, Scale(250)); DrawTextWithMarquee(g_seuratFont, fontSize, textPos, textMin, textMax, colWhite, g_creditsStr.c_str(), g_installerEndTime, 0.9, Scale(200));
ResetMarqueeFade(); ResetMarqueeFade();
} }
@@ -799,7 +878,7 @@ static void DrawButton(ImVec2 min, ImVec2 max, const char *buttonText, bool sour
DrawButtonContainer(min, max, baser, baseg, alpha); DrawButtonContainer(min, max, baser, baseg, alpha);
ImFont *font = sourceButton ? g_newRodinFont : g_dfsogeistdFont; ImFont *font = sourceButton ? g_newRodinFont : g_dfsogeistdFont;
float size = Scale(sourceButton ? 15.0f : 20.0f); float size = Scale(sourceButton ? 16.5f : 20.0f);
float squashRatio; float squashRatio;
ImVec2 textSize = ComputeTextSize(font, buttonText, size, squashRatio, Scale(maxTextWidth)); ImVec2 textSize = ComputeTextSize(font, buttonText, size, squashRatio, Scale(maxTextWidth));
min.x += ((max.x - min.x) - textSize.x) / 2.0f; min.x += ((max.x - min.x) - textSize.x) / 2.0f;
@@ -872,7 +951,11 @@ static void DrawSourceButton(ButtonColumn buttonColumn, float yRatio, const char
float minusY = (CONTAINER_BUTTON_GAP + BUTTON_HEIGHT) * yRatio; float minusY = (CONTAINER_BUTTON_GAP + BUTTON_HEIGHT) * yRatio;
ImVec2 min = { minX, g_aspectRatioOffsetY + Scale(CONTAINER_Y + CONTAINER_HEIGHT - CONTAINER_BUTTON_GAP - BUTTON_HEIGHT - minusY) }; 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) }; ImVec2 max = { maxX, g_aspectRatioOffsetY + Scale(CONTAINER_Y + CONTAINER_HEIGHT - CONTAINER_BUTTON_GAP - minusY) };
DrawButton(min, max, sourceText, true, sourceSet, buttonPressed);
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);
} }
static void DrawProgressBar(float progressRatio) static void DrawProgressBar(float progressRatio)
@@ -1065,8 +1148,10 @@ static void DrawLanguagePicker()
bool buttonPressed = false; bool buttonPressed = false;
if (g_currentPage == WizardPage::SelectLanguage) if (g_currentPage == WizardPage::SelectLanguage)
{ {
bool buttonPressed; float alphaMotion = ComputeMotionInstaller(g_appearTime, g_disappearTime, CONTAINER_INNER_TIME, CONTAINER_INNER_DURATION);
float minX, maxX; float minX, maxX;
bool buttonPressed;
for (int i = 0; i < 6; i++) for (int i = 0; i < 6; i++)
{ {
ComputeButtonColumnCoordinates((i < 3) ? ButtonColumnLeft : ButtonColumnRight, minX, maxX); ComputeButtonColumnCoordinates((i < 3) ? ButtonColumnLeft : ButtonColumnRight, minX, maxX);
@@ -1075,16 +1160,16 @@ static void DrawLanguagePicker()
ImVec2 min = { minX, g_aspectRatioOffsetY + Scale(CONTAINER_Y + CONTAINER_HEIGHT - CONTAINER_BUTTON_GAP - BUTTON_HEIGHT - minusY) }; 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) }; ImVec2 max = { maxX, g_aspectRatioOffsetY + Scale(CONTAINER_Y + CONTAINER_HEIGHT - CONTAINER_BUTTON_GAP - minusY) };
// TODO: The active button should change its style to show an enabled toggle if it matches the current language. auto lightSize = Scale(14);
DrawButton(min, max, LANGUAGE_TEXT[i], false, true, buttonPressed, FLT_MAX, LANGUAGE_ENUM[i] == ELanguage::English); DrawButton(min, max, LANGUAGE_TEXT[i], false, true, buttonPressed, FLT_MAX, LANGUAGE_ENUM[i] == ELanguage::English);
DrawToggleLight({ min.x + lightSize, min.y + ((max.y - min.y) - lightSize) / 2 + Scale(1) }, Config::Language == LANGUAGE_ENUM[i], alphaMotion);
if (buttonPressed) if (buttonPressed)
{
Config::Language = LANGUAGE_ENUM[i]; Config::Language = LANGUAGE_ENUM[i];
} }
} }
} }
}
static void DrawSourcePickers() static void DrawSourcePickers()
{ {
@@ -1125,8 +1210,8 @@ static void DrawSources()
{ {
if (g_currentPage == WizardPage::SelectGameAndUpdate) if (g_currentPage == WizardPage::SelectGameAndUpdate)
{ {
DrawSourceButton(ButtonColumnMiddle, 1.5f, Localise("Installer_Step_Game").c_str(), !g_gameSourcePath.empty()); DrawSourceButton(ButtonColumnLeft, 0, Localise("Installer_Step_Game").c_str(), !g_gameSourcePath.empty());
DrawSourceButton(ButtonColumnMiddle, 0.5f, Localise("Installer_Step_Update").c_str(), !g_updateSourcePath.empty()); DrawSourceButton(ButtonColumnRight, 0, Localise("Installer_Step_Update").c_str(), !g_updateSourcePath.empty());
} }
if (g_currentPage == WizardPage::SelectDLC) if (g_currentPage == WizardPage::SelectDLC)
@@ -1470,6 +1555,12 @@ void InstallerWizard::Init()
g_arrowCircle = LOAD_ZSTD_TEXTURE(g_arrow_circle); g_arrowCircle = LOAD_ZSTD_TEXTURE(g_arrow_circle);
g_pulseInstall = LOAD_ZSTD_TEXTURE(g_pulse_install); g_pulseInstall = LOAD_ZSTD_TEXTURE(g_pulse_install);
g_upHedgeDev = LOAD_ZSTD_TEXTURE(g_hedgedev); g_upHedgeDev = LOAD_ZSTD_TEXTURE(g_hedgedev);
for (int i = 0; i < g_creditsSize; i++)
{
g_creditsStr += g_credits[i];
g_creditsStr += " ";
}
} }
void InstallerWizard::Draw() void InstallerWizard::Draw()
+55 -40
View File
@@ -1,17 +1,15 @@
#include "message_window.h" #include "message_window.h"
#include "imgui_utils.h"
#include <api/SWA.h> #include <api/SWA.h>
#include <gpu/imgui/imgui_snapshot.h>
#include <gpu/video.h> #include <gpu/video.h>
#include <hid/hid.h> #include <hid/hid.h>
#include <locale/locale.h> #include <locale/locale.h>
#include <ui/button_guide.h> #include <ui/button_guide.h>
#include <ui/sdl_listener.h> #include <ui/imgui_utils.h>
#include <app.h> #include <app.h>
#include <exports.h>
#include <res/images/common/general_window.dds.h>
#include <decompressor.h> #include <decompressor.h>
#include <res/images/common/select_fade.dds.h> #include <exports.h>
#include <gpu/imgui/imgui_snapshot.h> #include <sdl_listener.h>
constexpr double OVERLAY_CONTAINER_COMMON_MOTION_START = 0; constexpr double OVERLAY_CONTAINER_COMMON_MOTION_START = 0;
constexpr double OVERLAY_CONTAINER_COMMON_MOTION_END = 11; constexpr double OVERLAY_CONTAINER_COMMON_MOTION_END = 11;
@@ -39,9 +37,6 @@ static double g_controlsAppearTime;
static ImFont* g_fntSeurat; static ImFont* g_fntSeurat;
static std::unique_ptr<GuestTexture> g_upSelectionCursor;
static std::unique_ptr<GuestTexture> g_upWindow;
std::string g_text; std::string g_text;
int g_result; int g_result;
std::vector<std::string> g_buttons; std::vector<std::string> g_buttons;
@@ -53,7 +48,7 @@ class SDLEventListenerForMessageWindow : public SDLEventListener
public: public:
void OnSDLEvent(SDL_Event* event) override void OnSDLEvent(SDL_Event* event) override
{ {
if (App::s_isInit || !MessageWindow::s_isVisible) if (App::s_isInit || !MessageWindow::s_isVisible || !hid::IsInputAllowed())
return; return;
constexpr float axisValueRange = 32767.0f; constexpr float axisValueRange = 32767.0f;
@@ -146,9 +141,8 @@ public:
} }
} }
} }
}; }
g_sdlEventListenerForMessageWindow;
static SDLEventListenerForMessageWindow g_eventListener;
bool DrawContainer(float appearTime, ImVec2 centre, ImVec2 max, bool isForeground = true) bool DrawContainer(float appearTime, ImVec2 centre, ImVec2 max, bool isForeground = true)
{ {
@@ -190,11 +184,15 @@ bool DrawContainer(float appearTime, ImVec2 centre, ImVec2 max, bool isForegroun
if (isForeground) if (isForeground)
drawList->AddRectFilled({ 0.0f, 0.0f }, ImGui::GetIO().DisplaySize, IM_COL32(0, 0, 0, 190 * (g_foregroundCount ? 1 : alpha))); drawList->AddRectFilled({ 0.0f, 0.0f }, ImGui::GetIO().DisplaySize, IM_COL32(0, 0, 0, 190 * (g_foregroundCount ? 1 : alpha)));
DrawPauseContainer(g_upWindow.get(), _min, _max, alpha); DrawPauseContainer(_min, _max, alpha);
if (containerMotion >= 1.0f && !g_isClosing)
{
drawList->PushClipRect(_min, _max); drawList->PushClipRect(_min, _max);
return true;
}
return containerMotion >= 1.0f && !g_isClosing; return false;
} }
void DrawButton(int rowIndex, float yOffset, float width, float height, std::string& text) void DrawButton(int rowIndex, float yOffset, float width, float height, std::string& text)
@@ -210,20 +208,7 @@ void DrawButton(int rowIndex, float yOffset, float width, float height, std::str
bool isSelected = rowIndex == g_selectedRowIndex; bool isSelected = rowIndex == g_selectedRowIndex;
if (isSelected) if (isSelected)
{ DrawSelectionContainer(min, max, true);
static auto breatheStart = ImGui::GetTime();
auto alpha = Lerp(1.0f, 0.75f, (sin((ImGui::GetTime() - breatheStart) * (2.0f * M_PI / (55.0f / 60.0f))) + 1.0f) / 2.0f);
auto colour = IM_COL32(255, 255, 255, 255 * alpha);
auto width = Scale(11);
auto left = PIXELS_TO_UV_COORDS(64, 64, 0, 0, 11, 50);
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_upSelectionCursor.get(), min, { min.x + width, max.y }, GET_UV_COORDS(left), colour);
drawList->AddImage(g_upSelectionCursor.get(), { min.x + width, min.y }, { max.x - width, max.y }, GET_UV_COORDS(centre), colour);
drawList->AddImage(g_upSelectionCursor.get(), { max.x - width, min.y }, max, GET_UV_COORDS(right), colour);
}
auto fontSize = Scale(28); auto fontSize = Scale(28);
auto textSize = g_fntSeurat->CalcTextSizeA(fontSize, FLT_MAX, 0, text.c_str()); auto textSize = g_fntSeurat->CalcTextSizeA(fontSize, FLT_MAX, 0, text.c_str());
@@ -273,9 +258,6 @@ void MessageWindow::Init()
auto& io = ImGui::GetIO(); auto& io = ImGui::GetIO();
g_fntSeurat = ImFontAtlasSnapshot::GetFont("FOT-SeuratPro-M.otf"); g_fntSeurat = ImFontAtlasSnapshot::GetFont("FOT-SeuratPro-M.otf");
g_upSelectionCursor = LOAD_ZSTD_TEXTURE(g_select_fade);
g_upWindow = LOAD_ZSTD_TEXTURE(g_general_window);
} }
void MessageWindow::Draw() void MessageWindow::Draw()
@@ -288,18 +270,42 @@ void MessageWindow::Draw()
ImVec2 centre = { res.x / 2, res.y / 2 }; ImVec2 centre = { res.x / 2, res.y / 2 };
float maxWidth = Scale(640.0f); auto maxWidth = Scale(820);
auto fontSize = Scale(28); auto fontSize = Scale(28);
auto textSize = MeasureCentredParagraph(g_fntSeurat, fontSize, maxWidth, 5, g_text.c_str());
const auto input = RemoveRubyAnnotations(g_text.c_str());
auto lines = Split(input.first.c_str(), g_fntSeurat, fontSize, maxWidth);
for (auto& line : lines)
{
line = ReAddRubyAnnotations(line, input.second);
}
auto lineMargin = Config::Language != ELanguage::Japanese ? 5.0f : 5.5f;
auto textSize = MeasureCentredParagraph(g_fntSeurat, fontSize, lineMargin, lines);
auto textMarginX = Scale(37); auto textMarginX = Scale(37);
auto textMarginY = Scale(45); auto textMarginY = Scale(45);
auto textX = centre.x;
auto textY = centre.y + Scale(3);
if (Config::Language == ELanguage::Japanese)
{
textMarginX -= Scale(2.5f);
textMarginY -= Scale(7.5f);
textY += Scale(lines.size() % 2 == 0 ? 8.5f : 15.5f);
}
bool isController = hid::IsInputDeviceController(); bool isController = hid::IsInputDeviceController();
bool isKeyboard = hid::g_inputDevice == hid::EInputDevice::Keyboard; bool isKeyboard = hid::g_inputDevice == hid::EInputDevice::Keyboard;
// Handle controller input when the game is booted. // Handle controller input when the game is booted.
if (App::s_isInit) if (App::s_isInit)
{ {
// Always assume keyboard to prevent mouse from blocking control in-game.
isKeyboard = true;
if (auto pInputState = SWA::CInputState::GetInstance()) if (auto pInputState = SWA::CInputState::GetInstance())
{ {
auto& rPadState = pInputState->GetPadState(); auto& rPadState = pInputState->GetPadState();
@@ -322,19 +328,25 @@ void MessageWindow::Draw()
if (DrawContainer(g_appearTime, centre, { textSize.x / 2 + textMarginX, textSize.y / 2 + textMarginY }, !g_isControlsVisible)) if (DrawContainer(g_appearTime, centre, { textSize.x / 2 + textMarginX, textSize.y / 2 + textMarginY }, !g_isControlsVisible))
{ {
DrawCentredParagraph DrawRubyAnnotatedText
( (
g_fntSeurat, g_fntSeurat,
fontSize, fontSize,
maxWidth, maxWidth,
{ centre.x, centre.y + Scale(3) }, { textX, textY },
5, lineMargin,
g_text.c_str(), g_text.c_str(),
[=](const char* str, ImVec2 pos) [=](const char* str, ImVec2 pos)
{ {
DrawTextWithShadow(g_fntSeurat, fontSize, pos, IM_COL32(255, 255, 255, 255), str); DrawTextWithShadow(g_fntSeurat, fontSize, pos, IM_COL32(255, 255, 255, 255), str);
} },
[=](const char* str, float size, ImVec2 pos)
{
DrawTextWithShadow(g_fntSeurat, size, pos, IM_COL32(255, 255, 255, 255), str, 1.0f);
},
true
); );
drawList->PopClipRect(); drawList->PopClipRect();
@@ -377,12 +389,15 @@ void MessageWindow::Draw()
} }
if (scrollUp || scrollDown) if (scrollUp || scrollDown)
{
Game_PlaySound("sys_actstg_pausecursor"); Game_PlaySound("sys_actstg_pausecursor");
g_joypadAxis.y = 0;
}
g_upWasHeld = upIsHeld; g_upWasHeld = upIsHeld;
g_downWasHeld = downIsHeld; g_downWasHeld = downIsHeld;
if (isController) if (isController || (isKeyboard && App::s_isInit))
{ {
std::array<Button, 2> buttons = std::array<Button, 2> buttons =
{ {
@@ -392,7 +407,7 @@ void MessageWindow::Draw()
ButtonGuide::Open(buttons); ButtonGuide::Open(buttons);
} }
else if (!App::s_isInit) // Only display keyboard prompt during installer. else // Only display keyboard prompt during installer.
{ {
ButtonGuide::Open(Button(Localise("Common_Select"), EButtonIcon::Enter)); ButtonGuide::Open(Button(Localise("Common_Select"), EButtonIcon::Enter));
} }
+403 -84
View File
@@ -1,10 +1,7 @@
#include "options_menu.h" #include "options_menu.h"
#include "options_menu_thumbnails.h" #include "options_menu_thumbnails.h"
#include "imgui_utils.h"
#include "game_window.h"
#include "exports.h"
#include <api/SWA/System/InputState.h> #include <api/SWA.h>
#include <apu/audio.h> #include <apu/audio.h>
#include <gpu/imgui/imgui_common.h> #include <gpu/imgui/imgui_common.h>
#include <gpu/video.h> #include <gpu/video.h>
@@ -13,11 +10,14 @@
#include <kernel/heap.h> #include <kernel/heap.h>
#include <kernel/memory.h> #include <kernel/memory.h>
#include <locale/locale.h> #include <locale/locale.h>
#include <patches/aspect_ratio_patches.h>
#include <patches/audio_patches.h>
#include <ui/button_guide.h> #include <ui/button_guide.h>
#include <ui/game_window.h>
#include <ui/imgui_utils.h>
#include <app.h> #include <app.h>
#include <decompressor.h> #include <decompressor.h>
#include <exports.h>
#include <patches/audio_patches.h>
#include <res/images/options_menu/miles_electric.dds.h> #include <res/images/options_menu/miles_electric.dds.h>
@@ -27,6 +27,8 @@ static constexpr double MILES_ELECTRIC_FOREGROUND_FADE_DURATION = 6.0;
static constexpr double MILES_ELECTRIC_FOREGROUND_FADE_IN_TIME = MILES_ELECTRIC_SCALE_DURATION - 2.0; static constexpr double MILES_ELECTRIC_FOREGROUND_FADE_IN_TIME = MILES_ELECTRIC_SCALE_DURATION - 2.0;
static constexpr double MILES_ELECTRIC_FOREGROUND_FADE_OUT_TIME = MILES_ELECTRIC_FOREGROUND_FADE_IN_TIME + MILES_ELECTRIC_FOREGROUND_FADE_DURATION; static constexpr double MILES_ELECTRIC_FOREGROUND_FADE_OUT_TIME = MILES_ELECTRIC_FOREGROUND_FADE_IN_TIME + MILES_ELECTRIC_FOREGROUND_FADE_DURATION;
static constexpr double VALUE_SLIDER_INTRO_DURATION = 20.0;
static constexpr double CONTAINER_LINE_ANIMATION_DURATION = 8.0; static constexpr double CONTAINER_LINE_ANIMATION_DURATION = 8.0;
static constexpr double CONTAINER_OUTER_TIME = CONTAINER_LINE_ANIMATION_DURATION + 8.0; // 8 frame delay static constexpr double CONTAINER_OUTER_TIME = CONTAINER_LINE_ANIMATION_DURATION + 8.0; // 8 frame delay
@@ -43,7 +45,7 @@ static constexpr double CONTAINER_FULL_DURATION = CONTAINER_BACKGROUND_TIME + CO
static constexpr double CONTAINER_CATEGORY_TIME = (CONTAINER_INNER_TIME + CONTAINER_BACKGROUND_TIME) / 2.0; static constexpr double CONTAINER_CATEGORY_TIME = (CONTAINER_INNER_TIME + CONTAINER_BACKGROUND_TIME) / 2.0;
static constexpr double CONTAINER_CATEGORY_DURATION = 12.0; static constexpr double CONTAINER_CATEGORY_DURATION = 12.0;
static constexpr float CONTAINER_POS_Y = 118.0f; static constexpr float CONTAINER_POS_Y = 117.0f;
static constexpr float SETTINGS_WIDE_GRID_COUNT = 90.0f; static constexpr float SETTINGS_WIDE_GRID_COUNT = 90.0f;
static constexpr float INFO_WIDE_GRID_COUNT = 42.0f; static constexpr float INFO_WIDE_GRID_COUNT = 42.0f;
@@ -62,6 +64,7 @@ static int32_t g_firstVisibleRowIndex;
static int32_t g_prevSelectedRowIndex; static int32_t g_prevSelectedRowIndex;
static int32_t g_selectedRowIndex; static int32_t g_selectedRowIndex;
static double g_rowSelectionTime; static double g_rowSelectionTime;
static float g_prevOffsetRatio;
static bool g_leftWasHeld; static bool g_leftWasHeld;
static bool g_upWasHeld; static bool g_upWasHeld;
@@ -69,6 +72,7 @@ static bool g_rightWasHeld;
static bool g_downWasHeld; static bool g_downWasHeld;
static bool g_lockedOnOption; static bool g_lockedOnOption;
static double g_lockedOnTime;
static double g_lastTappedTime; static double g_lastTappedTime;
static double g_lastIncrementTime; static double g_lastIncrementTime;
static double g_lastIncrementSoundTime; static double g_lastIncrementSoundTime;
@@ -89,18 +93,145 @@ static bool g_isControlsVisible = false;
static bool g_isEnterKeyBuffered = false; static bool g_isEnterKeyBuffered = false;
static bool g_canReset = false; static bool g_canReset = false;
static bool g_isLanguageOptionChanged = false; static bool g_isLanguageOptionChanged = false;
static bool g_titleAnimBegin = true;
static double g_appearTime = 0.0; static double g_appearTime = 0.0;
static std::unique_ptr<GuestTexture> g_upMilesElectric; static std::unique_ptr<GuestTexture> g_upMilesElectric;
static void DrawTitle()
{
static constexpr double fadeOffset = 3.0;
static constexpr double fadeDuration = 28.0;
static constexpr double fadeAdditiveDuration = 20.0;
static constexpr double squareMoveDuration = 5.0;
static constexpr double squareMoveEndDuration = 40.0;
static constexpr double squareBlinkDuration = 10.0;
static constexpr double squareFadeDuration = 45.0;
static bool isRectVisible;
static bool isRectFinalAdjustment;
static float rectX;
static double rectMoveMotionOffset;
static double rectBlinkMotionOffset;
if (g_titleAnimBegin)
{
isRectVisible = true;
isRectFinalAdjustment = false;
rectX = 0;
rectMoveMotionOffset = 0;
rectBlinkMotionOffset = 0;
g_titleAnimBegin = false;
}
auto drawList = ImGui::GetForegroundDrawList();
auto x = Scale(122);
auto y = Scale(56);
if (g_aspectRatio >= WIDE_ASPECT_RATIO)
x += g_aspectRatioOffsetX;
else
x += (1.0f - g_aspectRatioNarrowScale) * g_aspectRatioScale * -20.0f;
ImVec2 optionsMin = { x, y };
auto drawText = [&](float alpha)
{
DrawTextWithOutline
(
g_dfsogeistdFont,
Scale(48),
optionsMin,
IM_COL32(255, 190, 33, 255 * alpha),
Localise("Options_Header_Name").c_str(),
4,
IM_COL32(0, 0, 0, 255 * alpha),
IMGUI_SHADER_MODIFIER_TITLE_BEVEL
);
};
if (g_isStage)
{
drawText(1.0f);
return;
}
drawText(Hermite(0.0f, 1.0f, ComputeMotion(g_appearTime, fadeOffset, fadeDuration)));
auto rectMoveMotion = ComputeMotion(g_appearTime, rectMoveMotionOffset, squareMoveDuration);
auto rectEndMotion = ComputeMotion(g_appearTime, 0.0, squareMoveEndDuration);
auto rectBlinkMotion = sin(ComputeMotion(g_appearTime, squareMoveEndDuration + rectBlinkMotionOffset, squareBlinkDuration) * M_PI);
auto rectAlphaMotion = 1.0f;
auto rectY = Scale(10);
auto rectSize = Scale(32);
if (rectBlinkMotion > 0.0)
{
if (!isRectFinalAdjustment)
{
/* Ensure the blinking animation starts
past the German localisation. */
rectX += rectSize + (rectSize * 0.25f);
isRectFinalAdjustment = true;
}
isRectVisible = !isRectVisible;
rectBlinkMotionOffset += squareBlinkDuration;
}
if (rectEndMotion >= 1.0)
{
// Fade out the square.
rectAlphaMotion = 1.0f - ComputeMotion(g_appearTime, squareMoveEndDuration + squareBlinkDuration, squareFadeDuration);
}
else if (rectMoveMotion >= 1.0)
{
// Move the square along in steps by its own width and offset the animation to repeat.
rectX += rectSize;
rectMoveMotionOffset += squareMoveDuration;
}
if (isRectVisible)
{
float rectScale = 1.0f;
if (rectBlinkMotion == 0.0)
{
constexpr float RECT_SCALES[] = { 1.2f, 1.1f, 1.1f, 1.0f, 1.15f, 0.4f, 1.2f, 1.1f, 1.05f, 1.0f, 1.5f, 1.2f, 1.0f };
rectScale = RECT_SCALES[uint32_t(round(rectX / rectSize)) % std::size(RECT_SCALES)];
}
ImVec2 rectMin = { optionsMin.x + rectX, optionsMin.y + rectY };
ImVec2 rectMax = { optionsMin.x + rectX + rectSize * rectScale, optionsMin.y + rectY + rectSize };
auto rectOutlineMargin = Scale(2.5f);
ImVec2 rectOutlineMin = { rectMin.x - rectOutlineMargin, rectMin.y - rectOutlineMargin };
ImVec2 rectOutlineMax = { rectMax.x + rectOutlineMargin, rectMax.y + rectOutlineMargin };
drawList->AddRectFilled(rectOutlineMin, rectOutlineMax, IM_COL32(0, 0, 0, 255 * rectAlphaMotion), Scale(5));
SetShaderModifier(IMGUI_SHADER_MODIFIER_RECTANGLE_BEVEL);
SetGradient(rectMin, rectMax, IM_COL32_WHITE, IM_COL32_WHITE);
drawList->AddRectFilled(rectMin, rectMax, IM_COL32(255, 188, 0, 255 * rectAlphaMotion));
ResetGradient();
SetShaderModifier(IMGUI_SHADER_MODIFIER_NONE);
}
// The flash gets rendered after the rectangle in the original game.
SetAdditive(true);
drawText(1.0 - 2.0 * abs(ComputeLinearMotion(g_appearTime, fadeDuration, fadeAdditiveDuration) - 0.5));
SetAdditive(false);
}
static void DrawScanlineBars() static void DrawScanlineBars()
{ {
constexpr uint32_t COLOR0 = IM_COL32(203, 255, 0, 0); constexpr uint32_t COLOR0 = IM_COL32(203, 255, 0, 0);
constexpr uint32_t COLOR1 = IM_COL32(203, 255, 0, 55); constexpr uint32_t COLOR1 = IM_COL32(203, 255, 0, 55);
constexpr uint32_t FADE_COLOR0 = IM_COL32(0, 0, 0, 255); constexpr uint32_t FADE_COLOR0 = IM_COL32(0, 0, 0, 255);
constexpr uint32_t FADE_COLOR1 = IM_COL32(0, 0, 0, 0); constexpr uint32_t FADE_COLOR1 = IM_COL32(0, 0, 0, 0);
constexpr uint32_t OUTLINE_COLOR = IM_COL32(115, 178, 104, 255);
float height = Scale(105.0f); float height = Scale(105.0f);
@@ -146,54 +277,59 @@ static void DrawScanlineBars()
); );
// Bottom bar // Bottom bar
ImVec2 max{ 0.0f, res.y - height };
SetProceduralOrigin(max);
drawList->AddRectFilledMultiColor drawList->AddRectFilledMultiColor
( (
{ res.x, res.y }, { res.x, res.y },
{ 0.0f, res.y - height }, max,
COLOR0, COLOR0,
COLOR0, COLOR0,
COLOR1, COLOR1,
COLOR1 COLOR1
); );
ResetProceduralOrigin();
SetShaderModifier(IMGUI_SHADER_MODIFIER_NONE); SetShaderModifier(IMGUI_SHADER_MODIFIER_NONE);
float optionsX; DrawTitle();
if (g_aspectRatio >= WIDE_ASPECT_RATIO)
optionsX = g_aspectRatioOffsetX;
else
optionsX = (1.0f - g_aspectRatioNarrowScale) * g_aspectRatioScale * -20.0f;
// Options text auto drawLine = [&](bool top)
DrawTextWithOutline {
( float y = top ? height : (res.y - height);
g_dfsogeistdFont,
Scale(48.0f), constexpr uint32_t TOP_COLOR0 = IM_COL32(222, 255, 189, 7);
{ optionsX + Scale(122.0f), Scale(56.0f) }, constexpr uint32_t TOP_COLOR1 = IM_COL32(222, 255, 189, 65);
IM_COL32(255, 190, 33, 255), constexpr uint32_t BOTTOM_COLOR0 = IM_COL32(173, 255, 156, 65);
Localise("Options_Header_Name").c_str(), constexpr uint32_t BOTTOM_COLOR1 = IM_COL32(173, 255, 156, 7);
4,
IM_COL32_BLACK, drawList->AddRectFilledMultiColor(
IMGUI_SHADER_MODIFIER_TITLE_BEVEL { 0.0f, y - Scale(2.0f) },
); { res.x, y },
top ? TOP_COLOR0 : BOTTOM_COLOR1,
top ? TOP_COLOR0 : BOTTOM_COLOR1,
top ? TOP_COLOR1 : BOTTOM_COLOR0,
top ? TOP_COLOR1 : BOTTOM_COLOR0);
drawList->AddRectFilledMultiColor(
{ 0.0f, y + Scale(1.0f) },
{ res.x, y + Scale(3.0f) },
top ? BOTTOM_COLOR0 : TOP_COLOR1,
top ? BOTTOM_COLOR0 : TOP_COLOR1,
top ? BOTTOM_COLOR1 : TOP_COLOR0,
top ? BOTTOM_COLOR1 : TOP_COLOR0);
constexpr uint32_t CENTER_COLOR = IM_COL32(115, 178, 104, 255);
drawList->AddRectFilled({ 0.0f, y }, { res.x, y + Scale(1.0f) }, CENTER_COLOR);
};
// Top bar line // Top bar line
drawList->AddLine drawLine(true);
(
{ 0.0f, height },
{ res.x, height },
OUTLINE_COLOR,
Scale(1)
);
// Bottom bar line // Bottom bar line
drawList->AddLine drawLine(false);
(
{ 0.0f, res.y - height },
{ res.x, res.y - height },
OUTLINE_COLOR,
Scale(1)
);
DrawVersionString(g_newRodinFont); DrawVersionString(g_newRodinFont);
} }
@@ -274,6 +410,7 @@ static void ResetSelection()
g_selectedRowIndex = 0; g_selectedRowIndex = 0;
g_prevSelectedRowIndex = 0; g_prevSelectedRowIndex = 0;
g_rowSelectionTime = ImGui::GetTime(); g_rowSelectionTime = ImGui::GetTime();
g_prevOffsetRatio = 0.0f;
g_leftWasHeld = false; g_leftWasHeld = false;
g_upWasHeld = false; g_upWasHeld = false;
g_rightWasHeld = false; g_rightWasHeld = false;
@@ -455,6 +592,132 @@ static bool DrawCategories()
return false; return false;
} }
static void DrawSelectionArrows(ImVec2 min, ImVec2 max, bool isLeftTapped, bool isRightTapped, bool isSlider)
{
static constexpr double sizeMotionDuration = 16.0;
static bool isLeftArrowMotion = false;
static bool isRightArrowMotion = false;
auto drawList = ImGui::GetForegroundDrawList();
auto gridSize = Scale(GRID_SIZE);
auto width = gridSize * 2.5f;
auto padding = gridSize;
auto bgMotion = (isLeftArrowMotion || isRightArrowMotion)
? ComputeMotion(g_lastTappedTime, 8.0, sizeMotionDuration)
: 0;
if (isLeftTapped)
{
isLeftArrowMotion = true;
isRightArrowMotion = false;
}
if (isRightTapped)
{
isLeftArrowMotion = false;
isRightArrowMotion = true;
}
if (bgMotion >= 1.0 || isSlider)
{
isLeftArrowMotion = false;
isRightArrowMotion = false;
}
auto getBgColour = [&](bool isAnim) -> ImU32
{
return IM_COL32(0, 97, 0, Lerp(96, 255, isAnim ? bgMotion : 1));
};
auto bgLeftColour = getBgColour(isLeftArrowMotion);
auto bgRightColour = getBgColour(isRightArrowMotion);
auto invertMotion = isSlider ? ComputeMotion(g_lockedOnTime, 0, VALUE_SLIDER_INTRO_DURATION) : 0;
auto invertMotionX = invertMotion > 0.5 ? 1.0 : 0.0; // Arrow side point invert animation
auto xAdd = Hermite(0, Scale(10), sin(invertMotion * M_PI)); // Arrow jump animation
auto y = (min.y + max.y) / 2.0f;
// Left triangle vertices
auto leftX = Lerp(min.x - padding, min.x - padding - width, invertMotionX) - xAdd;
auto leftV1Y = Hermite(min.y, max.y, invertMotion);
auto leftV2Y = Hermite(max.y, min.y, invertMotion);
auto leftV3X = Hermite(min.x - padding - width, min.x - padding, invertMotionX) - xAdd;
// Right triangle vertices
auto rightX = Lerp(max.x + padding, max.x + padding + width, invertMotionX) + xAdd;
auto rightV1Y = Hermite(max.y, min.y, invertMotion);
auto rightV2Y = Hermite(min.y, max.y, invertMotion);
auto rightV3X = Hermite(max.x + padding + width, max.x + padding, invertMotionX) + xAdd;
auto drawLeftArrow = [&](ImU32 col)
{
drawList->AddTriangleFilled({ leftX, leftV1Y }, { leftX, leftV2Y }, { leftV3X, y }, col);
};
auto drawRightArrow = [&](ImU32 col)
{
drawList->AddTriangleFilled({ rightX, rightV1Y }, { rightX, rightV2Y }, { rightV3X, y }, col);
};
drawLeftArrow(bgLeftColour);
drawRightArrow(bgRightColour);
// Additive gradient colours
auto c0 = IM_COL32(255, 0, 255, 255);
auto c1 = IM_COL32(255, 128, 255, 255);
SetAdditive(true);
// Apply additive gradients
SetGradient({ leftX, leftV1Y }, { leftV3X, y }, c0, c1, c1, c0);
drawLeftArrow(bgLeftColour);
SetGradient({ rightX, rightV1Y }, { rightV3X, y }, c0, c1, c1, c0);
drawRightArrow(bgRightColour);
ResetGradient();
if (isSlider)
{
auto col = IM_COL32(0, 97, 0, 255 * invertMotion);
drawLeftArrow(col);
drawRightArrow(col);
}
else
{
auto fgMotion = ComputeMotion(g_lastTappedTime, 0, sizeMotionDuration);
auto fgMotionSine = sin(fgMotion * M_PI);
auto fgScale = Lerp(0, Scale(4), fgMotionSine);
auto fgColour = IM_COL32(0, 97, 0, 255 * fgMotionSine);
if (isLeftArrowMotion)
{
drawList->AddTriangleFilled
(
{ min.x - padding, min.y - fgScale },
{ min.x - padding, max.y + fgScale },
{ min.x - padding - width - (fgScale + Scale(2)), (min.y + max.y) / 2.0f },
fgColour
);
}
if (isRightArrowMotion)
{
drawList->AddTriangleFilled
(
{ max.x + padding, max.y + fgScale },
{ max.x + padding, min.y - fgScale },
{ max.x + padding + width + (fgScale + Scale(2)), (min.y + max.y) / 2.0f },
fgColour
);
}
}
SetAdditive(false);
}
template<typename T> template<typename T>
static void DrawConfigOption(int32_t rowIndex, float yOffset, ConfigDef<T>* config, static void DrawConfigOption(int32_t rowIndex, float yOffset, ConfigDef<T>* config,
bool isAccessible, std::string* inaccessibleReason = nullptr, bool isAccessible, std::string* inaccessibleReason = nullptr,
@@ -467,15 +730,15 @@ static void DrawConfigOption(int32_t rowIndex, float yOffset, ConfigDef<T>* conf
constexpr float OPTION_NARROW_GRID_COUNT = 36.0f; constexpr float OPTION_NARROW_GRID_COUNT = 36.0f;
constexpr float OPTION_WIDE_GRID_COUNT = 54.0f; constexpr float OPTION_WIDE_GRID_COUNT = 54.0f;
constexpr bool IS_SLIDER_TYPE = std::is_same_v<T, float> || std::is_same_v<T, int32_t>;
auto isValueSlider = IS_SLIDER_TYPE && isSlider;
auto gridSize = Scale(GRID_SIZE); auto gridSize = Scale(GRID_SIZE);
auto optionWidth = gridSize * floor(Lerp(OPTION_NARROW_GRID_COUNT, OPTION_WIDE_GRID_COUNT, g_aspectRatioNarrowScale)); auto optionWidth = gridSize * floor(Lerp(OPTION_NARROW_GRID_COUNT, OPTION_WIDE_GRID_COUNT, g_aspectRatioNarrowScale));
auto optionHeight = gridSize * 5.5f; auto optionHeight = gridSize * 5.5f;
auto optionPadding = gridSize * 0.5f; auto optionPadding = gridSize * 0.5f;
auto valueWidth = Scale(192.0f); auto valueWidth = Scale(192.0f);
auto valueHeight = gridSize * 3.0f; auto valueHeight = gridSize * 3.0f;
auto triangleWidth = gridSize * 2.5f;
auto trianglePadding = gridSize;
// Left side // Left side
ImVec2 min = { clipRectMin.x, clipRectMin.y + (optionHeight + optionPadding) * rowIndex + yOffset }; ImVec2 min = { clipRectMin.x, clipRectMin.y + (optionHeight + optionPadding) * rowIndex + yOffset };
@@ -489,6 +752,7 @@ static void DrawConfigOption(int32_t rowIndex, float yOffset, ConfigDef<T>* conf
ImVec4 textClipRect = { min.x, min.y, max.x, max.y }; ImVec4 textClipRect = { min.x, min.y, max.x, max.y };
bool lockedOnOption = false; bool lockedOnOption = false;
if (g_selectedRowIndex == rowIndex) if (g_selectedRowIndex == rowIndex)
{ {
g_selectedItem = config; g_selectedItem = config;
@@ -523,6 +787,7 @@ static void DrawConfigOption(int32_t rowIndex, float yOffset, ConfigDef<T>* conf
if (g_lockedOnOption) if (g_lockedOnOption)
{ {
g_lockedOnTime = ImGui::GetTime();
g_leftWasHeld = false; g_leftWasHeld = false;
g_rightWasHeld = false; g_rightWasHeld = false;
@@ -590,6 +855,9 @@ static void DrawConfigOption(int32_t rowIndex, float yOffset, ConfigDef<T>* conf
auto alpha = fadedOut ? 0.5f : 1.0f; auto alpha = fadedOut ? 0.5f : 1.0f;
auto textColour = IM_COL32(255, 255, 255, 255 * alpha); auto textColour = IM_COL32(255, 255, 255, 255 * alpha);
if (Config::Language == ELanguage::Japanese)
textPos.y += Scale(10.0f);
if (g_selectedItem == config) if (g_selectedItem == config)
{ {
float prevItemOffset = (g_prevSelectedRowIndex - g_selectedRowIndex) * (optionHeight + optionPadding); float prevItemOffset = (g_prevSelectedRowIndex - g_selectedRowIndex) * (optionHeight + optionPadding);
@@ -612,7 +880,27 @@ static void DrawConfigOption(int32_t rowIndex, float yOffset, ConfigDef<T>* conf
} }
else else
{ {
drawList->AddText(g_seuratFont, size, textPos, textColour, configName.c_str(), 0, 0.0f, &textClipRect); drawList->PushClipRect(min, max, true);
DrawRubyAnnotatedText
(
g_seuratFont,
size,
FLT_MAX,
textPos,
0.0f,
configName.c_str(),
[=](const char* str, ImVec2 pos)
{
DrawTextBasic(g_seuratFont, size, pos, textColour, str);
},
[=](const char* str, float annotationSize, ImVec2 pos)
{
DrawTextBasic(g_seuratFont, annotationSize, pos, textColour, str);
}
);
drawList->PopClipRect();
} }
// Right side // Right side
@@ -629,12 +917,19 @@ static void DrawConfigOption(int32_t rowIndex, float yOffset, ConfigDef<T>* conf
{ {
if constexpr (std::is_same_v<T, float> || std::is_same_v<T, int32_t>) if constexpr (std::is_same_v<T, float> || std::is_same_v<T, int32_t>)
{ {
if (lockedOnOption)
{
SetAdditive(true);
drawList->AddRectFilled(min, max, IM_COL32(192, 192, 0, 96 * ComputeMotion(g_lockedOnTime, 0, VALUE_SLIDER_INTRO_DURATION)));
SetAdditive(false);
}
// Inner container of slider // Inner container of slider
const uint32_t innerColor0 = IM_COL32(0, 65, 0, 255 * alpha); const uint32_t innerColor0 = IM_COL32(0, 65, 0, 255 * alpha);
const uint32_t innerColor1 = IM_COL32(0, 32, 0, 255 * alpha); const uint32_t innerColor1 = IM_COL32(0, 32, 0, 255 * alpha);
float xPadding = Scale(6.0f); float xPadding = Scale(6);
float yPadding = Scale(3.0f); float yPadding = Scale(3);
drawList->AddRectFilledMultiColor drawList->AddRectFilledMultiColor
( (
@@ -650,8 +945,8 @@ static void DrawConfigOption(int32_t rowIndex, float yOffset, ConfigDef<T>* conf
const uint32_t sliderColor0 = IM_COL32(57, 241, 0, 255 * alpha); const uint32_t sliderColor0 = IM_COL32(57, 241, 0, 255 * alpha);
const uint32_t sliderColor1 = IM_COL32(2, 106, 0, 255 * alpha); const uint32_t sliderColor1 = IM_COL32(2, 106, 0, 255 * alpha);
xPadding += Scale(1.0f); xPadding += Scale(2);
yPadding += Scale(1.0f); yPadding += Scale(2);
ImVec2 sliderMin = { min.x + xPadding, min.y + yPadding }; ImVec2 sliderMin = { min.x + xPadding, min.y + yPadding };
ImVec2 sliderMax = { max.x - xPadding, max.y - yPadding }; ImVec2 sliderMax = { max.x - xPadding, max.y - yPadding };
@@ -670,29 +965,12 @@ static void DrawConfigOption(int32_t rowIndex, float yOffset, ConfigDef<T>* conf
SetShaderModifier(IMGUI_SHADER_MODIFIER_NONE); SetShaderModifier(IMGUI_SHADER_MODIFIER_NONE);
if constexpr (std::is_same_v<T, bool>)
DrawToggleLight({ min.x + Scale(14), min.y + ((max.y - min.y) - Scale(14)) / 2 + Scale(1) }, config->Value, alpha);
// Selection triangles // Selection triangles
if (lockedOnOption) if (lockedOnOption)
{ {
constexpr uint32_t COLOR = IM_COL32(0, 97, 0, 255);
// Left
drawList->AddTriangleFilled
(
{ min.x - trianglePadding, min.y },
{ min.x - trianglePadding, max.y },
{ min.x - trianglePadding - triangleWidth, (min.y + max.y) / 2.0f },
COLOR
);
// Right
drawList->AddTriangleFilled
(
{ max.x + trianglePadding, max.y },
{ max.x + trianglePadding, min.y },
{ max.x + trianglePadding + triangleWidth, (min.y + max.y) / 2.0f },
COLOR
);
bool leftIsHeld = padState.IsDown(SWA::eKeyState_DpadLeft) || padState.LeftStickHorizontal < -0.5f; bool leftIsHeld = padState.IsDown(SWA::eKeyState_DpadLeft) || padState.LeftStickHorizontal < -0.5f;
bool rightIsHeld = padState.IsDown(SWA::eKeyState_DpadRight) || padState.LeftStickHorizontal > 0.5f; bool rightIsHeld = padState.IsDown(SWA::eKeyState_DpadRight) || padState.LeftStickHorizontal > 0.5f;
@@ -710,6 +988,8 @@ static void DrawConfigOption(int32_t rowIndex, float yOffset, ConfigDef<T>* conf
g_leftWasHeld = leftIsHeld; g_leftWasHeld = leftIsHeld;
g_rightWasHeld = rightIsHeld; g_rightWasHeld = rightIsHeld;
DrawSelectionArrows(min, max, leftTapped, rightTapped, isValueSlider);
if constexpr (std::is_enum_v<T>) if constexpr (std::is_enum_v<T>)
{ {
auto it = config->EnumTemplateReverse.find(config->Value); auto it = config->EnumTemplateReverse.find(config->Value);
@@ -896,8 +1176,8 @@ static void DrawConfigOptions()
break; break;
case 1: // INPUT case 1: // INPUT
DrawConfigOption(rowCount++, yOffset, &Config::InvertCameraX, true); DrawConfigOption(rowCount++, yOffset, &Config::HorizontalCamera, true);
DrawConfigOption(rowCount++, yOffset, &Config::InvertCameraY, true); DrawConfigOption(rowCount++, yOffset, &Config::VerticalCamera, true);
DrawConfigOption(rowCount++, yOffset, &Config::Vibration, true); DrawConfigOption(rowCount++, yOffset, &Config::Vibration, true);
DrawConfigOption(rowCount++, yOffset, &Config::AllowBackgroundInput, true); DrawConfigOption(rowCount++, yOffset, &Config::AllowBackgroundInput, true);
DrawConfigOption(rowCount++, yOffset, &Config::ControllerIcons, true); DrawConfigOption(rowCount++, yOffset, &Config::ControllerIcons, true);
@@ -1009,7 +1289,11 @@ static void DrawConfigOptions()
{ {
float totalHeight = (clipRectMax.y - clipRectMin.y); float totalHeight = (clipRectMax.y - clipRectMin.y);
float heightRatio = float(visibleRowCount) / float(rowCount); float heightRatio = float(visibleRowCount) / float(rowCount);
float offsetRatio = float(g_firstVisibleRowIndex) / float(rowCount); float offsetRatio = float(g_firstVisibleRowIndex) / float(rowCount);
offsetRatio = Lerp(g_prevOffsetRatio, offsetRatio, 1.0f - exp(-16.0f * ImGui::GetIO().DeltaTime));
g_prevOffsetRatio = offsetRatio;
float minY = offsetRatio * totalHeight + clipRectMin.y; float minY = offsetRatio * totalHeight + clipRectMin.y;
drawList->AddRectFilled drawList->AddRectFilled
@@ -1090,18 +1374,51 @@ static void DrawInfoPanel(ImVec2 infoMin, ImVec2 infoMax)
desc += "\n\n" + g_selectedItem->GetValueDescription(Config::Language); desc += "\n\n" + g_selectedItem->GetValueDescription(Config::Language);
} }
auto size = Scale(26.0f); auto fontSize = Scale(28.0f);
auto annotationFontSize = fontSize * ANNOTATION_FONT_SIZE_MODIFIER;
drawList->AddText // Extra padding between the start of the description text and the bottom of the thumbnail
float offsetY = Scale(24.0f);
float textX = clipRectMin.x - Scale(0.5f);
float textY = thumbnailMax.y + offsetY;
if (Config::Language == ELanguage::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
clipRectMin.x -= annotationFontSize;
clipRectMin.y -= annotationFontSize;
clipRectMax.x += annotationFontSize;
clipRectMax.y += annotationFontSize;
textY += annotationFontSize;
}
drawList->PushClipRect(clipRectMin, clipRectMax, false);
DrawRubyAnnotatedText
( (
g_seuratFont, g_seuratFont,
size, fontSize,
{ clipRectMin.x, thumbnailMax.y + size - 5.0f }, clipRectMax.x - clipRectMin.x,
IM_COL32_WHITE, { textX, textY },
5.0f,
desc.c_str(), desc.c_str(),
0,
clipRectMax.x - clipRectMin.x [=](const char* str, ImVec2 pos)
{
DrawTextBasic(g_seuratFont, fontSize, pos, IM_COL32(255, 255, 255, 255), str);
},
[=](const char* str, float size, ImVec2 pos)
{
DrawTextBasic(g_seuratFont, size, pos, IM_COL32(255, 255, 255, 255), str);
}
); );
drawList->PopClipRect();
} }
ResetProceduralOrigin(); ResetProceduralOrigin();
@@ -1113,7 +1430,7 @@ static void DrawInfoPanel(ImVec2 infoMin, ImVec2 infoMax)
static void SetOptionsMenuVisible(bool isVisible) static void SetOptionsMenuVisible(bool isVisible)
{ {
OptionsMenu::s_isVisible = isVisible; OptionsMenu::s_isVisible = isVisible;
*(bool*)g_memory.Translate(0x8328BB26) = !isVisible; *SWA::SGlobals::ms_IsRenderHud = !isVisible;
} }
static bool DrawMilesElectric() static bool DrawMilesElectric()
@@ -1229,18 +1546,19 @@ void OptionsMenu::Draw()
float infoGridCount = floor(Lerp(INFO_NARROW_GRID_COUNT, INFO_WIDE_GRID_COUNT, g_aspectRatioNarrowScale)); float infoGridCount = floor(Lerp(INFO_NARROW_GRID_COUNT, INFO_WIDE_GRID_COUNT, g_aspectRatioNarrowScale));
float totalGridCount = settingsGridCount + paddingGridCount + infoGridCount; float totalGridCount = settingsGridCount + paddingGridCount + infoGridCount;
float offsetX = (1280.0f - ((GRID_SIZE * totalGridCount) - 1)) / 2.0f; float minX = round(g_aspectRatioOffsetX + Scale((1280.0f - (GRID_SIZE * totalGridCount)) / 2.0f));
float minY = g_aspectRatioOffsetY + Scale(CONTAINER_POS_Y); float maxX = res.x - minX;
float maxY = g_aspectRatioOffsetY + Scale((720.0f - CONTAINER_POS_Y + 1.0f)); float minY = round(g_aspectRatioOffsetY + Scale(CONTAINER_POS_Y));
float maxY = round(g_aspectRatioOffsetY + Scale((720.0f - CONTAINER_POS_Y + 1.0f)));
DrawSettingsPanel( DrawSettingsPanel(
{ g_aspectRatioOffsetX + Scale(offsetX), minY }, { minX, minY },
{ g_aspectRatioOffsetX + Scale(offsetX + settingsGridCount * GRID_SIZE), maxY } { minX + Scale(settingsGridCount * GRID_SIZE), maxY }
); );
DrawInfoPanel( DrawInfoPanel(
{ g_aspectRatioOffsetX + Scale(offsetX + (settingsGridCount + paddingGridCount) * GRID_SIZE), minY }, { maxX - Scale(infoGridCount * GRID_SIZE) - 1.0f, minY },
{ g_aspectRatioOffsetX + Scale(offsetX + totalGridCount * GRID_SIZE), maxY } { maxX - 1.0f, maxY }
); );
if (g_isStage) if (g_isStage)
@@ -1262,6 +1580,7 @@ void OptionsMenu::Open(bool isPause, SWA::EMenuType pauseMenuType)
g_categoryAnimMin = { 0.0f, 0.0f }; g_categoryAnimMin = { 0.0f, 0.0f };
g_categoryAnimMax = { 0.0f, 0.0f }; g_categoryAnimMax = { 0.0f, 0.0f };
g_selectedItem = nullptr; g_selectedItem = nullptr;
g_titleAnimBegin = true;
/* Store button state so we can track it later /* Store button state so we can track it later
and prevent the first item being selected. */ and prevent the first item being selected. */
@@ -17,8 +17,7 @@
#include <res/images/options_menu/thumbnails/gi_texture_filtering_bilinear.dds.h> #include <res/images/options_menu/thumbnails/gi_texture_filtering_bilinear.dds.h>
#include <res/images/options_menu/thumbnails/gi_texture_filtering_bicubic.dds.h> #include <res/images/options_menu/thumbnails/gi_texture_filtering_bicubic.dds.h>
#include <res/images/options_menu/thumbnails/hints.dds.h> #include <res/images/options_menu/thumbnails/hints.dds.h>
#include <res/images/options_menu/thumbnails/invert_camera_x.dds.h> #include <res/images/options_menu/thumbnails/horizontal_camera.dds.h>
#include <res/images/options_menu/thumbnails/invert_camera_y.dds.h>
#include <res/images/options_menu/thumbnails/language.dds.h> #include <res/images/options_menu/thumbnails/language.dds.h>
#include <res/images/options_menu/thumbnails/master_volume.dds.h> #include <res/images/options_menu/thumbnails/master_volume.dds.h>
#include <res/images/options_menu/thumbnails/monitor.dds.h> #include <res/images/options_menu/thumbnails/monitor.dds.h>
@@ -41,6 +40,7 @@
#include <res/images/options_menu/thumbnails/transparency_antialiasing_false.dds.h> #include <res/images/options_menu/thumbnails/transparency_antialiasing_false.dds.h>
#include <res/images/options_menu/thumbnails/transparency_antialiasing_true.dds.h> #include <res/images/options_menu/thumbnails/transparency_antialiasing_true.dds.h>
#include <res/images/options_menu/thumbnails/ui_scale_mode.dds.h> #include <res/images/options_menu/thumbnails/ui_scale_mode.dds.h>
#include <res/images/options_menu/thumbnails/vertical_camera.dds.h>
#include <res/images/options_menu/thumbnails/voice_language.dds.h> #include <res/images/options_menu/thumbnails/voice_language.dds.h>
#include <res/images/options_menu/thumbnails/vibration.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.dds.h>
@@ -74,8 +74,8 @@ void LoadThumbnails()
g_timeOfDayTransitionThumbnails[ETimeOfDayTransition::Xbox] = LOAD_ZSTD_TEXTURE(g_time_of_day_transition_xbox); g_timeOfDayTransitionThumbnails[ETimeOfDayTransition::Xbox] = LOAD_ZSTD_TEXTURE(g_time_of_day_transition_xbox);
g_timeOfDayTransitionThumbnails[ETimeOfDayTransition::PlayStation] = LOAD_ZSTD_TEXTURE(g_time_of_day_transition_playstation); g_timeOfDayTransitionThumbnails[ETimeOfDayTransition::PlayStation] = LOAD_ZSTD_TEXTURE(g_time_of_day_transition_playstation);
g_configThumbnails[&Config::InvertCameraX] = LOAD_ZSTD_TEXTURE(g_invert_camera_x); g_configThumbnails[&Config::HorizontalCamera] = LOAD_ZSTD_TEXTURE(g_horizontal_camera);
g_configThumbnails[&Config::InvertCameraY] = LOAD_ZSTD_TEXTURE(g_invert_camera_y); g_configThumbnails[&Config::VerticalCamera] = LOAD_ZSTD_TEXTURE(g_vertical_camera);
g_configThumbnails[&Config::Vibration] = LOAD_ZSTD_TEXTURE(g_vibration); g_configThumbnails[&Config::Vibration] = LOAD_ZSTD_TEXTURE(g_vibration);
g_configThumbnails[&Config::AllowBackgroundInput] = LOAD_ZSTD_TEXTURE(g_allow_background_input); g_configThumbnails[&Config::AllowBackgroundInput] = LOAD_ZSTD_TEXTURE(g_allow_background_input);
g_configThumbnails[&Config::ControllerIcons] = LOAD_ZSTD_TEXTURE(g_controller_icons); g_configThumbnails[&Config::ControllerIcons] = LOAD_ZSTD_TEXTURE(g_controller_icons);
+11 -157
View File
@@ -1,70 +1,22 @@
#include "achievement_data.h" #include "achievement_data.h"
#include <ui/achievement_overlay.h>
#include <user/config.h>
#include <os/logger.h>
#define NUM_RECORDS sizeof(Data.Records) / sizeof(Record) #define NUM_RECORDS sizeof(Records) / sizeof(AchRecord)
time_t AchievementData::GetTimestamp(uint16_t id) bool AchievementData::VerifySignature() const
{ {
for (int i = 0; i < NUM_RECORDS; i++) char sig[4] = ACH_SIGNATURE;
{
if (!Data.Records[i].ID)
break;
if (Data.Records[i].ID == id) return memcmp(Signature, sig, sizeof(Signature)) == 0;
return Data.Records[i].Timestamp;
} }
return 0; bool AchievementData::VerifyVersion() const
{
return Version == AchVersion ACH_VERSION;
} }
int AchievementData::GetTotalRecords() bool AchievementData::VerifyChecksum()
{ {
auto result = 0; return Checksum == CalculateChecksum();
for (int i = 0; i < NUM_RECORDS; i++)
{
if (!Data.Records[i].ID)
break;
result++;
}
return result;
}
bool AchievementData::IsUnlocked(uint16_t id)
{
for (int i = 0; i < NUM_RECORDS; i++)
{
if (!Data.Records[i].ID)
break;
if (Data.Records[i].ID == id)
return true;
}
return false;
}
void AchievementData::Unlock(uint16_t id)
{
if (IsUnlocked(id))
return;
for (int i = 0; i < NUM_RECORDS; i++)
{
if (Data.Records[i].ID == 0)
{
Data.Records[i].ID = id;
Data.Records[i].Timestamp = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
break;
}
}
if (Config::AchievementNotifications)
AchievementOverlay::Open(id);
} }
uint32_t AchievementData::CalculateChecksum() uint32_t AchievementData::CalculateChecksum()
@@ -73,109 +25,11 @@ uint32_t AchievementData::CalculateChecksum()
for (int i = 0; i < NUM_RECORDS; i++) for (int i = 0; i < NUM_RECORDS; i++)
{ {
auto& record = Data.Records[i]; auto& record = Records[i];
for (size_t j = 0; j < sizeof(Record); j++) for (size_t j = 0; j < sizeof(AchRecord); j++)
result ^= ((uint8_t*)(&record))[j]; result ^= ((uint8_t*)(&record))[j];
} }
return result; return result;
} }
bool AchievementData::VerifySignature()
{
char sig[4] = ACH_SIGNATURE;
return Data.Signature[0] == sig[0] &&
Data.Signature[1] == sig[1] &&
Data.Signature[2] == sig[2] &&
Data.Signature[3] == sig[3];
}
bool AchievementData::VerifyVersion()
{
return Data.Version == Version ACH_VERSION;
}
bool AchievementData::VerifyChecksum()
{
return Data.Checksum == CalculateChecksum();
}
void AchievementData::Load()
{
auto dataPath = GetDataPath(true);
if (!std::filesystem::exists(dataPath))
{
// Try loading base achievement data as fallback.
dataPath = GetDataPath(false);
if (!std::filesystem::exists(dataPath))
return;
}
std::ifstream file(dataPath, std::ios::binary);
if (!file)
{
LOGN_ERROR("Failed to read achievement data.");
return;
}
file.read((char*)&Data.Signature, sizeof(Data.Signature));
if (!VerifySignature())
{
LOGN_ERROR("Invalid achievement data signature.");
char sig[4] = ACH_SIGNATURE;
Data.Signature[0] = sig[0];
Data.Signature[1] = sig[1];
Data.Signature[2] = sig[2];
Data.Signature[3] = sig[3];
file.close();
return;
}
file.read((char*)&Data.Version, sizeof(Data.Version));
if (!VerifyVersion())
{
LOGN_ERROR("Unsupported achievement data version.");
Data.Version = ACH_VERSION;
file.close();
return;
}
file.seekg(0);
file.read((char*)&Data, sizeof(Data));
// TODO: display error message to user before wiping data?
if (!VerifyChecksum())
{
LOGN_WARNING("Achievement data checksum mismatch.");
memset(&Data.Records, 0, sizeof(Data.Records));
}
file.close();
}
void AchievementData::Save()
{
std::ofstream file(GetDataPath(true), std::ios::binary);
if (!file)
{
LOGN_ERROR("Failed to write achievement data.");
return;
}
Data.Checksum = CalculateChecksum();
file.write((const char*)&Data, sizeof(Data));
file.close();
}
+19 -35
View File
@@ -2,6 +2,7 @@
#include <user/paths.h> #include <user/paths.h>
#define ACH_FILENAME "ACH-DATA"
#define ACH_SIGNATURE { 'A', 'C', 'H', ' ' } #define ACH_SIGNATURE { 'A', 'C', 'H', ' ' }
#define ACH_VERSION { 1, 0, 0 } #define ACH_VERSION { 1, 0, 0 }
#define ACH_RECORDS 50 #define ACH_RECORDS 50
@@ -9,23 +10,14 @@
class AchievementData class AchievementData
{ {
public: public:
#pragma pack(push, 1) struct AchVersion
struct Record
{
uint16_t ID;
time_t Timestamp;
uint16_t Reserved[3];
};
#pragma pack(pop)
struct Version
{ {
uint8_t Major; uint8_t Major;
uint8_t Minor; uint8_t Minor;
uint8_t Revision; uint8_t Revision;
uint8_t Reserved; uint8_t Reserved;
bool operator==(const Version& other) const bool operator==(const AchVersion& other) const
{ {
return Major == other.Major && return Major == other.Major &&
Minor == other.Minor && Minor == other.Minor &&
@@ -33,31 +25,23 @@ public:
} }
}; };
class Data #pragma pack(push, 1)
struct AchRecord
{ {
public: uint16_t ID;
char Signature[4]; time_t Timestamp;
Version Version{}; uint16_t Reserved[3];
};
#pragma pack(pop)
char Signature[4] ACH_SIGNATURE;
AchVersion Version ACH_VERSION;
uint32_t Checksum; uint32_t Checksum;
uint32_t Reserved; uint32_t Reserved;
Record Records[ACH_RECORDS]; AchRecord Records[ACH_RECORDS];
};
bool VerifySignature() const;
static inline Data Data{ ACH_SIGNATURE, ACH_VERSION }; bool VerifyVersion() const;
bool VerifyChecksum();
static std::filesystem::path GetDataPath(bool checkForMods) uint32_t CalculateChecksum();
{
return GetSavePath(checkForMods) / "ACH-DATA";
}
static time_t GetTimestamp(uint16_t id);
static int GetTotalRecords();
static bool IsUnlocked(uint16_t id);
static void Unlock(uint16_t id);
static uint32_t CalculateChecksum();
static bool VerifySignature();
static bool VerifyVersion();
static bool VerifyChecksum();
static void Load();
static void Save();
}; };
@@ -0,0 +1,164 @@
#include "achievement_manager.h"
#include <os/logger.h>
#include <ui/achievement_overlay.h>
#include <user/config.h>
#define NUM_RECORDS sizeof(AchievementManager::Data.Records) / sizeof(AchievementData::AchRecord)
time_t AchievementManager::GetTimestamp(uint16_t id)
{
for (int i = 0; i < NUM_RECORDS; i++)
{
if (!Data.Records[i].ID)
break;
if (Data.Records[i].ID == id)
return Data.Records[i].Timestamp;
}
return 0;
}
size_t AchievementManager::GetTotalRecords()
{
auto result = 0;
for (int i = 0; i < NUM_RECORDS; i++)
{
if (!Data.Records[i].ID)
break;
result++;
}
return result;
}
bool AchievementManager::IsUnlocked(uint16_t id)
{
for (int i = 0; i < NUM_RECORDS; i++)
{
if (!Data.Records[i].ID)
break;
if (Data.Records[i].ID == id)
return true;
}
return false;
}
void AchievementManager::Unlock(uint16_t id)
{
if (IsUnlocked(id))
return;
for (int i = 0; i < NUM_RECORDS; i++)
{
if (Data.Records[i].ID == 0)
{
Data.Records[i].ID = id;
Data.Records[i].Timestamp = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
break;
}
}
if (Config::AchievementNotifications)
AchievementOverlay::Open(id);
}
void AchievementManager::Load()
{
Data = {};
Status = EAchStatus::Success;
auto dataPath = GetDataPath(true);
if (!std::filesystem::exists(dataPath))
{
// Try loading base achievement data as fallback.
dataPath = GetDataPath(false);
if (!std::filesystem::exists(dataPath))
return;
}
std::error_code ec;
auto fileSize = std::filesystem::file_size(dataPath, ec);
auto dataSize = sizeof(AchievementData);
if (fileSize != dataSize)
{
Status = EAchStatus::BadFileSize;
return;
}
std::ifstream file(dataPath, std::ios::binary);
if (!file)
{
Status = EAchStatus::IOError;
return;
}
AchievementData data{};
file.read((char*)&data.Signature, sizeof(data.Signature));
if (!data.VerifySignature())
{
Status = EAchStatus::BadSignature;
file.close();
return;
}
file.read((char*)&data.Version, sizeof(data.Version));
// TODO: upgrade in future if the version changes.
if (!data.VerifyVersion())
{
Status = EAchStatus::BadVersion;
file.close();
return;
}
file.seekg(0);
file.read((char*)&data, sizeof(data));
if (!data.VerifyChecksum())
{
Status = EAchStatus::BadChecksum;
file.close();
return;
}
file.close();
memcpy(&Data, &data, dataSize);
}
void AchievementManager::Save(bool ignoreStatus)
{
if (!ignoreStatus && Status != EAchStatus::Success)
{
LOGN_WARNING("Achievement data will not be saved in this session!");
return;
}
LOGN("Saving achievements...");
std::ofstream file(GetDataPath(true), std::ios::binary);
if (!file)
{
LOGN_ERROR("Failed to write achievement data.");
return;
}
Data.Checksum = Data.CalculateChecksum();
file.write((const char*)&Data, sizeof(AchievementData));
file.close();
Status = EAchStatus::Success;
}
@@ -0,0 +1,32 @@
#pragma once
#include <user/achievement_data.h>
enum class EAchStatus
{
Success,
IOError,
BadFileSize,
BadSignature,
BadVersion,
BadChecksum
};
class AchievementManager
{
public:
static inline AchievementData Data{};
static inline EAchStatus Status{};
static std::filesystem::path GetDataPath(bool checkForMods)
{
return GetSavePath(checkForMods) / ACH_FILENAME;
}
static time_t GetTimestamp(uint16_t id);
static size_t GetTotalRecords();
static bool IsUnlocked(uint16_t id);
static void Unlock(uint16_t id);
static void Load();
static void Save(bool ignoreStatus = false);
};
+257 -6
View File
@@ -18,18 +18,18 @@ CONFIG_DEFINE_ENUM_TEMPLATE(ELanguage)
{ "Italian", ELanguage::Italian } { "Italian", ELanguage::Italian }
}; };
CONFIG_DEFINE_ENUM_TEMPLATE(EUnleashGaugeBehaviour)
{
{ "Original", EUnleashGaugeBehaviour::Original },
{ "Revised", EUnleashGaugeBehaviour::Revised }
};
CONFIG_DEFINE_ENUM_TEMPLATE(ETimeOfDayTransition) CONFIG_DEFINE_ENUM_TEMPLATE(ETimeOfDayTransition)
{ {
{ "Xbox", ETimeOfDayTransition::Xbox }, { "Xbox", ETimeOfDayTransition::Xbox },
{ "PlayStation", ETimeOfDayTransition::PlayStation } { "PlayStation", ETimeOfDayTransition::PlayStation }
}; };
CONFIG_DEFINE_ENUM_TEMPLATE(ECameraRotationMode)
{
{ "Normal", ECameraRotationMode::Normal },
{ "Reverse", ECameraRotationMode::Reverse },
};
CONFIG_DEFINE_ENUM_TEMPLATE(EControllerIcons) CONFIG_DEFINE_ENUM_TEMPLATE(EControllerIcons)
{ {
{ "Auto", EControllerIcons::Auto }, { "Auto", EControllerIcons::Auto },
@@ -37,6 +37,257 @@ CONFIG_DEFINE_ENUM_TEMPLATE(EControllerIcons)
{ "PlayStation", EControllerIcons::PlayStation } { "PlayStation", EControllerIcons::PlayStation }
}; };
CONFIG_DEFINE_ENUM_TEMPLATE(SDL_Scancode)
{
{ "???", SDL_SCANCODE_UNKNOWN },
{ "A", SDL_SCANCODE_A },
{ "B", SDL_SCANCODE_B },
{ "C", SDL_SCANCODE_C },
{ "D", SDL_SCANCODE_D },
{ "E", SDL_SCANCODE_E },
{ "F", SDL_SCANCODE_F },
{ "G", SDL_SCANCODE_G },
{ "H", SDL_SCANCODE_H },
{ "I", SDL_SCANCODE_I },
{ "J", SDL_SCANCODE_J },
{ "K", SDL_SCANCODE_K },
{ "L", SDL_SCANCODE_L },
{ "M", SDL_SCANCODE_M },
{ "N", SDL_SCANCODE_N },
{ "O", SDL_SCANCODE_O },
{ "P", SDL_SCANCODE_P },
{ "Q", SDL_SCANCODE_Q },
{ "R", SDL_SCANCODE_R },
{ "S", SDL_SCANCODE_S },
{ "T", SDL_SCANCODE_T },
{ "U", SDL_SCANCODE_U },
{ "V", SDL_SCANCODE_V },
{ "W", SDL_SCANCODE_W },
{ "X", SDL_SCANCODE_X },
{ "Y", SDL_SCANCODE_Y },
{ "Z", SDL_SCANCODE_Z },
{ "1", SDL_SCANCODE_1 },
{ "2", SDL_SCANCODE_2 },
{ "3", SDL_SCANCODE_3 },
{ "4", SDL_SCANCODE_4 },
{ "5", SDL_SCANCODE_5 },
{ "6", SDL_SCANCODE_6 },
{ "7", SDL_SCANCODE_7 },
{ "8", SDL_SCANCODE_8 },
{ "9", SDL_SCANCODE_9 },
{ "0", SDL_SCANCODE_0 },
{ "RETURN", SDL_SCANCODE_RETURN },
{ "ESCAPE", SDL_SCANCODE_ESCAPE },
{ "BACKSPACE", SDL_SCANCODE_BACKSPACE },
{ "TAB", SDL_SCANCODE_TAB },
{ "SPACE", SDL_SCANCODE_SPACE },
{ "MINUS", SDL_SCANCODE_MINUS },
{ "EQUALS", SDL_SCANCODE_EQUALS },
{ "LEFT BRACKET", SDL_SCANCODE_LEFTBRACKET },
{ "RIGHT BRACKET", SDL_SCANCODE_RIGHTBRACKET },
{ "BACKSLASH", SDL_SCANCODE_BACKSLASH },
{ "NON-US HASH", SDL_SCANCODE_NONUSHASH },
{ "SEMICOLON", SDL_SCANCODE_SEMICOLON },
{ "APOSTROPHE", SDL_SCANCODE_APOSTROPHE },
{ "GRAVE", SDL_SCANCODE_GRAVE },
{ "COMMA", SDL_SCANCODE_COMMA },
{ "PERIOD", SDL_SCANCODE_PERIOD },
{ "SLASH", SDL_SCANCODE_SLASH },
{ "CAPS LOCK", SDL_SCANCODE_CAPSLOCK },
{ "F1", SDL_SCANCODE_F1 },
{ "F2", SDL_SCANCODE_F2 },
{ "F3", SDL_SCANCODE_F3 },
{ "F4", SDL_SCANCODE_F4 },
{ "F5", SDL_SCANCODE_F5 },
{ "F6", SDL_SCANCODE_F6 },
{ "F7", SDL_SCANCODE_F7 },
{ "F8", SDL_SCANCODE_F8 },
{ "F9", SDL_SCANCODE_F9 },
{ "F10", SDL_SCANCODE_F10 },
{ "F11", SDL_SCANCODE_F11 },
{ "F12", SDL_SCANCODE_F12 },
{ "PRINT SCREEN", SDL_SCANCODE_PRINTSCREEN },
{ "SCROLL LOCK", SDL_SCANCODE_SCROLLLOCK },
{ "PAUSE", SDL_SCANCODE_PAUSE },
{ "INSERT", SDL_SCANCODE_INSERT },
{ "HOME", SDL_SCANCODE_HOME },
{ "PAGE UP", SDL_SCANCODE_PAGEUP },
{ "DELETE", SDL_SCANCODE_DELETE },
{ "END", SDL_SCANCODE_END },
{ "PAGE DOWN", SDL_SCANCODE_PAGEDOWN },
{ "RIGHT", SDL_SCANCODE_RIGHT },
{ "LEFT", SDL_SCANCODE_LEFT },
{ "DOWN", SDL_SCANCODE_DOWN },
{ "UP", SDL_SCANCODE_UP },
{ "NUM LOCK", SDL_SCANCODE_NUMLOCKCLEAR },
{ "KP DIVIDE", SDL_SCANCODE_KP_DIVIDE },
{ "KP MULTIPLY", SDL_SCANCODE_KP_MULTIPLY },
{ "KP MINUS", SDL_SCANCODE_KP_MINUS },
{ "KP PLUS", SDL_SCANCODE_KP_PLUS },
{ "KP ENTER", SDL_SCANCODE_KP_ENTER },
{ "KP 1", SDL_SCANCODE_KP_1 },
{ "KP 2", SDL_SCANCODE_KP_2 },
{ "KP 3", SDL_SCANCODE_KP_3 },
{ "KP 4", SDL_SCANCODE_KP_4 },
{ "KP 5", SDL_SCANCODE_KP_5 },
{ "KP 6", SDL_SCANCODE_KP_6 },
{ "KP 7", SDL_SCANCODE_KP_7 },
{ "KP 8", SDL_SCANCODE_KP_8 },
{ "KP 9", SDL_SCANCODE_KP_9 },
{ "KP 0", SDL_SCANCODE_KP_0 },
{ "KP PERIOD", SDL_SCANCODE_KP_PERIOD },
{ "NON-US BACKSLASH", SDL_SCANCODE_NONUSBACKSLASH },
{ "APPLICATION", SDL_SCANCODE_APPLICATION },
{ "POWER", SDL_SCANCODE_POWER },
{ "KP EQUALS", SDL_SCANCODE_KP_EQUALS },
{ "F13", SDL_SCANCODE_F13 },
{ "F14", SDL_SCANCODE_F14 },
{ "F15", SDL_SCANCODE_F15 },
{ "F16", SDL_SCANCODE_F16 },
{ "F17", SDL_SCANCODE_F17 },
{ "F18", SDL_SCANCODE_F18 },
{ "F19", SDL_SCANCODE_F19 },
{ "F20", SDL_SCANCODE_F20 },
{ "F21", SDL_SCANCODE_F21 },
{ "F22", SDL_SCANCODE_F22 },
{ "F23", SDL_SCANCODE_F23 },
{ "F24", SDL_SCANCODE_F24 },
{ "EXECUTE", SDL_SCANCODE_EXECUTE },
{ "HELP", SDL_SCANCODE_HELP },
{ "MENU", SDL_SCANCODE_MENU },
{ "SELECT", SDL_SCANCODE_SELECT },
{ "STOP", SDL_SCANCODE_STOP },
{ "AGAIN", SDL_SCANCODE_AGAIN },
{ "UNDO", SDL_SCANCODE_UNDO },
{ "CUT", SDL_SCANCODE_CUT },
{ "COPY", SDL_SCANCODE_COPY },
{ "PASTE", SDL_SCANCODE_PASTE },
{ "FIND", SDL_SCANCODE_FIND },
{ "MUTE", SDL_SCANCODE_MUTE },
{ "VOLUME UP", SDL_SCANCODE_VOLUMEUP },
{ "VOLUME DOWN", SDL_SCANCODE_VOLUMEDOWN },
{ "KP COMMA", SDL_SCANCODE_KP_COMMA },
{ "KP EQUALS AS400", SDL_SCANCODE_KP_EQUALSAS400 },
{ "INTERNATIONAL 1", SDL_SCANCODE_INTERNATIONAL1 },
{ "INTERNATIONAL 2", SDL_SCANCODE_INTERNATIONAL2 },
{ "INTERNATIONAL 3", SDL_SCANCODE_INTERNATIONAL3 },
{ "INTERNATIONAL 4", SDL_SCANCODE_INTERNATIONAL4 },
{ "INTERNATIONAL 5", SDL_SCANCODE_INTERNATIONAL5 },
{ "INTERNATIONAL 6", SDL_SCANCODE_INTERNATIONAL6 },
{ "INTERNATIONAL 7", SDL_SCANCODE_INTERNATIONAL7 },
{ "INTERNATIONAL 8", SDL_SCANCODE_INTERNATIONAL8 },
{ "INTERNATIONAL 9", SDL_SCANCODE_INTERNATIONAL9 },
{ "LANG 1", SDL_SCANCODE_LANG1 },
{ "LANG 2", SDL_SCANCODE_LANG2 },
{ "LANG 3", SDL_SCANCODE_LANG3 },
{ "LANG 4", SDL_SCANCODE_LANG4 },
{ "LANG 5", SDL_SCANCODE_LANG5 },
{ "LANG 6", SDL_SCANCODE_LANG6 },
{ "LANG 7", SDL_SCANCODE_LANG7 },
{ "LANG 8", SDL_SCANCODE_LANG8 },
{ "LANG 9", SDL_SCANCODE_LANG9 },
{ "ALT ERASE", SDL_SCANCODE_ALTERASE },
{ "SYS REQ", SDL_SCANCODE_SYSREQ },
{ "CANCEL", SDL_SCANCODE_CANCEL },
{ "CLEAR", SDL_SCANCODE_CLEAR },
{ "PRIOR", SDL_SCANCODE_PRIOR },
{ "RETURN 2", SDL_SCANCODE_RETURN2 },
{ "SEPARATOR", SDL_SCANCODE_SEPARATOR },
{ "OUT", SDL_SCANCODE_OUT },
{ "OPER", SDL_SCANCODE_OPER },
{ "CLEAR AGAIN", SDL_SCANCODE_CLEARAGAIN },
{ "CR SEL", SDL_SCANCODE_CRSEL },
{ "EX SEL", SDL_SCANCODE_EXSEL },
{ "KP 00", SDL_SCANCODE_KP_00 },
{ "KP 000", SDL_SCANCODE_KP_000 },
{ "THOUSANDS SEPARATOR", SDL_SCANCODE_THOUSANDSSEPARATOR },
{ "DECIMAL SEPARATOR", SDL_SCANCODE_DECIMALSEPARATOR },
{ "CURRENCY UNIT", SDL_SCANCODE_CURRENCYUNIT },
{ "CURRENCY SUBUNIT", SDL_SCANCODE_CURRENCYSUBUNIT },
{ "KP LEFT PAREN", SDL_SCANCODE_KP_LEFTPAREN },
{ "KP RIGHT PAREN", SDL_SCANCODE_KP_RIGHTPAREN },
{ "KP LEFT BRACE", SDL_SCANCODE_KP_LEFTBRACE },
{ "KP RIGHT BRACE", SDL_SCANCODE_KP_RIGHTBRACE },
{ "KP TAB", SDL_SCANCODE_KP_TAB },
{ "KP BACKSPACE", SDL_SCANCODE_KP_BACKSPACE },
{ "KP A", SDL_SCANCODE_KP_A },
{ "KP B", SDL_SCANCODE_KP_B },
{ "KP C", SDL_SCANCODE_KP_C },
{ "KP D", SDL_SCANCODE_KP_D },
{ "KP E", SDL_SCANCODE_KP_E },
{ "KP F", SDL_SCANCODE_KP_F },
{ "KP XOR", SDL_SCANCODE_KP_XOR },
{ "KP POWER", SDL_SCANCODE_KP_POWER },
{ "KP PERCENT", SDL_SCANCODE_KP_PERCENT },
{ "KP LESS", SDL_SCANCODE_KP_LESS },
{ "KP GREATER", SDL_SCANCODE_KP_GREATER },
{ "KP AMPERSAND", SDL_SCANCODE_KP_AMPERSAND },
{ "KP DBL AMPERSAND", SDL_SCANCODE_KP_DBLAMPERSAND },
{ "KP VERTICAL BAR", SDL_SCANCODE_KP_VERTICALBAR },
{ "KP DBL VERTICAL BAR", SDL_SCANCODE_KP_DBLVERTICALBAR },
{ "KP COLON", SDL_SCANCODE_KP_COLON },
{ "KP HASH", SDL_SCANCODE_KP_HASH },
{ "KP SPACE", SDL_SCANCODE_KP_SPACE },
{ "KP AT", SDL_SCANCODE_KP_AT },
{ "KP EXCLAM", SDL_SCANCODE_KP_EXCLAM },
{ "KP MEM STORE", SDL_SCANCODE_KP_MEMSTORE },
{ "KP MEM RECALL", SDL_SCANCODE_KP_MEMRECALL },
{ "KP MEM CLEAR", SDL_SCANCODE_KP_MEMCLEAR },
{ "KP MEM ADD", SDL_SCANCODE_KP_MEMADD },
{ "KP MEM SUBTRACT", SDL_SCANCODE_KP_MEMSUBTRACT },
{ "KP MEM MULTIPLY", SDL_SCANCODE_KP_MEMMULTIPLY },
{ "KP MEM DIVIDE", SDL_SCANCODE_KP_MEMDIVIDE },
{ "KP PLUS/MINUS", SDL_SCANCODE_KP_PLUSMINUS },
{ "KP CLEAR", SDL_SCANCODE_KP_CLEAR },
{ "KP CLEAR ENTRY", SDL_SCANCODE_KP_CLEARENTRY },
{ "KP BINARY", SDL_SCANCODE_KP_BINARY },
{ "KP OCTAL", SDL_SCANCODE_KP_OCTAL },
{ "KP DECIMAL", SDL_SCANCODE_KP_DECIMAL },
{ "KP HEXADECIMAL", SDL_SCANCODE_KP_HEXADECIMAL },
{ "LEFT CTRL", SDL_SCANCODE_LCTRL },
{ "LEFT SHIFT", SDL_SCANCODE_LSHIFT },
{ "LEFT ALT", SDL_SCANCODE_LALT },
{ "LEFT SUPER", SDL_SCANCODE_LGUI },
{ "RIGHT CTRL", SDL_SCANCODE_RCTRL },
{ "RIGHT SHIFT", SDL_SCANCODE_RSHIFT },
{ "RIGHT ALT", SDL_SCANCODE_RALT },
{ "RIGHT SUPER", SDL_SCANCODE_RGUI },
{ "MODE", SDL_SCANCODE_MODE },
{ "AUDIO NEXT", SDL_SCANCODE_AUDIONEXT },
{ "AUDIO PREV", SDL_SCANCODE_AUDIOPREV },
{ "AUDIO STOP", SDL_SCANCODE_AUDIOSTOP },
{ "AUDIO PLAY", SDL_SCANCODE_AUDIOPLAY },
{ "AUDIO MUTE", SDL_SCANCODE_AUDIOMUTE },
{ "MEDIA SELECT", SDL_SCANCODE_MEDIASELECT },
{ "WWW", SDL_SCANCODE_WWW },
{ "MAIL", SDL_SCANCODE_MAIL },
{ "CALCULATOR", SDL_SCANCODE_CALCULATOR },
{ "COMPUTER", SDL_SCANCODE_COMPUTER },
{ "AC SEARCH", SDL_SCANCODE_AC_SEARCH },
{ "AC HOME", SDL_SCANCODE_AC_HOME },
{ "AC BACK", SDL_SCANCODE_AC_BACK },
{ "AC FORWARD", SDL_SCANCODE_AC_FORWARD },
{ "AC STOP", SDL_SCANCODE_AC_STOP },
{ "AC REFRESH", SDL_SCANCODE_AC_REFRESH },
{ "AC BOOKMARKS", SDL_SCANCODE_AC_BOOKMARKS },
{ "BRIGHTNESS DOWN", SDL_SCANCODE_BRIGHTNESSDOWN },
{ "BRIGHTNESS UP", SDL_SCANCODE_BRIGHTNESSUP },
{ "DISPLAY SWITCH", SDL_SCANCODE_DISPLAYSWITCH },
{ "KBD ILLUM TOGGLE", SDL_SCANCODE_KBDILLUMTOGGLE },
{ "KBD ILLUM DOWN", SDL_SCANCODE_KBDILLUMDOWN },
{ "KBD ILLUM UP", SDL_SCANCODE_KBDILLUMUP },
{ "EJECT", SDL_SCANCODE_EJECT },
{ "SLEEP", SDL_SCANCODE_SLEEP },
{ "APP 1", SDL_SCANCODE_APP1 },
{ "APP 2", SDL_SCANCODE_APP2 },
{ "AUDIO REWIND", SDL_SCANCODE_AUDIOREWIND },
{ "AUDIO FAST FORWARD", SDL_SCANCODE_AUDIOFASTFORWARD },
{ "SOFT LEFT", SDL_SCANCODE_SOFTLEFT },
{ "SOFT RIGHT", SDL_SCANCODE_SOFTRIGHT },
{ "CALL", SDL_SCANCODE_CALL },
{ "END CALL", SDL_SCANCODE_ENDCALL },
};
CONFIG_DEFINE_ENUM_TEMPLATE(EChannelConfiguration) CONFIG_DEFINE_ENUM_TEMPLATE(EChannelConfiguration)
{ {
{ "Stereo", EChannelConfiguration::Stereo }, { "Stereo", EChannelConfiguration::Stereo },
+9 -9
View File
@@ -29,10 +29,10 @@ public:
extern std::vector<IConfigDef*> g_configDefinitions; extern std::vector<IConfigDef*> g_configDefinitions;
enum class EUnleashGaugeBehaviour : uint32_t enum class EVoiceLanguage : uint32_t
{ {
Original, English,
Revised Japanese
}; };
enum class ETimeOfDayTransition : uint32_t enum class ETimeOfDayTransition : uint32_t
@@ -41,6 +41,12 @@ enum class ETimeOfDayTransition : uint32_t
PlayStation PlayStation
}; };
enum class ECameraRotationMode : uint32_t
{
Normal,
Reverse
};
enum class EControllerIcons : uint32_t enum class EControllerIcons : uint32_t
{ {
Auto, Auto,
@@ -54,12 +60,6 @@ enum class EChannelConfiguration : uint32_t
Surround Surround
}; };
enum class EVoiceLanguage : uint32_t
{
English,
Japanese
};
enum class EGraphicsAPI : uint32_t enum class EGraphicsAPI : uint32_t
{ {
#ifdef UNLEASHED_RECOMP_D3D12 #ifdef UNLEASHED_RECOMP_D3D12
+30 -2
View File
@@ -8,13 +8,37 @@ CONFIG_DEFINE_LOCALISED("System", bool, Hints, true);
CONFIG_DEFINE_LOCALISED("System", bool, ControlTutorial, true); CONFIG_DEFINE_LOCALISED("System", bool, ControlTutorial, true);
CONFIG_DEFINE_LOCALISED("System", bool, AchievementNotifications, true); CONFIG_DEFINE_LOCALISED("System", bool, AchievementNotifications, true);
CONFIG_DEFINE_ENUM_LOCALISED("System", ETimeOfDayTransition, TimeOfDayTransition, ETimeOfDayTransition::Xbox); CONFIG_DEFINE_ENUM_LOCALISED("System", ETimeOfDayTransition, TimeOfDayTransition, ETimeOfDayTransition::Xbox);
CONFIG_DEFINE("System", bool, ShowConsole, false);
CONFIG_DEFINE_LOCALISED("Input", bool, InvertCameraX, false); CONFIG_DEFINE_ENUM_LOCALISED("Input", ECameraRotationMode, HorizontalCamera, ECameraRotationMode::Normal);
CONFIG_DEFINE_LOCALISED("Input", bool, InvertCameraY, false); CONFIG_DEFINE_ENUM_LOCALISED("Input", ECameraRotationMode, VerticalCamera, ECameraRotationMode::Normal);
CONFIG_DEFINE_LOCALISED("Input", bool, Vibration, true); CONFIG_DEFINE_LOCALISED("Input", bool, Vibration, true);
CONFIG_DEFINE_LOCALISED("Input", bool, AllowBackgroundInput, false); CONFIG_DEFINE_LOCALISED("Input", bool, AllowBackgroundInput, false);
CONFIG_DEFINE_ENUM_LOCALISED("Input", EControllerIcons, ControllerIcons, EControllerIcons::Auto); CONFIG_DEFINE_ENUM_LOCALISED("Input", EControllerIcons, ControllerIcons, EControllerIcons::Auto);
CONFIG_DEFINE_ENUM("Bindings", SDL_Scancode, Key_A, SDL_SCANCODE_S);
CONFIG_DEFINE_ENUM("Bindings", SDL_Scancode, Key_B, SDL_SCANCODE_D);
CONFIG_DEFINE_ENUM("Bindings", SDL_Scancode, Key_X, SDL_SCANCODE_A);
CONFIG_DEFINE_ENUM("Bindings", SDL_Scancode, Key_Y, SDL_SCANCODE_W);
CONFIG_DEFINE_ENUM("Bindings", SDL_Scancode, Key_DPadUp, SDL_SCANCODE_UNKNOWN);
CONFIG_DEFINE_ENUM("Bindings", SDL_Scancode, Key_DPadDown, SDL_SCANCODE_UNKNOWN);
CONFIG_DEFINE_ENUM("Bindings", SDL_Scancode, Key_DPadLeft, SDL_SCANCODE_UNKNOWN);
CONFIG_DEFINE_ENUM("Bindings", SDL_Scancode, Key_DPadRight, SDL_SCANCODE_UNKNOWN);
CONFIG_DEFINE_ENUM("Bindings", SDL_Scancode, Key_Start, SDL_SCANCODE_RETURN);
CONFIG_DEFINE_ENUM("Bindings", SDL_Scancode, Key_Back, SDL_SCANCODE_BACKSPACE);
CONFIG_DEFINE_ENUM("Bindings", SDL_Scancode, Key_LeftTrigger, SDL_SCANCODE_1);
CONFIG_DEFINE_ENUM("Bindings", SDL_Scancode, Key_RightTrigger, SDL_SCANCODE_3);
CONFIG_DEFINE_ENUM("Bindings", SDL_Scancode, Key_LeftBumper, SDL_SCANCODE_Q);
CONFIG_DEFINE_ENUM("Bindings", SDL_Scancode, Key_RightBumper, SDL_SCANCODE_E);
CONFIG_DEFINE_ENUM("Bindings", SDL_Scancode, Key_LeftStickUp, SDL_SCANCODE_UP);
CONFIG_DEFINE_ENUM("Bindings", SDL_Scancode, Key_LeftStickDown, SDL_SCANCODE_DOWN);
CONFIG_DEFINE_ENUM("Bindings", SDL_Scancode, Key_LeftStickLeft, SDL_SCANCODE_LEFT);
CONFIG_DEFINE_ENUM("Bindings", SDL_Scancode, Key_LeftStickRight, SDL_SCANCODE_RIGHT);
CONFIG_DEFINE_ENUM("Bindings", SDL_Scancode, Key_RightStickUp, SDL_SCANCODE_UNKNOWN);
CONFIG_DEFINE_ENUM("Bindings", SDL_Scancode, Key_RightStickDown, SDL_SCANCODE_UNKNOWN);
CONFIG_DEFINE_ENUM("Bindings", SDL_Scancode, Key_RightStickLeft, SDL_SCANCODE_UNKNOWN);
CONFIG_DEFINE_ENUM("Bindings", SDL_Scancode, Key_RightStickRight, SDL_SCANCODE_UNKNOWN);
CONFIG_DEFINE_LOCALISED("Audio", float, MasterVolume, 1.0f); CONFIG_DEFINE_LOCALISED("Audio", float, MasterVolume, 1.0f);
CONFIG_DEFINE_LOCALISED("Audio", float, MusicVolume, 1.0f); CONFIG_DEFINE_LOCALISED("Audio", float, MusicVolume, 1.0f);
CONFIG_DEFINE_LOCALISED("Audio", float, EffectsVolume, 1.0f); CONFIG_DEFINE_LOCALISED("Audio", float, EffectsVolume, 1.0f);
@@ -77,8 +101,12 @@ CONFIG_DEFINE_ENUM_LOCALISED("Video", EUIScaleMode, UIScaleMode, EUIScaleMode::E
CONFIG_DEFINE_HIDDEN("Exports", bool, AllowCancellingUnleash, false); CONFIG_DEFINE_HIDDEN("Exports", bool, AllowCancellingUnleash, false);
CONFIG_DEFINE_HIDDEN("Exports", bool, DisableAutoSaveWarning, false); CONFIG_DEFINE_HIDDEN("Exports", bool, DisableAutoSaveWarning, false);
CONFIG_DEFINE_HIDDEN("Exports", bool, DisableDLCIcon, 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, FixUnleashOutOfControlDrain, false);
CONFIG_DEFINE_HIDDEN("Exports", bool, HomingAttackOnBoost, true); 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, SaveScoreAtCheckpoints, false);
CONFIG_DEFINE_HIDDEN("Exports", bool, SkipIntroLogos, false); CONFIG_DEFINE_HIDDEN("Exports", bool, SkipIntroLogos, false);
CONFIG_DEFINE_HIDDEN("Exports", bool, UseOfficialTitleOnTitleBar, false); CONFIG_DEFINE_HIDDEN("Exports", bool, UseOfficialTitleOnTitleBar, false);
CONFIG_DEFINE("Update", time_t, LastChecked, 0);
+52
View File
@@ -0,0 +1,52 @@
#include "paths.h"
#include <os/process.h>
std::filesystem::path g_executableRoot = os::process::GetExecutablePath().remove_filename();
std::filesystem::path g_userPath = BuildUserPath();
bool CheckPortable()
{
return std::filesystem::exists(g_executableRoot / "portable.txt");
}
std::filesystem::path BuildUserPath()
{
if (CheckPortable())
return g_executableRoot;
std::filesystem::path userPath;
#if defined(_WIN32)
PWSTR knownPath = NULL;
if (SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, NULL, &knownPath) == S_OK)
userPath = std::filesystem::path{ knownPath } / USER_DIRECTORY;
CoTaskMemFree(knownPath);
#elif defined(__linux__)
const char* homeDir = getenv("HOME");
if (homeDir == nullptr)
{
homeDir = getpwuid(getuid())->pw_dir;
}
if (homeDir != nullptr)
{
// Prefer to store in the .config directory if it exists. Use the home directory otherwise.
std::filesystem::path homePath = homeDir;
std::filesystem::path configPath = homePath / ".config";
if (std::filesystem::exists(configPath))
userPath = configPath / USER_DIRECTORY;
else
userPath = homePath / ("." USER_DIRECTORY);
}
#else
static_assert(false, "GetUserPath() not implemented for this platform.");
#endif
return userPath;
}
const std::filesystem::path& GetUserPath()
{
return g_userPath;
}
+3 -36
View File
@@ -13,42 +13,9 @@ inline std::filesystem::path GetGamePath()
return GAME_INSTALL_DIRECTORY; return GAME_INSTALL_DIRECTORY;
} }
inline std::filesystem::path GetUserPath() bool CheckPortable();
{ std::filesystem::path BuildUserPath();
if (std::filesystem::exists(GAME_INSTALL_DIRECTORY "/portable.txt")) const std::filesystem::path& GetUserPath();
return GAME_INSTALL_DIRECTORY;
std::filesystem::path userPath;
#if defined(_WIN32)
PWSTR knownPath = NULL;
if (SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, NULL, &knownPath) == S_OK)
userPath = std::filesystem::path{ knownPath } / USER_DIRECTORY;
CoTaskMemFree(knownPath);
#elif defined(__linux__)
const char *homeDir = getenv("HOME");
if (homeDir == nullptr)
{
homeDir = getpwuid(getuid())->pw_dir;
}
if (homeDir != nullptr)
{
// Prefer to store in the .config directory if it exists. Use the home directory otherwise.
std::filesystem::path homePath = homeDir;
std::filesystem::path configPath = homePath / ".config";
if (std::filesystem::exists(configPath))
userPath = configPath / USER_DIRECTORY;
else
userPath = homePath / ("." USER_DIRECTORY);
}
#else
static_assert(false, "GetUserPath() not implemented for this platform.");
#endif
return userPath;
}
inline std::filesystem::path GetSavePath(bool checkForMods) inline std::filesystem::path GetSavePath(bool checkForMods)
{ {
+8
View File
@@ -0,0 +1,8 @@
#include "registry.h"
#include <os/process.h>
#include <os/registry.h>
void Registry::Save()
{
os::registry::WriteValue(STR(ExecutableFilePath), os::process::GetExecutablePath());
}
+7
View File
@@ -0,0 +1,7 @@
#pragma once
class Registry
{
public:
static void Save();
};
+114 -61
View File
@@ -31,6 +31,113 @@ function(CheckOutputFile OUTPUT_FILE TEMPLATE_FILE)
endif() endif()
endfunction() endfunction()
# VERSION_TXT : the input text file containing the milestone, major, minor and revision variables.
function(ParseVersionInfo VERSION_TXT)
if (NOT VERSION_TXT)
message(FATAL_ERROR "VERSION_TXT not specified.")
endif()
if (NOT EXISTS "${VERSION_TXT}")
message(FATAL_ERROR "No such file: ${VERSION_TXT}")
endif()
file(READ "${VERSION_TXT}" FILE_CONTENT)
string(REGEX REPLACE "\r?\n" ";" FILE_LINES "${FILE_CONTENT}")
foreach(LINE ${FILE_LINES})
if (LINE STREQUAL "")
continue()
endif()
# Find key/value pair match.
string(REGEX MATCH "([A-Za-z_]+)=\\\"?([^\"]+)\\\"?" MATCH "${LINE}")
if (MATCH)
# Extract key/value pairs.
string(REGEX REPLACE "([A-Za-z_]+)=.*" "\\1" KEY "${MATCH}")
string(REGEX REPLACE "[A-Za-z_]+=\\\"?([^\"]+)\\\"?" "\\1" VALUE "${MATCH}")
# Set environment variable.
set("${KEY}" "${VALUE}" CACHE INTERNAL "${KEY}")
endif()
endforeach()
endfunction()
# VERSION_TXT : the input text file containing the milestone, major, minor and revision variables.
# OUTPUT_CSV : the output string will be formatted as #,#,# without any alphabetic characters (optional).
# BUILD_TYPE : the current build configuration (e.g. "Release", "RelWithDebInfo", "Debug") (optional).
# SHOW_GIT_INFO : the Git commit hash and branch name should be appended to the version string (optional).
# SHOW_BUILD_TYPE : the build type should be appended to the version string (optional).
# OUTPUT_VAR : the output variable to store the version string in.
function(CreateVersionString)
cmake_parse_arguments(ARGS "" "VERSION_TXT;OUTPUT_CSV;BUILD_TYPE;SHOW_GIT_INFO;SHOW_BUILD_TYPE;OUTPUT_VAR" "" ${ARGN})
if (NOT ARGS_VERSION_TXT)
message(FATAL_ERROR "VERSION_TXT not specified.")
endif()
if (NOT ARGS_OUTPUT_VAR)
message(FATAL_ERROR "OUTPUT_VAR not specified.")
endif()
ParseVersionInfo("${ARGS_VERSION_TXT}")
if (ARGS_OUTPUT_CSV)
set(VERSION_STRING "${VERSION_MAJOR},${VERSION_MINOR},${VERSION_REVISION}")
else()
set(VERSION_STRING "v${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REVISION}")
if (VERSION_MILESTONE)
string(PREPEND VERSION_STRING "${VERSION_MILESTONE} ")
endif()
if (ARGS_SHOW_GIT_INFO)
find_package(Git REQUIRED)
# Get Git branch name.
execute_process(
COMMAND ${GIT_EXECUTABLE} rev-parse --abbrev-ref HEAD
OUTPUT_VARIABLE BRANCH_NAME
OUTPUT_STRIP_TRAILING_WHITESPACE
)
set(BRANCH_NAME ${BRANCH_NAME} CACHE INTERNAL "BRANCH_NAME")
# Get Git commit hash.
execute_process(
COMMAND ${GIT_EXECUTABLE} rev-parse HEAD
OUTPUT_VARIABLE COMMIT_HASH
OUTPUT_STRIP_TRAILING_WHITESPACE
)
set(COMMIT_HASH ${COMMIT_HASH} CACHE INTERNAL "COMMIT_HASH")
# Get short Git commit hash.
execute_process(
COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD
OUTPUT_VARIABLE COMMIT_HASH_SHORT
OUTPUT_STRIP_TRAILING_WHITESPACE
)
set(COMMIT_HASH_SHORT ${COMMIT_HASH_SHORT} CACHE INTERNAL "COMMIT_HASH_SHORT")
# Append commit hash and branch.
if (COMMIT_HASH_SHORT AND BRANCH_NAME)
string(APPEND VERSION_STRING ".${COMMIT_HASH_SHORT}-${BRANCH_NAME}")
endif()
endif()
# Append build configuration.
if (ARGS_BUILD_TYPE AND ARGS_SHOW_BUILD_TYPE)
string(APPEND VERSION_STRING " (${ARGS_BUILD_TYPE})")
endif()
endif()
if (ARGS_OUTPUT_VAR)
set(${ARGS_OUTPUT_VAR} ${VERSION_STRING} PARENT_SCOPE)
endif()
endfunction()
# OUTPUT_DIR : the output directory of the resulting *.h/*.cpp files. # OUTPUT_DIR : the output directory of the resulting *.h/*.cpp files.
# VERSION_TXT : the input text file containing the milestone, major, minor and revision variables. # VERSION_TXT : the input text file containing the milestone, major, minor and revision variables.
# H_TEMPLATE : the input template for the header. # H_TEMPLATE : the input template for the header.
@@ -59,71 +166,17 @@ function(GenerateVersionSources)
message(FATAL_ERROR "CXX_TEMPLATE not specified.") message(FATAL_ERROR "CXX_TEMPLATE not specified.")
endif() endif()
# Required for *.cpp template.
set(BUILD_TYPE ${ARGS_BUILD_TYPE}) set(BUILD_TYPE ${ARGS_BUILD_TYPE})
if (ARGS_SHOW_GIT_INFO) CreateVersionString(
find_package(Git REQUIRED) VERSION_TXT ${ARGS_VERSION_TXT}
BUILD_TYPE ${ARGS_BUILD_TYPE}
# Get Git branch name. SHOW_GIT_INFO ${ARGS_SHOW_GIT_INFO}
execute_process( SHOW_BUILD_TYPE ${ARGS_SHOW_BUILD_TYPE}
COMMAND ${GIT_EXECUTABLE} rev-parse --abbrev-ref HEAD OUTPUT_VAR VERSION_STRING
OUTPUT_VARIABLE BRANCH_NAME
OUTPUT_STRIP_TRAILING_WHITESPACE
) )
# Get Git commit hash.
execute_process(
COMMAND ${GIT_EXECUTABLE} rev-parse HEAD
OUTPUT_VARIABLE COMMIT_HASH
OUTPUT_STRIP_TRAILING_WHITESPACE
)
# Get short Git commit hash.
execute_process(
COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD
OUTPUT_VARIABLE COMMIT_HASH_SHORT
OUTPUT_STRIP_TRAILING_WHITESPACE
)
endif()
file(READ "${ARGS_VERSION_TXT}" FILE_CONTENT)
string(REGEX REPLACE "\r?\n" ";" FILE_LINES "${FILE_CONTENT}")
foreach(LINE ${FILE_LINES})
if (LINE STREQUAL "")
continue()
endif()
# Find key/value pair match.
string(REGEX MATCH "([A-Za-z_]+)=\\\"?([^\"]+)\\\"?" MATCH "${LINE}")
if (MATCH)
# Extract key/value pairs.
string(REGEX REPLACE "([A-Za-z_]+)=.*" "\\1" KEY "${MATCH}")
string(REGEX REPLACE "[A-Za-z_]+=\\\"?([^\"]+)\\\"?" "\\1" VALUE "${MATCH}")
# Set environment variable.
set("${KEY}" "${VALUE}")
endif()
endforeach()
set(VERSION_STRING "v${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REVISION}")
# Prepend version milestone.
if (VERSION_MILESTONE)
string(PREPEND VERSION_STRING "${VERSION_MILESTONE} ")
endif()
# Append commit hash and branch.
if (COMMIT_HASH_SHORT)
string(APPEND VERSION_STRING ".${COMMIT_HASH_SHORT}-${BRANCH_NAME}")
endif()
# Append build configuration.
if (ARGS_BUILD_TYPE AND ARGS_SHOW_BUILD_TYPE)
string(APPEND VERSION_STRING " (${ARGS_BUILD_TYPE})")
endif()
message("-- Build: ${VERSION_STRING}") message("-- Build: ${VERSION_STRING}")
get_filename_component(H_TEMPLATE_NAME_WE "${ARGS_H_TEMPLATE}" NAME_WE) get_filename_component(H_TEMPLATE_NAME_WE "${ARGS_H_TEMPLATE}" NAME_WE)
+145 -11
View File
@@ -580,9 +580,63 @@ registers = ["r3", "f0"]
# SWA::Player::CEvilPostureInputStandard # SWA::Player::CEvilPostureInputStandard
[[midasm_hook]] [[midasm_hook]]
name = "PostureDPadSupportYMidAsmHook"
address = 0x823CDA88
registers = ["r3", "f12"]
# SWA::Player::CEvilSonicContext::CStateColumn
[[midasm_hook]]
name = "PostureDPadSupportXMidAsmHook" name = "PostureDPadSupportXMidAsmHook"
address = 0x823CDA74 address = 0x823E057C
registers = ["r3", "f0"] registers = ["r11", "f0"]
# SWA::Player::CEvilSonicContext::CStateColumn
[[midasm_hook]]
name = "PostureDPadSupportXMidAsmHook"
address = 0x823E0634
registers = ["r11", "f0"]
# SWA::Player::CEvilSonicContext::CStateColumn
[[midasm_hook]]
name = "PostureDPadSupportXMidAsmHook"
address = 0x823E065C
registers = ["r11", "f0"]
# SWA::Player::CEvilSonicContext::CStateColumn
[[midasm_hook]]
name = "PostureDPadSupportXMidAsmHook"
address = 0x823E088C
registers = ["r11", "f0"]
# SWA::Player::CEvilSonicContext::CStateColumn
[[midasm_hook]]
name = "PostureDPadSupportYMidAsmHook"
address = 0x823DFB10
registers = ["r23", "f0"]
# SWA::Player::CEvilSonicContext::CStateColumn
[[midasm_hook]]
name = "PostureDPadSupportYMidAsmHook"
address = 0x823E0AB4
registers = ["r11", "f0"]
# SWA::Player::CEvilSonicContext::CStateColumn
[[midasm_hook]]
name = "PostureDPadSupportYMidAsmHook"
address = 0x823E0BB8
registers = ["r11", "f13"]
# SWA::Player::CEvilSonicContext::CStateColumn
[[midasm_hook]]
name = "PostureDPadSupportYMidAsmHook"
address = 0x823E0CBC
registers = ["r29", "f0"]
# SWA::Player::CEvilSonicContext::CStateColumn
[[midasm_hook]]
name = "PostureDPadSupportYMidAsmHook"
address = 0x823E01EC
registers = ["r23", "f0"]
# SWA::CObjBobsleigh::CStateMode3D # SWA::CObjBobsleigh::CStateMode3D
[[midasm_hook]] [[midasm_hook]]
@@ -647,51 +701,53 @@ after_instruction = true
# SWA::CWorldMapCamera - disable rotation deadzone for touch # SWA::CWorldMapCamera - disable rotation deadzone for touch
[[midasm_hook]] [[midasm_hook]]
name = "WorldMapTouchSupportMidAsmHook" name = "WorldMapDeadzoneMidAsmHook"
address = 0x824862EC address = 0x824862EC
registers = ["r30"]
jump_address_on_true = 0x824862F0 jump_address_on_true = 0x824862F0
# SWA::CWorldMapCamera - disable flag magnetism for touch # SWA::CWorldMapCamera - disable flag magnetism for touch
[[midasm_hook]] [[midasm_hook]]
name = "WorldMapTouchMagnetismSupportMidAsmHook" name = "WorldMapMagnetismMidAsmHook"
address = 0x824866D4 address = 0x824866D4
registers = ["f0"] registers = ["f0"]
jump_address_on_true = 0x82486838 jump_address_on_true = 0x824866D8
jump_address_on_false = 0x82486838
# SWA::CWorldMapCamera - touch and D-Pad support for camera adjustment threshold on the X axis # SWA::CWorldMapCamera - touch and D-Pad support for camera adjustment threshold on the X axis
[[midasm_hook]] [[midasm_hook]]
name = "TouchAndDPadSupportWorldMapXMidAsmHook" name = "WorldMapHidSupportXMidAsmHook"
address = 0x824862D8 address = 0x824862D8
registers = ["r30", "f12"] registers = ["r30", "f12"]
# SWA::CWorldMapCamera - touch and D-Pad support for adjusing camera yaw # SWA::CWorldMapCamera - touch and D-Pad support for adjusing camera yaw
[[midasm_hook]] [[midasm_hook]]
name = "TouchAndDPadSupportWorldMapXMidAsmHook" name = "WorldMapHidSupportXMidAsmHook"
address = 0x82486318 address = 0x82486318
registers = ["r30", "f12"] registers = ["r30", "f12"]
# SWA::CWorldMapCamera - touch and D-Pad support for camera adjustment threshold on the Y axis # SWA::CWorldMapCamera - touch and D-Pad support for camera adjustment threshold on the Y axis
[[midasm_hook]] [[midasm_hook]]
name = "TouchAndDPadSupportWorldMapYMidAsmHook" name = "WorldMapHidSupportYMidAsmHook"
address = 0x824862CC address = 0x824862CC
registers = ["r30", "f0"] registers = ["r30", "f0"]
# SWA::CWorldMapCamera - touch and D-Pad support for adjusing camera pitch # SWA::CWorldMapCamera - touch and D-Pad support for adjusing camera pitch
[[midasm_hook]] [[midasm_hook]]
name = "TouchAndDPadSupportWorldMapYMidAsmHook" name = "WorldMapHidSupportYMidAsmHook"
address = 0x824862F4 address = 0x824862F4
registers = ["r30", "f0"] registers = ["r30", "f0"]
# SWA::CWorldMapCamera - touch and D-Pad support for flag magnetism on the X axis # SWA::CWorldMapCamera - touch and D-Pad support for flag magnetism on the X axis
[[midasm_hook]] [[midasm_hook]]
name = "TouchAndDPadSupportWorldMapXMidAsmHook" name = "WorldMapHidSupportXMidAsmHook"
address = 0x8248665C address = 0x8248665C
registers = ["r27", "f29"] registers = ["r27", "f29"]
after_instruction = true after_instruction = true
# SWA::CWorldMapCamera - touch and D-Pad support for flag magnetism on the Y axis # SWA::CWorldMapCamera - touch and D-Pad support for flag magnetism on the Y axis
[[midasm_hook]] [[midasm_hook]]
name = "TouchAndDPadSupportWorldMapYMidAsmHook" name = "WorldMapHidSupportYMidAsmHook"
address = 0x82486658 address = 0x82486658
registers = ["r27", "f28"] registers = ["r27", "f28"]
after_instruction = true after_instruction = true
@@ -743,11 +799,24 @@ name = "WorldMapInfoMidAsmHook"
address = 0x8257AF34 address = 0x8257AF34
registers = ["r4"] registers = ["r4"]
# Loading
[[midasm_hook]] [[midasm_hook]]
name = "AddPrimitive2DMidAsmHook" name = "AddPrimitive2DMidAsmHook"
address = 0x824DB3E4 address = 0x824DB3E4
registers = ["r3"] registers = ["r3"]
# NPC
[[midasm_hook]]
name = "AddPrimitive2DMidAsmHook"
address = 0x82B34914
registers = ["r3"]
# Title
[[midasm_hook]]
name = "AddPrimitive2DMidAsmHook"
address = 0x82581C2C
registers = ["r3"]
[[midasm_hook]] [[midasm_hook]]
name = "ObjGetItemFieldOfViewMidAsmHook" name = "ObjGetItemFieldOfViewMidAsmHook"
address = 0x82692934 address = 0x82692934
@@ -802,3 +871,68 @@ registers = ["r3"]
name = "ObjBigBarrelSetPositionMidAsmHook" name = "ObjBigBarrelSetPositionMidAsmHook"
address = 0x8271B5C8 address = 0x8271B5C8
registers = ["r3", "r4"] registers = ["r3", "r4"]
[[midasm_hook]]
name = "LoadingRenderMidAsmHook"
address = 0x824DB734
jump_address_on_true = 0x824DB738
[[midasm_hook]]
name = "FxFadePreRenderQuadMidAsmHook"
address = 0x82BA7D3C
registers = ["r31"]
[[midasm_hook]]
name = "FxFadePostRenderQuadMidAsmHook"
address = 0x82BA7D40
[[midasm_hook]]
name = "YggdrasillRenderQuadMidAsmHook"
address = 0x82E9FBA0
registers = ["r3", "r6"]
# CSB loader
[[midasm_hook]]
name = "MakeCueSheetDataMidAsmHook"
address = 0x82E5EA10
registers = ["r31"]
jump_address_on_true = 0x82E5EA40
# Name assignment
[[midasm_hook]]
name = "MakeCueSheetDataMidAsmHook"
address = 0x82E5EA40
registers = ["r31"]
jump_address_on_true = 0x82E5EA4C
[[midasm_hook]]
name = "WaitVsyncMidAsmHook"
address = 0x82AE2770
jump_address = 0x82AE2774
[[midasm_hook]]
name = "ApplicationFrameLimiterMidAsmHook"
address = 0x822C1064
jump_address = 0x822C111C
[[midasm_hook]]
name = "PressStartSaveLoadThreadMidAsmHook"
address = 0x822C4358
[[midasm_hook]]
name = "FxShadowMapInitMidAsmHook"
address = 0x82BAD8F4
registers = ["r11"]
[[midasm_hook]]
name = "FxShadowMapNoTerrainMidAsmHook"
address = 0x82BAD9EC
registers = ["r4", "r30"]
after_instruction = true
[[midasm_hook]]
name = "FxShadowMapMidAsmHook"
address = 0x82BADADC
registers = ["r4", "r5", "r6", "r30"]
jump_address_on_true = 0x82BAD9F0
jump_address_on_false = 0x82BADAFC
Vendored Submodule
+1
Submodule thirdparty/json added at 606b6347ed
+2 -1
View File
@@ -10,6 +10,7 @@
"platform": "windows" "platform": "windows"
}, },
"directx-dxc", "directx-dxc",
"freetype" "freetype",
"curl"
] ]
} }