mirror of
https://github.com/hedge-dev/UnleashedRecomp
synced 2026-06-08 12:28:15 -04:00
Compare commits
112 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d696524dbf | |||
| e3ac47a797 | |||
| 72640c8749 | |||
| 1687c65be9 | |||
| 6dac017d00 | |||
| 2150faed2e | |||
| 2f1b98c570 | |||
| 70ebdaa685 | |||
| e1edd5f35d | |||
| 88df0e08b7 | |||
| 511c670def | |||
| 16c35b45c2 | |||
| b291bdba91 | |||
| 727be2b47c | |||
| 62ad3a1a13 | |||
| 6c8dbdb6bc | |||
| 71bb081645 | |||
| 871515b3be | |||
| 266d436c28 | |||
| e7cc5a858e | |||
| 9549ba54aa | |||
| 47b1f20679 | |||
| ef51f04d4f | |||
| b68dbec612 | |||
| 96108e1759 | |||
| d3589979e4 | |||
| 2db4a9c78c | |||
| 2d56566924 | |||
| 553e011dad | |||
| 900ba7c916 | |||
| 66648d550a | |||
| c40ffbc70d | |||
| ee97736d58 | |||
| bbb3ebc25d | |||
| 52558a674e | |||
| 11d0fd2f9c | |||
| aaad10d797 | |||
| 342d696f99 | |||
| 0426b79094 | |||
| 3497d9b34b | |||
| 9d98d507b0 | |||
| cd38776576 | |||
| 54d5588d79 | |||
| ddd8ce77db | |||
| 8cba851212 | |||
| 87d2ab5af1 | |||
| f2e01bbe79 | |||
| ed0aeb3b7d | |||
| 21c1d36836 | |||
| 70f042d11f | |||
| fefb08cc4b | |||
| 9fea5f9e4c | |||
| c78c2010a3 | |||
| e89ecf8d68 | |||
| 3171dc8266 | |||
| 1ef5b86036 | |||
| e761c9f4d6 | |||
| 4816d4aa9d | |||
| 091b4ef089 | |||
| 692542fb85 | |||
| b26baea13c | |||
| a19e434e9b | |||
| 92e3cbea45 | |||
| 93f120ae83 | |||
| aa6118b448 | |||
| 863e1602ff | |||
| d5d2e83a10 | |||
| cb57e337b8 | |||
| 4fd08efa6d | |||
| f6e43761b7 | |||
| 293f873229 | |||
| 4691fe2e5a | |||
| 9289ee5e02 | |||
| 50529a95fc | |||
| 87d9e0dac7 | |||
| 996f23cc54 | |||
| 8b345d2cbd | |||
| 7b9b4245de | |||
| ff29b3e786 | |||
| 37d7e49d93 | |||
| 64a927615d | |||
| 9521cb3ee9 | |||
| 948e2f271f | |||
| f7ce4f179a | |||
| fc7918772b | |||
| a9677084ea | |||
| a5db997e5d | |||
| 4d41d8eaae | |||
| 7c989af42c | |||
| dee2a36d0f | |||
| d2d8c541e1 | |||
| 81d4390076 | |||
| 57e888658a | |||
| 0d4b66fe98 | |||
| 76ec5f032d | |||
| a9573584cd | |||
| 8e58cbcbf2 | |||
| c1988e9323 | |||
| 5321ee0fba | |||
| 1ee312442c | |||
| 5441075c5e | |||
| 470d8f797b | |||
| d041e2ba30 | |||
| e88ed2502c | |||
| e13b0ea009 | |||
| 5d437f8de6 | |||
| c6a25f21c2 | |||
| d443693d78 | |||
| dfa2a31286 | |||
| fb5d0cd94e | |||
| cf5c3423d1 | |||
| f9440e2076 |
@@ -48,7 +48,7 @@ jobs:
|
||||
run: |
|
||||
echo "commit_message=$(git log -1 --pretty=%s)" >> $GITHUB_ENV
|
||||
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
|
||||
|
||||
# Uploads the built flatpak bundle to GitHub
|
||||
|
||||
@@ -61,3 +61,6 @@
|
||||
[submodule "thirdparty/implot"]
|
||||
path = thirdparty/implot
|
||||
url = https://github.com/epezent/implot.git
|
||||
[submodule "thirdparty/json"]
|
||||
path = thirdparty/json
|
||||
url = https://github.com/nlohmann/json
|
||||
|
||||
@@ -129,15 +129,15 @@ set(UNLEASHED_RECOMP_HID_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/audio_patches.cpp"
|
||||
"patches/camera_patches.cpp"
|
||||
"patches/CGameModeStageTitle_patches.cpp"
|
||||
"patches/CHudPause_patches.cpp"
|
||||
"patches/CTitleStateIntro_patches.cpp"
|
||||
"patches/CTitleStateMenu_patches.cpp"
|
||||
"patches/fps_patches.cpp"
|
||||
"patches/frontend_listener.cpp"
|
||||
"patches/input_patches.cpp"
|
||||
"patches/inspire_patches.cpp"
|
||||
"patches/misc_patches.cpp"
|
||||
@@ -149,21 +149,23 @@ set(UNLEASHED_RECOMP_PATCHES_CXX_SOURCES
|
||||
|
||||
set(UNLEASHED_RECOMP_UI_CXX_SOURCES
|
||||
"ui/achievement_menu.cpp"
|
||||
"ui/achievement_overlay.cpp"
|
||||
"ui/installer_wizard.cpp"
|
||||
"ui/achievement_overlay.cpp"
|
||||
"ui/black_bar.cpp"
|
||||
"ui/button_guide.cpp"
|
||||
"ui/fader.cpp"
|
||||
"ui/game_window.cpp"
|
||||
"ui/imgui_utils.cpp"
|
||||
"ui/installer_wizard.cpp"
|
||||
"ui/message_window.cpp"
|
||||
"ui/options_menu_thumbnails.cpp"
|
||||
"ui/options_menu.cpp"
|
||||
"ui/sdl_listener.cpp"
|
||||
"ui/game_window.cpp"
|
||||
"ui/options_menu_thumbnails.cpp"
|
||||
)
|
||||
|
||||
set(UNLEASHED_RECOMP_INSTALL_CXX_SOURCES
|
||||
"install/installer.cpp"
|
||||
"install/iso_file_system.cpp"
|
||||
"install/memory_mapped_file.cpp"
|
||||
"install/update_checker.cpp"
|
||||
"install/xcontent_file_system.cpp"
|
||||
"install/xex_patcher.cpp"
|
||||
"install/hashes/apotos_shamar.cpp"
|
||||
@@ -178,7 +180,10 @@ set(UNLEASHED_RECOMP_INSTALL_CXX_SOURCES
|
||||
|
||||
set(UNLEASHED_RECOMP_USER_CXX_SOURCES
|
||||
"user/achievement_data.cpp"
|
||||
"user/config.cpp"
|
||||
"user/achievement_manager.cpp"
|
||||
"user/config.cpp"
|
||||
"user/registry.cpp"
|
||||
"user/paths.cpp"
|
||||
)
|
||||
|
||||
set(UNLEASHED_RECOMP_MOD_CXX_SOURCES
|
||||
@@ -205,6 +210,7 @@ set(UNLEASHED_RECOMP_THIRDPARTY_INCLUDES
|
||||
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/ddspp"
|
||||
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/imgui"
|
||||
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/implot"
|
||||
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/json/include"
|
||||
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/libmspack/libmspack/mspack"
|
||||
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/magic_enum/include"
|
||||
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/stb"
|
||||
@@ -230,6 +236,7 @@ set(UNLEASHED_RECOMP_CXX_SOURCES
|
||||
"exports.cpp"
|
||||
"main.cpp"
|
||||
"misc_impl.cpp"
|
||||
"sdl_listener.cpp"
|
||||
"stdafx.cpp"
|
||||
"version.cpp"
|
||||
|
||||
@@ -246,19 +253,64 @@ set(UNLEASHED_RECOMP_CXX_SOURCES
|
||||
${UNLEASHED_RECOMP_USER_CXX_SOURCES}
|
||||
${UNLEASHED_RECOMP_MOD_CXX_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)
|
||||
# Set up Win32 resources for application icon.
|
||||
set(ICON_PATH "${PROJECT_SOURCE_DIR}/../UnleashedRecompResources/images/game_icon.ico")
|
||||
# Create binary version number for Win32 integer attributes.
|
||||
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)
|
||||
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()
|
||||
add_executable(UnleashedRecomp ${UNLEASHED_RECOMP_CXX_SOURCES})
|
||||
endif()
|
||||
|
||||
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()
|
||||
|
||||
if (UNLEASHED_RECOMP_D3D12)
|
||||
@@ -272,6 +324,7 @@ if (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
endif()
|
||||
|
||||
find_package(directx-dxc REQUIRED)
|
||||
find_package(CURL REQUIRED)
|
||||
|
||||
if (UNLEASHED_RECOMP_D3D12)
|
||||
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/D3D12)
|
||||
@@ -298,10 +351,12 @@ file(CHMOD ${DIRECTX_DXC_TOOL} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE)
|
||||
|
||||
if (WIN32)
|
||||
target_link_libraries(UnleashedRecomp PRIVATE
|
||||
comctl32
|
||||
ntdll
|
||||
winmm
|
||||
comctl32
|
||||
dwmapi
|
||||
ntdll
|
||||
Shcore
|
||||
Synchronization
|
||||
winmm
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -317,6 +372,7 @@ target_link_libraries(UnleashedRecomp PRIVATE
|
||||
tomlplusplus::tomlplusplus
|
||||
UnleashedRecompLib
|
||||
xxHash::xxhash
|
||||
CURL::libcurl
|
||||
)
|
||||
|
||||
target_include_directories(UnleashedRecomp PRIVATE
|
||||
@@ -360,7 +416,10 @@ function(compile_pixel_shader FILE_PATH)
|
||||
compile_shader(${FILE_PATH} ps_6_0)
|
||||
endfunction()
|
||||
|
||||
compile_vertex_shader(copy_vs)
|
||||
compile_pixel_shader(blend_color_alpha_ps)
|
||||
compile_vertex_shader(copy_vs)
|
||||
compile_pixel_shader(copy_color_ps)
|
||||
compile_pixel_shader(copy_depth_ps)
|
||||
compile_pixel_shader(csd_filter_ps)
|
||||
compile_vertex_shader(csd_no_tex_vs)
|
||||
compile_vertex_shader(csd_vs)
|
||||
@@ -373,7 +432,10 @@ compile_pixel_shader(gamma_correction_ps)
|
||||
compile_pixel_shader(imgui_ps)
|
||||
compile_vertex_shader(imgui_vs)
|
||||
compile_pixel_shader(movie_ps)
|
||||
compile_vertex_shader(movie_vs)
|
||||
compile_vertex_shader(movie_vs)
|
||||
compile_pixel_shader(resolve_msaa_color_2x)
|
||||
compile_pixel_shader(resolve_msaa_color_4x)
|
||||
compile_pixel_shader(resolve_msaa_color_8x)
|
||||
compile_pixel_shader(resolve_msaa_depth_2x)
|
||||
compile_pixel_shader(resolve_msaa_depth_4x)
|
||||
compile_pixel_shader(resolve_msaa_depth_8x)
|
||||
@@ -407,23 +469,6 @@ generate_aggregate_header(
|
||||
"${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_OUTPUT_PATH "${PROJECT_SOURCE_DIR}/res")
|
||||
|
||||
@@ -435,8 +480,8 @@ BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/co
|
||||
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/general_window.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/general_window.dds" ARRAY_NAME "g_general_window" COMPRESSION_TYPE "zstd")
|
||||
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/hedge-dev.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/hedge-dev.dds" ARRAY_NAME "g_hedgedev" COMPRESSION_TYPE "zstd")
|
||||
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/kbm.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/kbm.dds" ARRAY_NAME "g_kbm" COMPRESSION_TYPE "zstd")
|
||||
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/select_fade.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/select_fade.dds" ARRAY_NAME "g_select_fade" COMPRESSION_TYPE "zstd")
|
||||
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/select_fill.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/select_fill.dds" ARRAY_NAME "g_select_fill" COMPRESSION_TYPE "zstd")
|
||||
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/select.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/select.dds" ARRAY_NAME "g_select" COMPRESSION_TYPE "zstd")
|
||||
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/light.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/light.dds" ARRAY_NAME "g_light" COMPRESSION_TYPE "zstd")
|
||||
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/arrow_circle.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/arrow_circle.dds" ARRAY_NAME "g_arrow_circle" COMPRESSION_TYPE "zstd")
|
||||
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/install_001.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/install_001.dds" ARRAY_NAME "g_install_001" COMPRESSION_TYPE "zstd")
|
||||
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/install_002.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/install_002.dds" ARRAY_NAME "g_install_002" COMPRESSION_TYPE "zstd")
|
||||
@@ -463,8 +508,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_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/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/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/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/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/monitor.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/monitor.dds" ARRAY_NAME "g_monitor" COMPRESSION_TYPE "zstd")
|
||||
@@ -486,7 +530,8 @@ BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/op
|
||||
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/time_of_day_transition_playstation.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/time_of_day_transition_playstation.dds" ARRAY_NAME "g_time_of_day_transition_playstation" COMPRESSION_TYPE "zstd")
|
||||
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/transparency_antialiasing_false.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/transparency_antialiasing_false.dds" ARRAY_NAME "g_transparency_antialiasing_false" COMPRESSION_TYPE "zstd")
|
||||
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/transparency_antialiasing_true.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/transparency_antialiasing_true.dds" ARRAY_NAME "g_transparency_antialiasing_true" COMPRESSION_TYPE "zstd")
|
||||
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/ui_scale_mode.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/ui_scale_mode.dds" ARRAY_NAME "g_ui_scale_mode" COMPRESSION_TYPE "zstd")
|
||||
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/ui_alignment_mode.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/ui_alignment_mode.dds" ARRAY_NAME "g_ui_alignment_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/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")
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
#include "Hedgehog/Universe/Engine/hhUpdateInfo.h"
|
||||
#include "Hedgehog/Universe/Engine/hhUpdateUnit.h"
|
||||
#include "Hedgehog/Universe/Thread/hhParallelJob.h"
|
||||
#include "SWA/Achievement/AchievementID.h"
|
||||
#include "SWA/Achievement/AchievementManager.h"
|
||||
#include "SWA/Achievement/AchievementTest.h"
|
||||
#include "SWA/CSD/CsdDatabaseWrapper.h"
|
||||
@@ -61,6 +62,11 @@
|
||||
#include "SWA/Camera/Camera.h"
|
||||
#include "SWA/Camera/CameraController.h"
|
||||
#include "SWA/CharacterUtility/CharacterProxy.h"
|
||||
#include "SWA/ExtraStage/Tails/Enemy/Boss/ExStageBoss.h"
|
||||
#include "SWA/ExtraStage/Tails/Enemy/Boss/State/StateBase.h"
|
||||
#include "SWA/ExtraStage/Tails/Enemy/Boss/State/StateBattle.h"
|
||||
#include "SWA/ExtraStage/Tails/Player/ExPlayerTails.h"
|
||||
#include "SWA/Globals.h"
|
||||
#include "SWA/HUD/GeneralWindow/GeneralWindow.h"
|
||||
#include "SWA/HUD/Loading/Loading.h"
|
||||
#include "SWA/HUD/Pause/HudPause.h"
|
||||
@@ -78,6 +84,7 @@
|
||||
#include "SWA/Menu/MenuWindowBase.h"
|
||||
#include "SWA/Movie/MovieDisplayer.h"
|
||||
#include "SWA/Movie/MovieManager.h"
|
||||
#include "SWA/Object/Common/DashPanel/ObjDashPanel.h"
|
||||
#include "SWA/Player/Character/EvilSonic/EvilSonic.h"
|
||||
#include "SWA/Player/Character/EvilSonic/EvilSonicContext.h"
|
||||
#include "SWA/Player/Character/EvilSonic/Hud/EvilHudGuide.h"
|
||||
@@ -100,6 +107,7 @@
|
||||
#include "SWA/System/GameMode/Title/TitleMenu.h"
|
||||
#include "SWA/System/GameMode/Title/TitleStateBase.h"
|
||||
#include "SWA/System/GameMode/Title/TitleStateIntro.h"
|
||||
#include "SWA/System/GameMode/Title/TitleStateMenu.h"
|
||||
#include "SWA/System/GameMode/Title/TitleStateWorldMap.h"
|
||||
#include "SWA/System/GameMode/WorldMap/WorldMapCamera.h"
|
||||
#include "SWA/System/GameMode/WorldMap/WorldMapCursor.h"
|
||||
|
||||
@@ -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
|
||||
|
||||
#include <SWA.inl>
|
||||
#include <SWA/Achievement/AchievementID.h>
|
||||
|
||||
namespace SWA::Achievement
|
||||
{
|
||||
@@ -11,7 +12,7 @@ namespace SWA::Achievement
|
||||
{
|
||||
public:
|
||||
SWA_INSERT_PADDING(0x08);
|
||||
be<uint32_t> m_AchievementID;
|
||||
be<EAchievementID> m_AchievementID;
|
||||
};
|
||||
|
||||
SWA_INSERT_PADDING(0x98);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <SWA.inl>
|
||||
#include <SWA/Achievement/AchievementID.h>
|
||||
|
||||
namespace SWA
|
||||
{
|
||||
@@ -9,7 +10,7 @@ namespace SWA
|
||||
public:
|
||||
SWA_INSERT_PADDING(0x38);
|
||||
be<uint32_t> m_Unk1;
|
||||
be<uint32_t> m_AchievementID;
|
||||
be<EAchievementID> m_AchievementID;
|
||||
uint8_t m_Unk2;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <SWA.inl>
|
||||
|
||||
namespace SWA
|
||||
{
|
||||
class CExStageBoss
|
||||
{
|
||||
public:
|
||||
class CStateBase;
|
||||
class CStateBattle;
|
||||
|
||||
class CExStageBossStateContext
|
||||
{
|
||||
public:
|
||||
SWA_INSERT_PADDING(0x14C);
|
||||
be<float> m_SplineProgress;
|
||||
SWA_INSERT_PADDING(0x0C);
|
||||
be<float> m_SplineSpeed;
|
||||
SWA_INSERT_PADDING(0x28);
|
||||
be<float> m_Field188;
|
||||
be<float> m_Field18C;
|
||||
SWA_INSERT_PADDING(0x21);
|
||||
bool m_IsBattleStart;
|
||||
SWA_INSERT_PADDING(0x36E);
|
||||
be<float> m_Field520;
|
||||
};
|
||||
};
|
||||
|
||||
SWA_ASSERT_OFFSETOF(CExStageBoss::CExStageBossStateContext, m_SplineProgress, 0x14C);
|
||||
SWA_ASSERT_OFFSETOF(CExStageBoss::CExStageBossStateContext, m_SplineSpeed, 0x15C);
|
||||
SWA_ASSERT_OFFSETOF(CExStageBoss::CExStageBossStateContext, m_Field188, 0x188);
|
||||
SWA_ASSERT_OFFSETOF(CExStageBoss::CExStageBossStateContext, m_Field18C, 0x18C);
|
||||
SWA_ASSERT_OFFSETOF(CExStageBoss::CExStageBossStateContext, m_IsBattleStart, 0x1B1);
|
||||
SWA_ASSERT_OFFSETOF(CExStageBoss::CExStageBossStateContext, m_Field520, 0x520);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <SWA.inl>
|
||||
#include "SWA/ExtraStage/Tails/Enemy/Boss/ExStageBoss.h"
|
||||
|
||||
namespace SWA
|
||||
{
|
||||
class CExStageBoss::CStateBase : public Hedgehog::Universe::CStateMachineBase::CStateBase {};
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <SWA.inl>
|
||||
#include "SWA/ExtraStage/Tails/Enemy/Boss/ExStageBoss.h"
|
||||
|
||||
namespace SWA
|
||||
{
|
||||
class CExStageBoss::CStateBattle : public CExStageBoss::CStateBase
|
||||
{
|
||||
public:
|
||||
SWA_INSERT_PADDING(0x08);
|
||||
be<float> m_Field68;
|
||||
be<float> m_FramesSinceLastMissile;
|
||||
};
|
||||
|
||||
SWA_ASSERT_OFFSETOF(CExStageBoss::CStateBattle, m_Field68, 0x68);
|
||||
SWA_ASSERT_OFFSETOF(CExStageBoss::CStateBattle, m_FramesSinceLastMissile, 0x6C);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <SWA.inl>
|
||||
|
||||
namespace SWA
|
||||
{
|
||||
class CExPlayerTails
|
||||
{
|
||||
public:
|
||||
class CExPlayerTailsStateContext
|
||||
{
|
||||
public:
|
||||
SWA_INSERT_PADDING(0x1F8);
|
||||
be<float> m_SplineBossStart;
|
||||
be<float> m_SplineEnd;
|
||||
SWA_INSERT_PADDING(0x30);
|
||||
be<float> m_SplineProgress;
|
||||
SWA_INSERT_PADDING(0x10);
|
||||
xpointer<void> m_Field244;
|
||||
SWA_INSERT_PADDING(0x18);
|
||||
be<float> m_SplineSpeed;
|
||||
SWA_INSERT_PADDING(0x0C);
|
||||
be<uint32_t> m_State; // 0 - Intro; 1 - Boss Intro; 3 - Boss
|
||||
};
|
||||
|
||||
class CStateBase : public Hedgehog::Universe::CStateMachineBase::CStateBase {};
|
||||
};
|
||||
|
||||
SWA_ASSERT_OFFSETOF(CExPlayerTails::CExPlayerTailsStateContext, m_SplineBossStart, 0x1F8);
|
||||
SWA_ASSERT_OFFSETOF(CExPlayerTails::CExPlayerTailsStateContext, m_SplineEnd, 0x1FC);
|
||||
SWA_ASSERT_OFFSETOF(CExPlayerTails::CExPlayerTailsStateContext, m_SplineProgress, 0x230);
|
||||
SWA_ASSERT_OFFSETOF(CExPlayerTails::CExPlayerTailsStateContext, m_Field244, 0x244);
|
||||
SWA_ASSERT_OFFSETOF(CExPlayerTails::CExPlayerTailsStateContext, m_SplineSpeed, 0x260);
|
||||
SWA_ASSERT_OFFSETOF(CExPlayerTails::CExPlayerTailsStateContext, m_State, 0x270);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -4,6 +4,15 @@
|
||||
|
||||
namespace SWA
|
||||
{
|
||||
enum EWindowStatus : uint32_t
|
||||
{
|
||||
eWindowStatus_Closed,
|
||||
eWindowStatus_OpeningMessage = 2,
|
||||
eWindowStatus_DisplayingMessage,
|
||||
eWindowStatus_OpeningControls,
|
||||
eWindowStatus_DisplayingControls
|
||||
};
|
||||
|
||||
class CGeneralWindow
|
||||
{
|
||||
public:
|
||||
@@ -14,5 +23,20 @@ namespace SWA
|
||||
Chao::CSD::RCPtr<Chao::CSD::CScene> m_rcWindow_2;
|
||||
Chao::CSD::RCPtr<Chao::CSD::CScene> m_rcWindowSelect;
|
||||
Chao::CSD::RCPtr<Chao::CSD::CScene> m_rcFooter;
|
||||
SWA_INSERT_PADDING(0x58);
|
||||
be<EWindowStatus> m_Status;
|
||||
be<uint32_t> m_CursorIndex;
|
||||
SWA_INSERT_PADDING(0x04);
|
||||
be<uint32_t> m_SelectedIndex;
|
||||
};
|
||||
|
||||
SWA_ASSERT_OFFSETOF(CGeneralWindow, m_rcGeneral, 0xD0);
|
||||
SWA_ASSERT_OFFSETOF(CGeneralWindow, m_rcBg, 0xD8);
|
||||
SWA_ASSERT_OFFSETOF(CGeneralWindow, m_rcWindow, 0xE0);
|
||||
SWA_ASSERT_OFFSETOF(CGeneralWindow, m_rcWindow_2, 0xE8);
|
||||
SWA_ASSERT_OFFSETOF(CGeneralWindow, m_rcWindowSelect, 0xF0);
|
||||
SWA_ASSERT_OFFSETOF(CGeneralWindow, m_rcFooter, 0xF8);
|
||||
SWA_ASSERT_OFFSETOF(CGeneralWindow, m_Status, 0x158);
|
||||
SWA_ASSERT_OFFSETOF(CGeneralWindow, m_CursorIndex, 0x15C);
|
||||
SWA_ASSERT_OFFSETOF(CGeneralWindow, m_SelectedIndex, 0x164);
|
||||
}
|
||||
|
||||
@@ -21,14 +21,20 @@ namespace SWA
|
||||
{
|
||||
public:
|
||||
SWA_INSERT_PADDING(0xD8);
|
||||
be<uint32_t> m_pUnk;
|
||||
be<uint32_t> m_FieldD8;
|
||||
SWA_INSERT_PADDING(0x3C);
|
||||
Chao::CSD::RCPtr<Chao::CSD::CScene> m_rcNightToDay;
|
||||
SWA_INSERT_PADDING(0x0C);
|
||||
be<uint32_t> m_IsVisible;
|
||||
SWA_INSERT_PADDING(0x0C);
|
||||
be<ELoadingDisplayType> m_LoadingDisplayType;
|
||||
SWA_INSERT_PADDING(0x65);
|
||||
SWA_INSERT_PADDING(0x61);
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -48,8 +48,7 @@ namespace SWA
|
||||
class CHudPause : public CGameObject
|
||||
{
|
||||
public:
|
||||
xpointer<void> m_pVftable;
|
||||
SWA_INSERT_PADDING(0x2C);
|
||||
SWA_INSERT_PADDING(0x30);
|
||||
RCPtr<CProject> m_rcPause;
|
||||
RCPtr<CScene> m_rcBg;
|
||||
RCPtr<CScene> m_rcBg1;
|
||||
@@ -58,12 +57,32 @@ namespace SWA
|
||||
RCPtr<CScene> m_rcBg1Select_2;
|
||||
RCPtr<CScene> m_rcStatusTitle;
|
||||
RCPtr<CScene> m_rcFooterA;
|
||||
SWA_INSERT_PADDING(0x5C);
|
||||
SWA_INSERT_PADDING(0x59);
|
||||
bool m_IsVisible;
|
||||
SWA_INSERT_PADDING(0x02);
|
||||
be<EActionType> m_Action;
|
||||
be<EMenuType> m_Menu;
|
||||
be<EStatusType> m_Status;
|
||||
be<ETransitionType> m_Transition;
|
||||
SWA_INSERT_PADDING(0x20);
|
||||
SWA_INSERT_PADDING(0x04);
|
||||
be<uint32_t> m_Submenu;
|
||||
SWA_INSERT_PADDING(0x18);
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <SWA.inl>
|
||||
|
||||
namespace SWA
|
||||
{
|
||||
class CObjDashPanel
|
||||
{
|
||||
public:
|
||||
SWA_INSERT_PADDING(0xE8);
|
||||
be<float> m_FieldE8;
|
||||
be<float> m_Speed;
|
||||
};
|
||||
}
|
||||
@@ -34,7 +34,9 @@ namespace SWA
|
||||
SWA_INSERT_PADDING(0x88);
|
||||
Hedgehog::Base::CSharedString m_StageName;
|
||||
xpointer<CSoundAdministrator> m_pSoundAdministrator;
|
||||
SWA_INSERT_PADDING(0x124);
|
||||
SWA_INSERT_PADDING(0x48);
|
||||
xpointer<CGeneralWindow> m_pGeneralWindow;
|
||||
SWA_INSERT_PADDING(0xD8);
|
||||
SScoreInfo m_ScoreInfo;
|
||||
SWA_INSERT_PADDING(0x0C);
|
||||
};
|
||||
@@ -60,6 +62,7 @@ namespace SWA
|
||||
SWA_ASSERT_OFFSETOF(CGameDocument::CMember, m_spDatabase, 0x1C);
|
||||
SWA_ASSERT_OFFSETOF(CGameDocument::CMember, m_StageName, 0xAC);
|
||||
SWA_ASSERT_OFFSETOF(CGameDocument::CMember, m_pSoundAdministrator, 0xB0);
|
||||
SWA_ASSERT_OFFSETOF(CGameDocument::CMember, m_pGeneralWindow, 0xFC);
|
||||
SWA_ASSERT_OFFSETOF(CGameDocument::CMember, m_ScoreInfo, 0x1D8);
|
||||
SWA_ASSERT_SIZEOF(CGameDocument::CMember, 0x230);
|
||||
|
||||
|
||||
@@ -4,10 +4,36 @@
|
||||
|
||||
namespace SWA
|
||||
{
|
||||
class CTitleMenu
|
||||
class CTitleMenu : public CMenuWindowBase
|
||||
{
|
||||
public:
|
||||
SWA_INSERT_PADDING(0x44);
|
||||
SWA_INSERT_PADDING(0x28);
|
||||
be<uint32_t> m_Field38;
|
||||
bool m_Field3C; // Seems to be related to exit transition.
|
||||
SWA_INSERT_PADDING(0x04);
|
||||
be<uint32_t> m_CursorIndex;
|
||||
SWA_INSERT_PADDING(0x0C);
|
||||
bool m_Field54; // Seems to be related to exit transition.
|
||||
SWA_INSERT_PADDING(0x0B);
|
||||
be<float> m_Field60;
|
||||
SWA_INSERT_PADDING(0x34);
|
||||
bool m_Field98;
|
||||
bool m_IsDeleteCheckMessageOpen;
|
||||
bool m_Field9A; // Seems to be related to cursor selection.
|
||||
SWA_INSERT_PADDING(0x04);
|
||||
bool m_Field9F;
|
||||
SWA_INSERT_PADDING(0x02);
|
||||
bool m_IsDLCInfoMessageOpen;
|
||||
};
|
||||
|
||||
SWA_ASSERT_OFFSETOF(CTitleMenu, m_Field38, 0x38);
|
||||
SWA_ASSERT_OFFSETOF(CTitleMenu, m_Field3C, 0x3C);
|
||||
SWA_ASSERT_OFFSETOF(CTitleMenu, m_CursorIndex, 0x44);
|
||||
SWA_ASSERT_OFFSETOF(CTitleMenu, m_Field54, 0x54);
|
||||
SWA_ASSERT_OFFSETOF(CTitleMenu, m_Field60, 0x60);
|
||||
SWA_ASSERT_OFFSETOF(CTitleMenu, m_Field98, 0x98);
|
||||
SWA_ASSERT_OFFSETOF(CTitleMenu, m_IsDeleteCheckMessageOpen, 0x99);
|
||||
SWA_ASSERT_OFFSETOF(CTitleMenu, m_Field9A, 0x9A);
|
||||
SWA_ASSERT_OFFSETOF(CTitleMenu, m_Field9F, 0x9F);
|
||||
SWA_ASSERT_OFFSETOF(CTitleMenu, m_IsDLCInfoMessageOpen, 0xA2);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <SWA.inl>
|
||||
|
||||
namespace SWA
|
||||
{
|
||||
class CTitleStateMenu : public CTitleStateBase {};
|
||||
}
|
||||
@@ -115,7 +115,7 @@ namespace boost
|
||||
}
|
||||
|
||||
public:
|
||||
shared_ptr() : px(nullptr), pn(nullptr) {}
|
||||
shared_ptr() : px(), pn() {}
|
||||
|
||||
// TODO
|
||||
explicit shared_ptr(T* p) = delete;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <app.h>
|
||||
#include "app.h"
|
||||
#include <api/SWA.h>
|
||||
#include <gpu/video.h>
|
||||
#include <install/installer.h>
|
||||
#include <kernel/function.h>
|
||||
@@ -8,6 +9,7 @@
|
||||
#include <ui/game_window.h>
|
||||
#include <user/config.h>
|
||||
#include <user/paths.h>
|
||||
#include <user/registry.h>
|
||||
|
||||
void App::Restart(std::vector<std::string> restartArgs)
|
||||
{
|
||||
@@ -34,6 +36,9 @@ PPC_FUNC(sub_824EB490)
|
||||
App::s_isMissingDLC = !Installer::checkAllDLC(GetGamePath());
|
||||
App::s_language = Config::Language;
|
||||
|
||||
SWA::SGlobals::Init();
|
||||
Registry::Save();
|
||||
|
||||
__imp__sub_824EB490(ctx, base);
|
||||
}
|
||||
|
||||
|
||||
@@ -94,6 +94,8 @@ void XAudioRegisterClient(PPCFunc* callback, uint32_t param)
|
||||
|
||||
void XAudioSubmitFrame(void* samples)
|
||||
{
|
||||
auto floatSamples = reinterpret_cast<be<float>*>(samples);
|
||||
|
||||
if (g_downMixToStereo)
|
||||
{
|
||||
// 0: left 1.0f, right 0.0f
|
||||
@@ -103,8 +105,6 @@ void XAudioSubmitFrame(void* samples)
|
||||
// 4: left 1.0f, right 0.0f
|
||||
// 5: left 0.0f, right 1.0f
|
||||
|
||||
auto floatSamples = reinterpret_cast<be<float>*>(samples);
|
||||
|
||||
std::array<float, 2 * XAUDIO_NUM_SAMPLES> audioFrames;
|
||||
|
||||
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 ch5 = floatSamples[5 * XAUDIO_NUM_SAMPLES + i];
|
||||
|
||||
audioFrames[i * 2 + 0] = ch0 + ch2 * 0.75f + ch4;
|
||||
audioFrames[i * 2 + 1] = ch1 + ch2 * 0.75f + ch5;
|
||||
audioFrames[i * 2 + 0] = (ch0 + ch2 * 0.75f + ch4) * Config::MasterVolume;
|
||||
audioFrames[i * 2 + 1] = (ch1 + ch2 * 0.75f + ch5) * Config::MasterVolume;
|
||||
}
|
||||
|
||||
SDL_QueueAudio(g_audioDevice, &audioFrames, sizeof(audioFrames));
|
||||
}
|
||||
else
|
||||
{
|
||||
auto rawSamples = reinterpret_cast<be<uint32_t>*>(samples);
|
||||
|
||||
std::array<uint32_t, XAUDIO_NUM_CHANNELS * XAUDIO_NUM_SAMPLES> audioFrames;
|
||||
std::array<float, XAUDIO_NUM_CHANNELS * XAUDIO_NUM_SAMPLES> audioFrames;
|
||||
|
||||
for (size_t i = 0; i < XAUDIO_NUM_SAMPLES; i++)
|
||||
{
|
||||
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));
|
||||
|
||||
@@ -93,7 +93,7 @@ static void PlayEmbeddedSound(EmbeddedSound s)
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -165,11 +165,7 @@ int GetThreadPriorityImpl(GuestThreadHandle* hThread)
|
||||
|
||||
uint32_t SetThreadIdealProcessorImpl(GuestThreadHandle* hThread, uint32_t dwIdealProcessor)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return SetThreadIdealProcessor(hThread == GetKernelObject(CURRENT_THREAD_HANDLE) ? GetCurrentThread() : hThread->thread.native_handle(), dwIdealProcessor);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
GUEST_FUNCTION_HOOK(sub_82DFA2E8, SetThreadNameImpl);
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
typedef returnType _##procName(__VA_ARGS__); \
|
||||
_##procName* procName = (_##procName*)PROC_ADDRESS(libraryName, #procName);
|
||||
|
||||
#define STR(x) #x
|
||||
|
||||
template<typename T>
|
||||
inline T RoundUp(const T& in_rValue, uint32_t in_round)
|
||||
{
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#define IMGUI_SHADER_MODIFIER_NONE 0
|
||||
#define IMGUI_SHADER_MODIFIER_SCANLINE 1
|
||||
#define IMGUI_SHADER_MODIFIER_CHECKERBOARD 2
|
||||
#define IMGUI_SHADER_MODIFIER_SCANLINE_BUTTON 3
|
||||
#define IMGUI_SHADER_MODIFIER_TEXT_SKEW 4
|
||||
#define IMGUI_SHADER_MODIFIER_MARQUEE_FADE 5
|
||||
#define IMGUI_SHADER_MODIFIER_GRAYSCALE 6
|
||||
#define IMGUI_SHADER_MODIFIER_TITLE_BEVEL 7
|
||||
#define IMGUI_SHADER_MODIFIER_CATEGORY_BEVEL 8
|
||||
#define IMGUI_SHADER_MODIFIER_NONE 0
|
||||
#define IMGUI_SHADER_MODIFIER_SCANLINE 1
|
||||
#define IMGUI_SHADER_MODIFIER_CHECKERBOARD 2
|
||||
#define IMGUI_SHADER_MODIFIER_SCANLINE_BUTTON 3
|
||||
#define IMGUI_SHADER_MODIFIER_TEXT_SKEW 4
|
||||
#define IMGUI_SHADER_MODIFIER_HORIZONTAL_MARQUEE_FADE 5
|
||||
#define IMGUI_SHADER_MODIFIER_VERTICAL_MARQUEE_FADE 6
|
||||
#define IMGUI_SHADER_MODIFIER_GRAYSCALE 7
|
||||
#define IMGUI_SHADER_MODIFIER_TITLE_BEVEL 8
|
||||
#define IMGUI_SHADER_MODIFIER_CATEGORY_BEVEL 9
|
||||
#define IMGUI_SHADER_MODIFIER_RECTANGLE_BEVEL 10
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -31,8 +33,10 @@ union ImGuiCallbackData
|
||||
{
|
||||
float boundsMin[2];
|
||||
float boundsMax[2];
|
||||
uint32_t gradientTop;
|
||||
uint32_t gradientBottom;
|
||||
uint32_t gradientTopLeft;
|
||||
uint32_t gradientTopRight;
|
||||
uint32_t gradientBottomRight;
|
||||
uint32_t gradientBottomLeft;
|
||||
} setGradient;
|
||||
|
||||
struct
|
||||
|
||||
@@ -221,6 +221,8 @@ static bool FontBuilder_Build(ImFontAtlas* atlas)
|
||||
for (size_t i = 0; i < atlas->ConfigData.size(); i++)
|
||||
{
|
||||
auto& config = atlas->ConfigData[i];
|
||||
bool increaseSpacing = strstr(config.Name, "Seurat") != nullptr;
|
||||
|
||||
auto& [index, count] = ranges[i];
|
||||
for (size_t j = 0; j < count; j++)
|
||||
{
|
||||
@@ -228,6 +230,11 @@ static bool FontBuilder_Build(ImFontAtlas* atlas)
|
||||
double x0, y0, x1, y1, u0, v0, u1, v1;
|
||||
glyph.getQuadPlaneBounds(x0, y0, x1, y1);
|
||||
glyph.getQuadAtlasBounds(u0, v0, u1, v1);
|
||||
|
||||
double advance = glyph.getAdvance();
|
||||
if (increaseSpacing && glyph.getCodepoint() == ' ')
|
||||
advance *= 1.5;
|
||||
|
||||
config.DstFont->AddGlyph(
|
||||
&config,
|
||||
glyph.getCodepoint(),
|
||||
@@ -239,7 +246,7 @@ static bool FontBuilder_Build(ImFontAtlas* atlas)
|
||||
v1 / packer.height,
|
||||
u1 / packer.width,
|
||||
v0 / packer.height,
|
||||
glyph.getAdvance());
|
||||
advance);
|
||||
}
|
||||
|
||||
config.DstFont->BuildLookupTable();
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
//# define D3D12_DEBUG_LAYER_GPU_BASED_VALIDATION_ENABLED
|
||||
#endif
|
||||
|
||||
//#define D3D12_DEBUG_SET_STABLE_POWER_STATE
|
||||
|
||||
// Old Windows SDK versions don't provide this macro, so we workaround it by making sure it is defined.
|
||||
#ifndef D3D12_RESOURCE_STATE_ALL_SHADER_RESOURCE
|
||||
#define D3D12_RESOURCE_STATE_ALL_SHADER_RESOURCE (D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE)
|
||||
@@ -692,6 +694,20 @@ namespace plume {
|
||||
);
|
||||
}
|
||||
|
||||
static D3D12_RESOLVE_MODE toD3D12(RenderResolveMode resolveMode) {
|
||||
switch (resolveMode) {
|
||||
case RenderResolveMode::MIN:
|
||||
return D3D12_RESOLVE_MODE_MIN;
|
||||
case RenderResolveMode::MAX:
|
||||
return D3D12_RESOLVE_MODE_MAX;
|
||||
case RenderResolveMode::AVERAGE:
|
||||
return D3D12_RESOLVE_MODE_AVERAGE;
|
||||
default:
|
||||
assert(false && "Unknown resolve mode.");
|
||||
return D3D12_RESOLVE_MODE_AVERAGE;
|
||||
}
|
||||
}
|
||||
|
||||
static void setObjectName(ID3D12Object *object, const std::string &name) {
|
||||
const std::wstring wideCharName = Utf8ToUtf16(name);
|
||||
object->SetName(wideCharName.c_str());
|
||||
@@ -1916,7 +1932,7 @@ namespace plume {
|
||||
resetSamplePositions();
|
||||
}
|
||||
|
||||
void D3D12CommandList::resolveTextureRegion(const RenderTexture *dstTexture, uint32_t dstX, uint32_t dstY, const RenderTexture *srcTexture, const RenderRect *srcRect) {
|
||||
void D3D12CommandList::resolveTextureRegion(const RenderTexture *dstTexture, uint32_t dstX, uint32_t dstY, const RenderTexture *srcTexture, const RenderRect *srcRect, RenderResolveMode resolveMode) {
|
||||
assert(dstTexture != nullptr);
|
||||
assert(srcTexture != nullptr);
|
||||
|
||||
@@ -1931,7 +1947,7 @@ namespace plume {
|
||||
}
|
||||
|
||||
setSamplePositions(interfaceDstTexture);
|
||||
d3d->ResolveSubresourceRegion(interfaceDstTexture->d3d, 0, dstX, dstY, interfaceSrcTexture->d3d, 0, (srcRect != nullptr) ? &rect : nullptr, toDXGI(interfaceDstTexture->desc.format), D3D12_RESOLVE_MODE_AVERAGE);
|
||||
d3d->ResolveSubresourceRegion(interfaceDstTexture->d3d, 0, dstX, dstY, interfaceSrcTexture->d3d, 0, (srcRect != nullptr) ? &rect : nullptr, toDXGI(interfaceDstTexture->desc.format), toD3D12(resolveMode));
|
||||
resetSamplePositions();
|
||||
}
|
||||
|
||||
@@ -3324,6 +3340,14 @@ namespace plume {
|
||||
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.
|
||||
bool preferOverNothing = (adapter == nullptr) || (d3d == nullptr);
|
||||
bool preferVideoMemory = adapterDesc.DedicatedVideoMemory > description.dedicatedVideoMemory;
|
||||
@@ -3346,6 +3370,7 @@ namespace plume {
|
||||
capabilities.sampleLocations = samplePositionsOption;
|
||||
capabilities.triangleFan = triangleFanSupportOption;
|
||||
capabilities.dynamicDepthBias = dynamicDepthBiasOption;
|
||||
capabilities.uma = uma;
|
||||
description.name = Utf16ToUtf8(adapterDesc.Description);
|
||||
description.dedicatedVideoMemory = adapterDesc.DedicatedVideoMemory;
|
||||
|
||||
@@ -3364,6 +3389,10 @@ namespace plume {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef D3D12_DEBUG_SET_STABLE_POWER_STATE
|
||||
d3d->SetStablePowerState(TRUE);
|
||||
#endif
|
||||
|
||||
D3D12MA::ALLOCATOR_DESC allocatorDesc = {};
|
||||
allocatorDesc.pDevice = d3d;
|
||||
allocatorDesc.pAdapter = adapter;
|
||||
|
||||
@@ -192,7 +192,7 @@ namespace plume {
|
||||
void copyBuffer(const RenderBuffer *dstBuffer, const RenderBuffer *srcBuffer) override;
|
||||
void copyTexture(const RenderTexture *dstTexture, const RenderTexture *srcTexture) override;
|
||||
void resolveTexture(const RenderTexture *dstTexture, const RenderTexture *srcTexture) override;
|
||||
void resolveTextureRegion(const RenderTexture *dstTexture, uint32_t dstX, uint32_t dstY, const RenderTexture *srcTexture, const RenderRect *srcRect) override;
|
||||
void resolveTextureRegion(const RenderTexture *dstTexture, uint32_t dstX, uint32_t dstY, const RenderTexture *srcTexture, const RenderRect *srcRect, RenderResolveMode resolveMode) override;
|
||||
void buildBottomLevelAS(const RenderAccelerationStructure *dstAccelerationStructure, RenderBufferReference scratchBuffer, const RenderBottomLevelASBuildInfo &buildInfo) override;
|
||||
void buildTopLevelAS(const RenderAccelerationStructure *dstAccelerationStructure, RenderBufferReference scratchBuffer, RenderBufferReference instancesBuffer, const RenderTopLevelASBuildInfo &buildInfo) override;
|
||||
void discardTexture(const RenderTexture* texture) override;
|
||||
|
||||
@@ -143,7 +143,7 @@ namespace plume {
|
||||
virtual void copyBuffer(const RenderBuffer *dstBuffer, const RenderBuffer *srcBuffer) = 0;
|
||||
virtual void copyTexture(const RenderTexture *dstTexture, const RenderTexture *srcTexture) = 0;
|
||||
virtual void resolveTexture(const RenderTexture *dstTexture, const RenderTexture *srcTexture) = 0;
|
||||
virtual void resolveTextureRegion(const RenderTexture *dstTexture, uint32_t dstX, uint32_t dstY, const RenderTexture *srcTexture, const RenderRect *srcRect = nullptr) = 0;
|
||||
virtual void resolveTextureRegion(const RenderTexture *dstTexture, uint32_t dstX, uint32_t dstY, const RenderTexture *srcTexture, const RenderRect *srcRect = nullptr, RenderResolveMode resolveMode = RenderResolveMode::AVERAGE) = 0;
|
||||
virtual void buildBottomLevelAS(const RenderAccelerationStructure *dstAccelerationStructure, RenderBufferReference scratchBuffer, const RenderBottomLevelASBuildInfo &buildInfo) = 0;
|
||||
virtual void buildTopLevelAS(const RenderAccelerationStructure *dstAccelerationStructure, RenderBufferReference scratchBuffer, RenderBufferReference instancesBuffer, const RenderTopLevelASBuildInfo &buildInfo) = 0;
|
||||
virtual void discardTexture(const RenderTexture* texture) = 0; // D3D12 only.
|
||||
|
||||
@@ -475,6 +475,20 @@ namespace plume {
|
||||
|
||||
typedef uint32_t RenderSampleCounts;
|
||||
|
||||
enum class RenderDeviceType {
|
||||
UNKNOWN,
|
||||
INTEGRATED,
|
||||
DISCRETE,
|
||||
VIRTUAL,
|
||||
CPU
|
||||
};
|
||||
|
||||
enum class RenderResolveMode {
|
||||
MIN,
|
||||
MAX,
|
||||
AVERAGE
|
||||
};
|
||||
|
||||
// Global functions.
|
||||
|
||||
constexpr uint32_t RenderFormatSize(RenderFormat format) {
|
||||
@@ -1754,6 +1768,7 @@ namespace plume {
|
||||
|
||||
struct RenderDeviceDescription {
|
||||
std::string name = "Unknown";
|
||||
RenderDeviceType type = RenderDeviceType::UNKNOWN;
|
||||
uint32_t driverVersion = 0;
|
||||
uint64_t dedicatedVideoMemory = 0;
|
||||
};
|
||||
@@ -1780,6 +1795,9 @@ namespace plume {
|
||||
// Draw.
|
||||
bool triangleFan = false;
|
||||
bool dynamicDepthBias = false;
|
||||
|
||||
// UMA.
|
||||
bool uma = false;
|
||||
};
|
||||
|
||||
struct RenderInterfaceCapabilities {
|
||||
|
||||
@@ -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) {
|
||||
# ifdef VULKAN_OBJECT_NAMES_ENABLED
|
||||
VkDebugMarkerObjectNameInfoEXT nameInfo = {};
|
||||
@@ -3059,12 +3074,13 @@ namespace plume {
|
||||
}
|
||||
|
||||
void VulkanCommandList::resolveTexture(const RenderTexture *dstTexture, const RenderTexture *srcTexture) {
|
||||
resolveTextureRegion(dstTexture, 0, 0, srcTexture, nullptr);
|
||||
resolveTextureRegion(dstTexture, 0, 0, srcTexture, nullptr, RenderResolveMode::AVERAGE);
|
||||
}
|
||||
|
||||
void VulkanCommandList::resolveTextureRegion(const RenderTexture *dstTexture, uint32_t dstX, uint32_t dstY, const RenderTexture *srcTexture, const RenderRect *srcRect) {
|
||||
void VulkanCommandList::resolveTextureRegion(const RenderTexture *dstTexture, uint32_t dstX, uint32_t dstY, const RenderTexture *srcTexture, const RenderRect *srcRect, RenderResolveMode resolveMode) {
|
||||
assert(dstTexture != nullptr);
|
||||
assert(srcTexture != nullptr);
|
||||
assert(resolveMode == RenderResolveMode::AVERAGE && "Vulkan only supports AVERAGE resolve mode.");
|
||||
|
||||
thread_local std::vector<VkImageResolve> imageResolves;
|
||||
imageResolves.clear();
|
||||
@@ -3496,6 +3512,7 @@ namespace plume {
|
||||
if (preferOption) {
|
||||
physicalDevice = physicalDevices[i];
|
||||
description.name = std::string(deviceProperties.deviceName);
|
||||
description.type = toDeviceType(deviceProperties.deviceType);
|
||||
description.driverVersion = deviceProperties.driverVersion;
|
||||
currentDeviceTypeScore = deviceTypeScore;
|
||||
}
|
||||
|
||||
@@ -315,7 +315,7 @@ namespace plume {
|
||||
void copyBuffer(const RenderBuffer *dstBuffer, const RenderBuffer *srcBuffer) override;
|
||||
void copyTexture(const RenderTexture *dstTexture, const RenderTexture *srcTexture) override;
|
||||
void resolveTexture(const RenderTexture *dstTexture, const RenderTexture *srcTexture) override;
|
||||
void resolveTextureRegion(const RenderTexture *dstTexture, uint32_t dstX, uint32_t dstY, const RenderTexture *srcTexture, const RenderRect *srcRect) override;
|
||||
void resolveTextureRegion(const RenderTexture *dstTexture, uint32_t dstX, uint32_t dstY, const RenderTexture *srcTexture, const RenderRect *srcRect, RenderResolveMode resolveMode) override;
|
||||
void buildBottomLevelAS(const RenderAccelerationStructure *dstAccelerationStructure, RenderBufferReference scratchBuffer, const RenderBottomLevelASBuildInfo &buildInfo) override;
|
||||
void buildTopLevelAS(const RenderAccelerationStructure *dstAccelerationStructure, RenderBufferReference scratchBuffer, RenderBufferReference instancesBuffer, const RenderTopLevelASBuildInfo &buildInfo) override;
|
||||
void discardTexture(const RenderTexture* texture) override;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#include "copy_common.hlsli"
|
||||
|
||||
Texture2D<float4> g_Texture2DDescriptorHeap[] : register(t0, space0);
|
||||
|
||||
float4 main(in float4 position : SV_Position) : SV_Target
|
||||
{
|
||||
return g_Texture2DDescriptorHeap[g_PushConstants.ResourceDescriptorIndex].Load(int3(position.xy, 0));
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
struct PushConstants
|
||||
{
|
||||
uint ResourceDescriptorIndex;
|
||||
};
|
||||
|
||||
[[vk::push_constant]] ConstantBuffer<PushConstants> g_PushConstants : register(b3, space4);
|
||||
@@ -0,0 +1,8 @@
|
||||
#include "copy_common.hlsli"
|
||||
|
||||
Texture2D<float> g_Texture2DDescriptorHeap[] : register(t0, space0);
|
||||
|
||||
float main(in float4 position : SV_Position) : SV_Depth
|
||||
{
|
||||
return g_Texture2DDescriptorHeap[g_PushConstants.ResourceDescriptorIndex].Load(int3(position.xy, 0));
|
||||
}
|
||||
@@ -38,7 +38,7 @@ cbuffer SharedConstants : register(b2, space4)
|
||||
|
||||
#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> 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 sampZBuffer_s = g_SamplerDescriptorHeap[sampZBuffer_SamplerDescriptorIndex];
|
||||
|
||||
float depth = sampZBuffer.SampleLevel(sampZBuffer_s, texCoord, 0).x;
|
||||
float4 velocityMap = sampVelocityMap.SampleLevel(sampVelocityMap_s, texCoord, 0);
|
||||
float depth = sampZBuffer.SampleLevel(sampZBuffer_s, texCoord.xy, 0).x;
|
||||
float4 velocityMap = sampVelocityMap.SampleLevel(sampVelocityMap_s, texCoord.xy, 0);
|
||||
float2 velocity = (velocityMap.xz + velocityMap.yw / 255.0) * 2.0 - 1.0;
|
||||
|
||||
int sampleCount = min(64, round(length(velocity * g_ViewportSize.xy)));
|
||||
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;
|
||||
|
||||
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;
|
||||
float sampleDepth = sampZBuffer.SampleLevel(sampZBuffer_s, sampleCoord, 0).x;
|
||||
|
||||
|
||||
@@ -6,8 +6,10 @@ struct PushConstants
|
||||
{
|
||||
float2 BoundsMin;
|
||||
float2 BoundsMax;
|
||||
uint GradientTop;
|
||||
uint GradientBottom;
|
||||
uint GradientTopLeft;
|
||||
uint GradientTopRight;
|
||||
uint GradientBottomRight;
|
||||
uint GradientBottomLeft;
|
||||
uint ShaderModifier;
|
||||
uint Texture2DDescriptorIndex;
|
||||
float2 InverseDisplaySize;
|
||||
|
||||
@@ -60,17 +60,17 @@ float4 SampleLinear(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))
|
||||
uvTexspace *= g_PushConstants.InverseDisplaySize.y * 720.0f;
|
||||
else
|
||||
uvTexspace *= g_PushConstants.InverseDisplaySize.x * 960.0f;
|
||||
|
||||
float2 seam = floor(uvTexspace + 0.5);
|
||||
uvTexspace = (uvTexspace - seam) / fwidth(uvTexspace) + seam;
|
||||
uvTexspace = clamp(uvTexspace, seam - 0.5, seam + 0.5);
|
||||
|
||||
return SampleLinear(uvTexspace);
|
||||
return SampleLinear(uvTexspace - 0.5);
|
||||
}
|
||||
|
||||
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 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)
|
||||
{
|
||||
@@ -135,10 +135,18 @@ 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 maxAlpha = saturate((g_PushConstants.BoundsMax.x - interpolators.Position.x) / g_PushConstants.Scale.x);
|
||||
float maxAlpha = saturate((g_PushConstants.BoundsMax.x - interpolators.Position.x) / g_PushConstants.Scale.y);
|
||||
|
||||
color.a *= minAlpha;
|
||||
color.a *= maxAlpha;
|
||||
}
|
||||
else if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_VERTICAL_MARQUEE_FADE)
|
||||
{
|
||||
float minAlpha = saturate((interpolators.Position.y - g_PushConstants.BoundsMin.y) / g_PushConstants.Scale.x);
|
||||
float maxAlpha = saturate((g_PushConstants.BoundsMax.y - interpolators.Position.y) / g_PushConstants.Scale.y);
|
||||
|
||||
color.a *= minAlpha;
|
||||
color.a *= maxAlpha;
|
||||
@@ -146,7 +154,29 @@ float4 main(in Interpolators interpolators) : SV_Target
|
||||
else if (any(g_PushConstants.BoundsMin != g_PushConstants.BoundsMax))
|
||||
{
|
||||
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)
|
||||
|
||||
@@ -7,7 +7,8 @@ void main(in float2 position : POSITION, in float2 uv : TEXCOORD, in float4 colo
|
||||
if (position.y < g_PushConstants.Origin.y)
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "copy_common.hlsli"
|
||||
|
||||
Texture2DMS<float4, SAMPLE_COUNT> g_Texture2DMSDescriptorHeap[] : register(t0, space0);
|
||||
|
||||
float4 main(in float4 position : SV_Position) : SV_Target
|
||||
{
|
||||
float4 result = g_Texture2DMSDescriptorHeap[g_PushConstants.ResourceDescriptorIndex].Load(int2(position.xy), 0);
|
||||
|
||||
[unroll] for (int i = 1; i < SAMPLE_COUNT; i++)
|
||||
result += g_Texture2DMSDescriptorHeap[g_PushConstants.ResourceDescriptorIndex].Load(int2(position.xy), i);
|
||||
|
||||
return result / SAMPLE_COUNT;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
#define SAMPLE_COUNT 2
|
||||
#include "resolve_msaa_color.hlsli"
|
||||
@@ -0,0 +1,2 @@
|
||||
#define SAMPLE_COUNT 4
|
||||
#include "resolve_msaa_color.hlsli"
|
||||
@@ -0,0 +1,2 @@
|
||||
#define SAMPLE_COUNT 8
|
||||
#include "resolve_msaa_color.hlsli"
|
||||
@@ -1,11 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
struct PushConstants
|
||||
{
|
||||
uint ResourceDescriptorIndex;
|
||||
};
|
||||
|
||||
[[vk::push_constant]] ConstantBuffer<PushConstants> g_PushConstants : register(b3, space4);
|
||||
#include "copy_common.hlsli"
|
||||
|
||||
Texture2DMS<float, SAMPLE_COUNT> g_Texture2DMSDescriptorHeap[] : register(t0, space0);
|
||||
|
||||
|
||||
+1073
-451
File diff suppressed because it is too large
Load Diff
@@ -18,7 +18,7 @@ struct Video
|
||||
static inline uint32_t s_viewportWidth;
|
||||
static inline uint32_t s_viewportHeight;
|
||||
|
||||
static void CreateHostDevice(const char *sdlVideoDriver);
|
||||
static bool CreateHostDevice(const char *sdlVideoDriver);
|
||||
static void WaitOnSwapChain();
|
||||
static void Present();
|
||||
static void StartPipelinePrecompilation();
|
||||
@@ -158,6 +158,7 @@ struct GuestTexture : GuestBaseTexture
|
||||
void* mappedMemory = nullptr;
|
||||
std::unique_ptr<RenderFramebuffer> framebuffer;
|
||||
std::unique_ptr<GuestTexture> patchedTexture;
|
||||
struct GuestSurface* sourceSurface = nullptr;
|
||||
};
|
||||
|
||||
struct GuestLockedRect
|
||||
@@ -205,6 +206,7 @@ struct GuestSurface : GuestBaseTexture
|
||||
uint32_t guestFormat = 0;
|
||||
ankerl::unordered_dense::map<const RenderTexture*, std::unique_ptr<RenderFramebuffer>> framebuffers;
|
||||
RenderSampleCounts sampleCount = RenderSampleCount::COUNT_1;
|
||||
ankerl::unordered_dense::set<GuestTexture*> destinationTextures;
|
||||
};
|
||||
|
||||
enum GuestDeclType
|
||||
|
||||
@@ -68,7 +68,7 @@ public:
|
||||
|
||||
bool CanPoll()
|
||||
{
|
||||
return controller && (GameWindow::s_isFocused || Config::AllowBackgroundInput);
|
||||
return controller;
|
||||
}
|
||||
|
||||
void PollAxis()
|
||||
@@ -127,6 +127,11 @@ public:
|
||||
|
||||
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;
|
||||
@@ -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)
|
||||
{
|
||||
switch (event->type)
|
||||
@@ -191,7 +205,13 @@ int HID_OnSDLEvent(void*, SDL_Event* event)
|
||||
const auto freeIndex = FindFreeController();
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -219,12 +239,16 @@ int HID_OnSDLEvent(void*, SDL_Event* event)
|
||||
if (event->type == SDL_CONTROLLERAXISMOTION)
|
||||
{
|
||||
if (abs(event->caxis.value) > 8000)
|
||||
{
|
||||
SDL_ShowCursor(SDL_DISABLE);
|
||||
SetControllerInputDevice(controller);
|
||||
}
|
||||
|
||||
controller->PollAxis();
|
||||
}
|
||||
else
|
||||
{
|
||||
SDL_ShowCursor(SDL_DISABLE);
|
||||
SetControllerInputDevice(controller);
|
||||
|
||||
controller->Poll();
|
||||
@@ -241,8 +265,14 @@ int HID_OnSDLEvent(void*, SDL_Event* event)
|
||||
case SDL_MOUSEMOTION:
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
{
|
||||
if (!GameWindow::IsFullscreen() || GameWindow::s_isFullscreenCursorVisible)
|
||||
SDL_ShowCursor(SDL_ENABLE);
|
||||
|
||||
hid::g_inputDevice = hid::EInputDevice::Mouse;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL_WINDOWEVENT:
|
||||
{
|
||||
@@ -258,17 +288,8 @@ int HID_OnSDLEvent(void*, SDL_Event* event)
|
||||
|
||||
case SDL_USER_EVILSONIC:
|
||||
{
|
||||
auto* controller = FindController(event->cdevice.which);
|
||||
|
||||
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);
|
||||
for (auto& controller : g_controllers)
|
||||
SetControllerTimeOfDayLED(controller, event->user.code);
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -279,12 +300,15 @@ int HID_OnSDLEvent(void*, SDL_Event* event)
|
||||
|
||||
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_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_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_ALLOW_BACKGROUND_EVENTS, "1");
|
||||
SDL_SetHint(SDL_HINT_XINPUT_ENABLED, "1");
|
||||
|
||||
SDL_InitSubSystem(SDL_INIT_EVENTS);
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#include "hid.h"
|
||||
#include <ui/game_window.h>
|
||||
#include <user/config.h>
|
||||
|
||||
hid::EInputDevice hid::g_inputDevice;
|
||||
hid::EInputDevice hid::g_inputDeviceController;
|
||||
@@ -11,6 +13,11 @@ void hid::SetProhibitedButtons(uint16_t wButtons)
|
||||
hid::g_prohibitedButtons = wButtons;
|
||||
}
|
||||
|
||||
bool hid::IsInputAllowed()
|
||||
{
|
||||
return GameWindow::s_isFocused || Config::AllowBackgroundInput;
|
||||
}
|
||||
|
||||
bool hid::IsInputDeviceController()
|
||||
{
|
||||
return hid::g_inputDevice != hid::EInputDevice::Keyboard &&
|
||||
|
||||
@@ -35,12 +35,13 @@ namespace hid
|
||||
extern uint16_t g_prohibitedButtons;
|
||||
|
||||
void Init();
|
||||
void SetProhibitedButtons(uint16_t wButtons);
|
||||
|
||||
uint32_t GetState(uint32_t dwUserIndex, XAMINPUT_STATE* pState);
|
||||
uint32_t SetState(uint32_t dwUserIndex, XAMINPUT_VIBRATION* pVibration);
|
||||
uint32_t GetCapabilities(uint32_t dwUserIndex, XAMINPUT_CAPABILITIES* pCaps);
|
||||
|
||||
void SetProhibitedButtons(uint16_t wButtons);
|
||||
bool IsInputAllowed();
|
||||
bool IsInputDeviceController();
|
||||
std::string GetInputDeviceName();
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ static std::unique_ptr<VirtualFileSystem> createFileSystemFromPath(const std::fi
|
||||
}
|
||||
}
|
||||
|
||||
static bool copyFile(const FilePair &pair, const uint64_t *fileHashes, VirtualFileSystem &sourceVfs, const std::filesystem::path &targetDirectory, bool skipHashChecks, std::vector<uint8_t> &fileData, Journal &journal, const std::function<void()> &progressCallback) {
|
||||
static bool copyFile(const FilePair &pair, const uint64_t *fileHashes, VirtualFileSystem &sourceVfs, const std::filesystem::path &targetDirectory, bool skipHashChecks, std::vector<uint8_t> &fileData, Journal &journal, const std::function<bool()> &progressCallback) {
|
||||
const std::string filename(pair.first);
|
||||
const uint32_t hashCount = pair.second;
|
||||
if (!sourceVfs.exists(filename))
|
||||
@@ -139,7 +139,13 @@ static bool copyFile(const FilePair &pair, const uint64_t *fileHashes, VirtualFi
|
||||
}
|
||||
|
||||
journal.progressCounter += fileData.size();
|
||||
progressCallback();
|
||||
|
||||
if (!progressCallback())
|
||||
{
|
||||
journal.lastResult = Journal::Result::Cancelled;
|
||||
journal.lastErrorMessage = "Installation was cancelled.";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -201,7 +207,17 @@ static DLC detectDLC(const std::filesystem::path &sourcePath, VirtualFileSystem
|
||||
bool Installer::checkGameInstall(const std::filesystem::path &baseDirectory, std::filesystem::path &modulePath)
|
||||
{
|
||||
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)
|
||||
@@ -256,7 +272,7 @@ bool Installer::computeTotalSize(std::span<const FilePair> filePairs, const uint
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Installer::copyFiles(std::span<const FilePair> filePairs, const uint64_t *fileHashes, VirtualFileSystem &sourceVfs, const std::filesystem::path &targetDirectory, const std::string &validationFile, bool skipHashChecks, Journal &journal, const std::function<void()> &progressCallback)
|
||||
bool Installer::copyFiles(std::span<const FilePair> filePairs, const uint64_t *fileHashes, VirtualFileSystem &sourceVfs, const std::filesystem::path &targetDirectory, const std::string &validationFile, bool skipHashChecks, Journal &journal, const std::function<bool()> &progressCallback)
|
||||
{
|
||||
std::error_code ec;
|
||||
if (!std::filesystem::exists(targetDirectory) && !std::filesystem::create_directories(targetDirectory, ec))
|
||||
@@ -419,7 +435,7 @@ bool Installer::parseSources(const Input &input, Journal &journal, Sources &sour
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Installer::install(const Sources &sources, const std::filesystem::path &targetDirectory, bool skipHashChecks, Journal &journal, const std::function<void()> &progressCallback)
|
||||
bool Installer::install(const Sources &sources, const std::filesystem::path &targetDirectory, bool skipHashChecks, Journal &journal, std::chrono::seconds endWaitTime, const std::function<bool()> &progressCallback)
|
||||
{
|
||||
// Install files in reverse order of importance. In case of a process crash or power outage, this will increase the likelihood of the installation
|
||||
// missing critical files required for the game to run. These files are used as the way to detect if the game is installed.
|
||||
@@ -487,7 +503,22 @@ bool Installer::install(const Sources &sources, const std::filesystem::path &tar
|
||||
|
||||
// Update the progress with the artificial amount attributed to the patching.
|
||||
journal.progressCounter += PatcherContribution;
|
||||
progressCallback();
|
||||
|
||||
for (uint32_t i = 0; i < 2; i++)
|
||||
{
|
||||
if (!progressCallback())
|
||||
{
|
||||
journal.lastResult = Journal::Result::Cancelled;
|
||||
journal.lastErrorMessage = "Installation was cancelled.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
// Wait the specified amount of time to allow the consumer of the callbacks to animate, halt or cancel the installation for a while after it's finished.
|
||||
std::this_thread::sleep_for(endWaitTime);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ struct Journal
|
||||
enum class Result
|
||||
{
|
||||
Success,
|
||||
Cancelled,
|
||||
VirtualFileSystemFailed,
|
||||
DirectoryCreationFailed,
|
||||
FileMissing,
|
||||
@@ -75,10 +76,10 @@ struct Installer
|
||||
static bool checkDLCInstall(const std::filesystem::path &baseDirectory, DLC dlc);
|
||||
static bool checkAllDLC(const std::filesystem::path &baseDirectory);
|
||||
static bool computeTotalSize(std::span<const FilePair> filePairs, const uint64_t *fileHashes, VirtualFileSystem &sourceVfs, Journal &journal, uint64_t &totalSize);
|
||||
static bool copyFiles(std::span<const FilePair> filePairs, const uint64_t *fileHashes, VirtualFileSystem &sourceVfs, const std::filesystem::path &targetDirectory, const std::string &validationFile, bool skipHashChecks, Journal &journal, const std::function<void()> &progressCallback);
|
||||
static bool copyFiles(std::span<const FilePair> filePairs, const uint64_t *fileHashes, VirtualFileSystem &sourceVfs, const std::filesystem::path &targetDirectory, const std::string &validationFile, bool skipHashChecks, Journal &journal, const std::function<bool()> &progressCallback);
|
||||
static bool parseContent(const std::filesystem::path &sourcePath, std::unique_ptr<VirtualFileSystem> &targetVfs, Journal &journal);
|
||||
static bool parseSources(const Input &input, Journal &journal, Sources &sources);
|
||||
static bool install(const Sources &sources, const std::filesystem::path &targetDirectory, bool skipHashChecks, Journal &journal, const std::function<void()> &progressCallback);
|
||||
static bool install(const Sources &sources, const std::filesystem::path &targetDirectory, bool skipHashChecks, Journal &journal, std::chrono::seconds endWaitTime, const std::function<bool()> &progressCallback);
|
||||
static void rollback(Journal &journal);
|
||||
|
||||
// Convenience method for checking if the specified file contains the game. This should be used when the user selects the file.
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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();
|
||||
};
|
||||
@@ -405,52 +405,62 @@ uint32_t XamInputGetState(uint32_t userIndex, uint32_t flags, XAMINPUT_STATE* st
|
||||
{
|
||||
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);
|
||||
|
||||
if (GameWindow::s_isFocused && !keyboardState[SDL_SCANCODE_LALT])
|
||||
{
|
||||
auto keyboardState = SDL_GetKeyboardState(NULL);
|
||||
|
||||
if (keyboardState[SDL_SCANCODE_UP])
|
||||
if (keyboardState[Config::Key_LeftStickUp])
|
||||
state->Gamepad.sThumbLY = 32767;
|
||||
if (keyboardState[SDL_SCANCODE_DOWN])
|
||||
if (keyboardState[Config::Key_LeftStickDown])
|
||||
state->Gamepad.sThumbLY = -32768;
|
||||
if (keyboardState[SDL_SCANCODE_LEFT])
|
||||
if (keyboardState[Config::Key_LeftStickLeft])
|
||||
state->Gamepad.sThumbLX = -32768;
|
||||
if (keyboardState[SDL_SCANCODE_RIGHT])
|
||||
if (keyboardState[Config::Key_LeftStickRight])
|
||||
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;
|
||||
if (keyboardState[SDL_SCANCODE_3])
|
||||
if (keyboardState[Config::Key_RightTrigger])
|
||||
state->Gamepad.bRightTrigger = 0xFF;
|
||||
|
||||
if (keyboardState[SDL_SCANCODE_I])
|
||||
if (keyboardState[Config::Key_DPadUp])
|
||||
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_DPAD_UP;
|
||||
if (keyboardState[SDL_SCANCODE_K])
|
||||
if (keyboardState[Config::Key_DPadDown])
|
||||
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_DPAD_DOWN;
|
||||
if (keyboardState[SDL_SCANCODE_J])
|
||||
if (keyboardState[Config::Key_DPadLeft])
|
||||
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_DPAD_LEFT;
|
||||
if (keyboardState[SDL_SCANCODE_L])
|
||||
if (keyboardState[Config::Key_DPadRight])
|
||||
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;
|
||||
if (keyboardState[SDL_SCANCODE_BACKSPACE])
|
||||
if (keyboardState[Config::Key_Back])
|
||||
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_BACK;
|
||||
|
||||
if (keyboardState[SDL_SCANCODE_Q])
|
||||
if (keyboardState[Config::Key_LeftBumper])
|
||||
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_LEFT_SHOULDER;
|
||||
if (keyboardState[SDL_SCANCODE_E])
|
||||
if (keyboardState[Config::Key_RightBumper])
|
||||
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_RIGHT_SHOULDER;
|
||||
|
||||
if (keyboardState[SDL_SCANCODE_S])
|
||||
if (keyboardState[Config::Key_A])
|
||||
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_A;
|
||||
if (keyboardState[SDL_SCANCODE_D])
|
||||
if (keyboardState[Config::Key_B])
|
||||
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_B;
|
||||
if (keyboardState[SDL_SCANCODE_A])
|
||||
if (keyboardState[Config::Key_X])
|
||||
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_X;
|
||||
if (keyboardState[SDL_SCANCODE_W])
|
||||
if (keyboardState[Config::Key_Y])
|
||||
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_Y;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,11 +27,11 @@
|
||||
CONFIG_DEFINE_LOCALE(Language)
|
||||
{
|
||||
{ ELanguage::English, { "Language", "Change the language used for text and logos." } },
|
||||
{ ELanguage::Japanese, { "言語", "[PLACEHOLDER]" } },
|
||||
{ ELanguage::German, { "Sprache", "[PLACEHOLDER]" } },
|
||||
{ ELanguage::French, { "Langue", "[PLACEHOLDER]" } },
|
||||
{ ELanguage::Spanish, { "Idioma", "[PLACEHOLDER]" } },
|
||||
{ ELanguage::Italian, { "Lingua", "[PLACEHOLDER]" } }
|
||||
{ ELanguage::Japanese, { "言語", "" } },
|
||||
{ ELanguage::German, { "Sprache", "" } },
|
||||
{ ELanguage::French, { "Langue", "" } },
|
||||
{ ELanguage::Spanish, { "Idioma", "" } },
|
||||
{ ELanguage::Italian, { "Lingua", "" } }
|
||||
};
|
||||
|
||||
CONFIG_DEFINE_ENUM_LOCALE(ELanguage)
|
||||
@@ -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)
|
||||
{
|
||||
{ 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)
|
||||
@@ -139,7 +196,7 @@ CONFIG_DEFINE_LOCALE(MusicAttenuation)
|
||||
|
||||
CONFIG_DEFINE_LOCALE(ChannelConfiguration)
|
||||
{
|
||||
{ ELanguage::English, { "Channel Configuration", "" } }
|
||||
{ ELanguage::English, { "Channel Configuration", "Change the output mode for your playback device." } }
|
||||
};
|
||||
|
||||
CONFIG_DEFINE_ENUM_LOCALE(EChannelConfiguration)
|
||||
@@ -158,17 +215,6 @@ CONFIG_DEFINE_LOCALE(VoiceLanguage)
|
||||
{ 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)
|
||||
{
|
||||
{ ELanguage::English, { "Subtitles", "Show subtitles during dialogue." } }
|
||||
@@ -200,7 +246,9 @@ CONFIG_DEFINE_ENUM_LOCALE(EAspectRatio)
|
||||
ELanguage::English,
|
||||
{
|
||||
{ EAspectRatio::Auto, { "AUTO", "Auto: the aspect ratio will dynamically adjust to the window size." } },
|
||||
{ EAspectRatio::OriginalNarrow, { "ORIGINAL 4:3", "" } }
|
||||
{ EAspectRatio::Wide, { "16:9", "16:9: locks the game to a widescreen aspect ratio." } },
|
||||
{ EAspectRatio::Narrow, { "4:3", "4:3: locks the game to a narrow aspect ratio." } },
|
||||
{ EAspectRatio::OriginalNarrow, { "ORIGINAL 4:3", "Original 4:3: locks the game to a narrow aspect ratio and retains parity with the game's original implementation." } }
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -227,7 +275,7 @@ CONFIG_DEFINE_LOCALE(FPS)
|
||||
|
||||
CONFIG_DEFINE_LOCALE(Brightness)
|
||||
{
|
||||
{ ELanguage::English, { "Brightness", "Adjust the brightness level of the game." } }
|
||||
{ ELanguage::English, { "Brightness", "Adjust the brightness level until the symbol on the left is barely visible." } }
|
||||
};
|
||||
|
||||
CONFIG_DEFINE_LOCALE(AntiAliasing)
|
||||
@@ -260,9 +308,7 @@ CONFIG_DEFINE_ENUM_LOCALE(EShadowResolution)
|
||||
{
|
||||
ELanguage::English,
|
||||
{
|
||||
{ EShadowResolution::Original, { "ORIGINAL", "Original: the game will automatically determine the resolution of the shadows." } },
|
||||
{ EShadowResolution::x4096, { "4096", "High resolutions can degrade performance significantly on lower end hardware." } },
|
||||
{ EShadowResolution::x8192, { "8192", "High resolutions can degrade performance significantly on lower end hardware." } },
|
||||
{ EShadowResolution::Original, { "ORIGINAL", "Original: the game will automatically determine the resolution of the shadows." } }
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -307,7 +353,7 @@ CONFIG_DEFINE_LOCALE(XboxColorCorrection)
|
||||
|
||||
CONFIG_DEFINE_LOCALE(CutsceneAspectRatio)
|
||||
{
|
||||
{ ELanguage::English, { "Cutscene Aspect Ratio", "" } }
|
||||
{ ELanguage::English, { "Cutscene Aspect Ratio", "Change the aspect ratio of the real-time cutscenes." } }
|
||||
};
|
||||
|
||||
CONFIG_DEFINE_ENUM_LOCALE(ECutsceneAspectRatio)
|
||||
@@ -315,24 +361,24 @@ CONFIG_DEFINE_ENUM_LOCALE(ECutsceneAspectRatio)
|
||||
{
|
||||
ELanguage::English,
|
||||
{
|
||||
{ ECutsceneAspectRatio::Original, { "ORIGINAL", "" } },
|
||||
{ ECutsceneAspectRatio::Unlocked, { "UNLOCKED", "" } },
|
||||
{ ECutsceneAspectRatio::Original, { "ORIGINAL", "Original: locks cutscenes to their original 16:9 aspect ratio." } },
|
||||
{ ECutsceneAspectRatio::Unlocked, { "UNLOCKED", "Unlocked: allows cutscenes to adjust their aspect ratio to the window size.\n\nWARNING: this will introduce visual oddities past the original 16:9 aspect ratio." } },
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
CONFIG_DEFINE_LOCALE(UIScaleMode)
|
||||
CONFIG_DEFINE_LOCALE(UIAlignmentMode)
|
||||
{
|
||||
{ ELanguage::English, { "UI Scale Mode", "Change how the UI scales to the display." } }
|
||||
{ ELanguage::English, { "UI Alignment Mode", "Change how the UI aligns with the display." } }
|
||||
};
|
||||
|
||||
CONFIG_DEFINE_ENUM_LOCALE(EUIScaleMode)
|
||||
CONFIG_DEFINE_ENUM_LOCALE(EUIAlignmentMode)
|
||||
{
|
||||
{
|
||||
ELanguage::English,
|
||||
{
|
||||
{ EUIScaleMode::Edge, { "EDGE", "Edge: the UI will anchor to the edges of the display." } },
|
||||
{ EUIScaleMode::Centre, { "CENTER", "Center: the UI will anchor to the center of the display." } },
|
||||
{ EUIAlignmentMode::Edge, { "EDGE", "Edge: the UI will align with the edges of the display." } },
|
||||
{ EUIAlignmentMode::Centre, { "CENTER", "Center: the UI will align with the center of the display." } },
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -24,7 +24,12 @@ std::unordered_map<std::string, std::unordered_map<ELanguage, std::string>> g_lo
|
||||
{
|
||||
"Options_Header_Name",
|
||||
{
|
||||
{ ELanguage::English, "OPTIONS" }
|
||||
{ ELanguage::English, "OPTIONS" },
|
||||
{ ELanguage::Japanese, "OPTION" },
|
||||
{ ELanguage::German, "OPTIONEN" },
|
||||
{ ELanguage::French, "OPTIONS" },
|
||||
{ ELanguage::Spanish, "OPCIONES" },
|
||||
{ ELanguage::Italian, "OPZIONI" }
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -165,27 +170,27 @@ std::unordered_map<std::string, std::unordered_map<ELanguage, std::string>> g_lo
|
||||
{
|
||||
"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::Italian, "Benvenuto a Unleashed Recompiled!\n\nDovrai avere una copia di\nSonic Unleashed per la Xbox 360\nper proseguire con l'installazione." }
|
||||
{ ELanguage::English, "Welcome to\nUnleashed Recompiled!\n\nYou'll need an Xbox 360 copy\nof Sonic Unleashed in order to proceed with the installation." },
|
||||
{ ELanguage::Italian, "Benvenuto a\nUnleashed Recompiled!\n\nDovrai avere una copia di\nSonic Unleashed per la Xbox 360\nper proseguire con l'installazione." }
|
||||
}
|
||||
},
|
||||
{
|
||||
"Installer_Page_SelectGameAndUpdate",
|
||||
{
|
||||
{ ELanguage::English, "Add the 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",
|
||||
{
|
||||
{ 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",
|
||||
{
|
||||
{ ELanguage::English, "The content will be installed to the program's folder. Please confirm you have enough free space.\n\n" },
|
||||
{ ELanguage::Italian, "Il contenuto verrà installato nella cartella di questo programma. Assicurati di avere abbastanza spazio libero sul tuo hard disk.\n\n" }
|
||||
{ ELanguage::English, "The content will be installed to the program's folder.\n\n" },
|
||||
{ ELanguage::Italian, "Il contenuto verrà installato nella cartella di questo programma.\n\n" }
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -254,6 +259,12 @@ std::unordered_map<std::string, std::unordered_map<ELanguage, std::string>> g_lo
|
||||
{ ELanguage::Italian, "SALTA" }
|
||||
}
|
||||
},
|
||||
{
|
||||
"Installer_Button_Retry",
|
||||
{
|
||||
{ ELanguage::English, "RETRY" }
|
||||
}
|
||||
},
|
||||
{
|
||||
"Installer_Button_AddFiles",
|
||||
{
|
||||
@@ -292,14 +303,14 @@ std::unordered_map<std::string, std::unordered_map<ELanguage, std::string>> g_lo
|
||||
// Notes: message appears when clicking the "Add Files" option for the first time.
|
||||
"Installer_Message_FilePickerTutorial",
|
||||
{
|
||||
{ ELanguage::English, "Select a digital dump.\n\nFor choosing a folder with\nextracted and unmodified\ngame files, use the\n\"Add Folder\" option instead." },
|
||||
{ ELanguage::English, "Select a digital dump with\ncontent from the game.\n\nThese files can be obtained from\nyour Xbox 360 hard drive by\nfollowing the instructions on\nthe GitHub page.\n\nFor choosing a folder with extracted\nand unmodified game files, use\nthe \"Add Folder\" option instead." },
|
||||
}
|
||||
},
|
||||
{
|
||||
// Notes: message appears when clicking the "Add Folder" option for the first time.
|
||||
"Installer_Message_FolderPickerTutorial",
|
||||
{
|
||||
{ ELanguage::English, "Select a folder that contains\nthe unmodified files that have\nbeen extracted from the game.\n\nFor choosing a digital dump,\nuse the\"Add Files\" option instead." },
|
||||
{ ELanguage::English, "Select a folder that contains the\nunmodified files that have been\nextracted from the game.\n\nThese files can be obtained from\nyour Xbox 360 hard drive by\nfollowing the instructions on\nthe GitHub page.\n\nFor choosing a digital dump,\nuse the\"Add Files\" option instead." },
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -334,6 +345,20 @@ std::unordered_map<std::string, std::unordered_map<ELanguage, std::string>> g_lo
|
||||
{ ELanguage::Italian, "Questa opzione riavviera il gioco\nper farti installare qualsiasi DLC\nche non hai installato.\n\nHai già installato tutti i DLC.\n\nVuoi procedere comunque?" }
|
||||
}
|
||||
},
|
||||
{
|
||||
// Notes: message appears when user chooses "Quit" on the first available installation screen.
|
||||
"Installer_Message_Quit",
|
||||
{
|
||||
{ ELanguage::English, "Are you sure you want to quit?" },
|
||||
}
|
||||
},
|
||||
{
|
||||
// Notes: message appears when user chooses "Cancel" during installation.
|
||||
"Installer_Message_Cancel",
|
||||
{
|
||||
{ ELanguage::English, "Are you sure you want to cancel the installation?" },
|
||||
}
|
||||
},
|
||||
{
|
||||
// Notes: message appears when pressing B at the title screen.
|
||||
"Title_Message_Quit",
|
||||
@@ -343,7 +368,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.
|
||||
// Do not localise this unless absolutely necessary, these strings are from the XEX.
|
||||
"Title_Message_SaveDataCorrupt",
|
||||
@@ -356,6 +381,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." }
|
||||
}
|
||||
},
|
||||
{
|
||||
// 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",
|
||||
{
|
||||
@@ -413,6 +466,18 @@ std::unordered_map<std::string, std::unordered_map<ELanguage, std::string>> g_lo
|
||||
{ ELanguage::Italian, "Indietro" }
|
||||
}
|
||||
},
|
||||
{
|
||||
"Common_Quit",
|
||||
{
|
||||
{ ELanguage::English, "Quit" },
|
||||
}
|
||||
},
|
||||
{
|
||||
"Common_Cancel",
|
||||
{
|
||||
{ ELanguage::English, "Cancel" }
|
||||
}
|
||||
},
|
||||
{
|
||||
"Common_Reset",
|
||||
{
|
||||
|
||||
@@ -10,15 +10,23 @@
|
||||
#include <xex.h>
|
||||
#include <apu/audio.h>
|
||||
#include <hid/hid.h>
|
||||
#include <user/achievement_data.h>
|
||||
#include <user/config.h>
|
||||
#include <user/paths.h>
|
||||
#include <user/registry.h>
|
||||
#include <kernel/xdbf.h>
|
||||
#include <install/installer.h>
|
||||
#include <install/update_checker.h>
|
||||
#include <os/logger.h>
|
||||
#include <os/process.h>
|
||||
#include <os/registry.h>
|
||||
#include <ui/game_window.h>
|
||||
#include <ui/installer_wizard.h>
|
||||
#include <mod/mod_loader.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <timeapi.h>
|
||||
#endif
|
||||
|
||||
const size_t XMAIOBegin = 0x7FEA0000;
|
||||
const size_t XMAIOEnd = XMAIOBegin + 0x0000FFFF;
|
||||
|
||||
@@ -145,6 +153,9 @@ int main(int argc, char *argv[])
|
||||
timeBeginPeriod(1);
|
||||
#endif
|
||||
|
||||
if (!os::registry::Init())
|
||||
LOGN_WARNING("OS doesn't support registry");
|
||||
|
||||
os::logger::Init();
|
||||
|
||||
bool forceInstaller = false;
|
||||
@@ -167,6 +178,21 @@ int main(int argc, char *argv[])
|
||||
|
||||
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();
|
||||
|
||||
std::filesystem::path modulePath;
|
||||
@@ -174,24 +200,32 @@ int main(int argc, char *argv[])
|
||||
bool runInstallerWizard = forceInstaller || forceDLCInstaller || !isGameInstalled;
|
||||
if (runInstallerWizard)
|
||||
{
|
||||
Video::CreateHostDevice(sdlVideoDriver);
|
||||
if (!Video::CreateHostDevice(sdlVideoDriver))
|
||||
{
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, GameWindow::GetTitle(), Localise("Video_BackendError").c_str(), GameWindow::s_pWindow);
|
||||
std::_Exit(1);
|
||||
}
|
||||
|
||||
if (!InstallerWizard::Run(GAME_INSTALL_DIRECTORY, isGameInstalled && forceDLCInstaller))
|
||||
{
|
||||
return 1;
|
||||
std::_Exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
ModLoader::Init();
|
||||
|
||||
AchievementData::Load();
|
||||
|
||||
KiSystemStartup();
|
||||
|
||||
uint32_t entry = LdrLoadModule(modulePath);
|
||||
|
||||
if (!runInstallerWizard)
|
||||
Video::CreateHostDevice(sdlVideoDriver);
|
||||
{
|
||||
if (!Video::CreateHostDevice(sdlVideoDriver))
|
||||
{
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, GameWindow::GetTitle(), Localise("Video_BackendError").c_str(), GameWindow::s_pWindow);
|
||||
std::_Exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
Video::StartPipelinePrecompilation();
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <kernel/function.h>
|
||||
#include <kernel/heap.h>
|
||||
#include <user/paths.h>
|
||||
#include <os/process.h>
|
||||
#include <xxHashMap.h>
|
||||
|
||||
enum class ModType
|
||||
@@ -90,7 +91,7 @@ std::vector<std::filesystem::path>* ModLoader::GetIncludeDirectories(size_t modI
|
||||
|
||||
void ModLoader::Init()
|
||||
{
|
||||
std::filesystem::path userPath = GetUserPath();
|
||||
const std::filesystem::path& userPath = GetUserPath();
|
||||
|
||||
IniFile configIni;
|
||||
if (!configIni.read(userPath / "cpkredir.ini"))
|
||||
@@ -110,9 +111,14 @@ void ModLoader::Init()
|
||||
if (!saveFilePathU8.empty())
|
||||
ModLoader::s_saveFilePath = std::u8string_view((const char8_t*)saveFilePathU8.c_str());
|
||||
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", "");
|
||||
if (modsDbIniFilePathU8.empty())
|
||||
return;
|
||||
@@ -187,15 +193,20 @@ void ModLoader::Init()
|
||||
modSaveFilePathU8 = modIni.getString("Main", "SaveFile", std::string());
|
||||
}
|
||||
|
||||
if (!mod.includeDirs.empty())
|
||||
g_mods.emplace_back(std::move(mod));
|
||||
|
||||
if (!modSaveFilePathU8.empty())
|
||||
{
|
||||
std::replace(modSaveFilePathU8.begin(), modSaveFilePathU8.end(), '\\', '/');
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
pid_t pid = fork();
|
||||
@@ -55,3 +60,8 @@ bool os::process::StartProcess(const std::filesystem::path& path, const std::vec
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -4,5 +4,7 @@ namespace os::process
|
||||
{
|
||||
std::filesystem::path GetExecutablePath();
|
||||
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 = {});
|
||||
void ShowConsole();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (path.empty())
|
||||
@@ -46,3 +51,14 @@ bool os::process::StartProcess(const std::filesystem::path& path, const std::vec
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void os::process::ShowConsole()
|
||||
{
|
||||
if (GetConsoleWindow() == nullptr)
|
||||
{
|
||||
AllocConsole();
|
||||
freopen("CONIN$", "r", stdin);
|
||||
freopen("CONOUT$", "w", stderr);
|
||||
freopen("CONOUT$", "w", stdout);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 <patches/ui/CTitleStateIntro_patches.h>
|
||||
#include <patches/CTitleStateIntro_patches.h>
|
||||
|
||||
// SWA::CGameModeStageTitle::Update
|
||||
PPC_FUNC_IMPL(__imp__sub_825518B8);
|
||||
|
||||
+1
-5
@@ -135,7 +135,6 @@ PPC_FUNC(sub_824B0930)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: disable Start button closing menu.
|
||||
if (AchievementMenu::s_isVisible)
|
||||
{
|
||||
// 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))
|
||||
{
|
||||
OptionsMenu::Close();
|
||||
|
||||
GuestToHostFunction<int>(sub_824AFD28, pHudPause, 0, 0, 0, 1);
|
||||
__imp__sub_824B0930(ctx, base);
|
||||
}
|
||||
@@ -163,9 +161,7 @@ PPC_FUNC(sub_824B0930)
|
||||
{
|
||||
g_achievementMenuIntroTime = 0;
|
||||
|
||||
const auto ms_IsRenderHud = (bool*)g_memory.Translate(0x8328BB26);
|
||||
|
||||
if (*ms_IsRenderHud && pHudPause->m_IsShown && pHudPause->m_Transition == SWA::eTransitionType_Undefined)
|
||||
if (*SWA::SGlobals::ms_IsRenderHud && pHudPause->m_IsShown && !pHudPause->m_Submenu && pHudPause->m_Transition == SWA::eTransitionType_Undefined)
|
||||
{
|
||||
ButtonGuide::Open(Button(Localise("Achievements_Name"), EButtonIcon::Back, EButtonAlignment::Left, EFontQuality::Low));
|
||||
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);
|
||||
}
|
||||
}
|
||||
+16
-3
@@ -1,9 +1,12 @@
|
||||
#include <api/SWA.h>
|
||||
#include <cpu/guest_stack_var.h>
|
||||
#include <locale/locale.h>
|
||||
#include <os/logger.h>
|
||||
#include <ui/button_guide.h>
|
||||
#include <ui/fader.h>
|
||||
#include <ui/message_window.h>
|
||||
#include <ui/options_menu.h>
|
||||
#include <user/achievement_manager.h>
|
||||
#include <user/paths.h>
|
||||
#include <app.h>
|
||||
#include <exports.h>
|
||||
@@ -50,12 +53,14 @@ PPC_FUNC_IMPL(__imp__sub_825882B8);
|
||||
PPC_FUNC(sub_825882B8)
|
||||
{
|
||||
auto pTitleState = (SWA::CTitleStateBase*)g_memory.Translate(ctx.r3.u32);
|
||||
auto pGameDocument = SWA::CGameDocument::GetInstance();
|
||||
|
||||
auto pInputState = SWA::CInputState::GetInstance();
|
||||
auto& pPadState = pInputState->GetPadState();
|
||||
auto isAccepted = pPadState.IsTapped(SWA::eKeyState_A) || pPadState.IsTapped(SWA::eKeyState_Start);
|
||||
|
||||
auto pContext = pTitleState->GetContextBase<SWA::CTitleStateBase::CTitleStateContext>();
|
||||
auto isNewGameIndex = pContext->m_pTitleMenu->m_CursorIndex == 0;
|
||||
auto isOptionsIndex = pContext->m_pTitleMenu->m_CursorIndex == 2;
|
||||
auto isInstallIndex = pContext->m_pTitleMenu->m_CursorIndex == 3;
|
||||
|
||||
@@ -63,7 +68,17 @@ PPC_FUNC(sub_825882B8)
|
||||
if (App::s_isSaveDataCorrupt && pContext->m_pTitleMenu->m_CursorIndex == 1)
|
||||
pContext->m_pTitleMenu->m_CursorIndex = 0;
|
||||
|
||||
if (!OptionsMenu::s_isVisible && isOptionsIndex)
|
||||
if (isNewGameIndex && isAccepted)
|
||||
{
|
||||
if (pContext->m_pTitleMenu->m_IsDeleteCheckMessageOpen &&
|
||||
pGameDocument->m_pMember->m_pGeneralWindow->m_SelectedIndex == 1)
|
||||
{
|
||||
LOGN("Resetting achievements...");
|
||||
|
||||
AchievementManager::Reset();
|
||||
}
|
||||
}
|
||||
else if (!OptionsMenu::s_isVisible && isOptionsIndex)
|
||||
{
|
||||
if (OptionsMenu::s_isRestartRequired)
|
||||
{
|
||||
@@ -76,7 +91,6 @@ PPC_FUNC(sub_825882B8)
|
||||
{
|
||||
Game_PlaySound("sys_worldmap_window");
|
||||
Game_PlaySound("sys_worldmap_decide");
|
||||
|
||||
OptionsMenu::Open();
|
||||
}
|
||||
}
|
||||
@@ -93,7 +107,6 @@ PPC_FUNC(sub_825882B8)
|
||||
if (OptionsMenu::CanClose() && pPadState.IsTapped(SWA::eKeyState_B))
|
||||
{
|
||||
Game_PlaySound("sys_worldmap_cansel");
|
||||
|
||||
OptionsMenu::Close();
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,9 @@
|
||||
#include <api/SWA.h>
|
||||
#include <app.h>
|
||||
#include <ui/game_window.h>
|
||||
#include <ui/black_bar.h>
|
||||
#include <gpu/video.h>
|
||||
#include <xxHashMap.h>
|
||||
|
||||
#include "aspect_ratio_patches.h"
|
||||
#include "camera_patches.h"
|
||||
@@ -271,7 +273,7 @@ PPC_FUNC(sub_8258B558)
|
||||
ctx.f1.f64 = offsetX + g_aspectRatioNarrowScale * 140.0f;
|
||||
ctx.f2.f64 = offsetY;
|
||||
|
||||
if (Config::UIScaleMode == EUIScaleMode::Edge && g_aspectRatioNarrowScale >= 1.0f)
|
||||
if (Config::UIAlignmentMode == EUIAlignmentMode::Edge && g_aspectRatioNarrowScale >= 1.0f)
|
||||
ctx.f1.f64 += g_aspectRatioOffsetX / g_aspectRatioScale;
|
||||
|
||||
sub_830BB3D0(ctx, base);
|
||||
@@ -293,7 +295,7 @@ PPC_FUNC(sub_8258B558)
|
||||
if (textBox != NULL)
|
||||
{
|
||||
float value = 708.0f + g_aspectRatioNarrowScale * 140.0f;
|
||||
if (Config::UIScaleMode == EUIScaleMode::Edge && g_aspectRatioNarrowScale >= 1.0f)
|
||||
if (Config::UIAlignmentMode == EUIAlignmentMode::Edge && g_aspectRatioNarrowScale >= 1.0f)
|
||||
value += g_aspectRatioOffsetX / g_aspectRatioScale;
|
||||
|
||||
PPC_STORE_U32(textBox + 0x38, reinterpret_cast<uint32_t&>(value));
|
||||
@@ -335,6 +337,13 @@ enum
|
||||
|
||||
OFFSET_SCALE_LEFT = 1 << 13,
|
||||
OFFSET_SCALE_RIGHT = 1 << 14,
|
||||
|
||||
REPEAT_LEFT = 1 << 15,
|
||||
|
||||
TORNADO_DEFENSE = 1 << 16,
|
||||
|
||||
LOADING_BLACK_BAR_MIN = 1 << 17,
|
||||
LOADING_BLACK_BAR_MAX = 1 << 18,
|
||||
};
|
||||
|
||||
struct CsdModifier
|
||||
@@ -344,7 +353,7 @@ struct CsdModifier
|
||||
uint32_t cornerIndex{};
|
||||
};
|
||||
|
||||
static const ankerl::unordered_dense::map<XXH64_hash_t, CsdModifier> g_modifiers =
|
||||
static const xxHashMap<CsdModifier> g_modifiers =
|
||||
{
|
||||
// ui_balloon
|
||||
{ HashStr("ui_balloon/window/bg"), { STRETCH } },
|
||||
@@ -364,13 +373,16 @@ 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_2"), { ALIGN_BOTTOM_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 } },
|
||||
|
||||
// ui_gate
|
||||
{ HashStr("ui_gate/footer/status_footer"), { ALIGN_BOTTOM } },
|
||||
{ HashStr("ui_gate/header/status_title"), { ALIGN_TOP | OFFSET_SCALE_LEFT, 652.0f } },
|
||||
{ HashStr("ui_gate/header/status_title/title_bg/center"), { ALIGN_TOP | EXTEND_LEFT } },
|
||||
{ HashStr("ui_gate/header/status_title/title_bg/center/h_light"), { ALIGN_TOP | EXTEND_LEFT} },
|
||||
{ HashStr("ui_gate/header/status_title/title_bg/center/h_light"), { ALIGN_TOP | EXTEND_LEFT } },
|
||||
{ HashStr("ui_gate/header/status_title/title_bg/right"), { ALIGN_TOP | STORE_RIGHT_CORNER } },
|
||||
{ HashStr("ui_gate/window/window_bg"), { STRETCH } },
|
||||
|
||||
@@ -383,20 +395,66 @@ static const ankerl::unordered_dense::map<XXH64_hash_t, CsdModifier> g_modifiers
|
||||
{ HashStr("ui_itemresult/main/iresult_title"), { ALIGN_TOP } },
|
||||
{ HashStr("ui_itemresult/main/iresult_title"), { ALIGN_TOP | OFFSET_SCALE_LEFT, 688.0f } },
|
||||
{ HashStr("ui_itemresult/main/iresult_title/title_bg/center"), { ALIGN_TOP | EXTEND_LEFT } },
|
||||
{ HashStr("ui_itemresult/main/iresult_title/title_bg/center/h_light"), { ALIGN_TOP | EXTEND_LEFT} },
|
||||
{ HashStr("ui_itemresult/main/iresult_title/title_bg/center/h_light"), { ALIGN_TOP | EXTEND_LEFT } },
|
||||
{ HashStr("ui_itemresult/main/iresult_title/title_bg/right"), { ALIGN_TOP | STORE_RIGHT_CORNER } },
|
||||
|
||||
// ui_loading
|
||||
{ HashStr("ui_loading/bg_1"), { 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 } },
|
||||
{ HashStr("ui_loading/event_viewer/black/black_top"), { LOADING_BLACK_BAR_MIN } },
|
||||
{ HashStr("ui_loading/event_viewer/black/black_under"), { LOADING_BLACK_BAR_MAX } },
|
||||
{ HashStr("ui_loading/pda/pda_frame/L"), { LOADING_BLACK_BAR_MIN } },
|
||||
{ HashStr("ui_loading/pda/pda_frame/R"), { LOADING_BLACK_BAR_MAX } },
|
||||
|
||||
// ui_mediaroom
|
||||
{ HashStr("ui_mediaroom/header/bg/img_1"), { EXTEND_LEFT } },
|
||||
{ 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
|
||||
{ HashStr("ui_missionscreen/player_count"), { ALIGN_TOP_LEFT | SCALE } },
|
||||
{ HashStr("ui_missionscreen/time_count"), { ALIGN_TOP_LEFT | SCALE } },
|
||||
{ HashStr("ui_missionscreen/score_count"), { ALIGN_TOP_LEFT | SCALE } },
|
||||
{ HashStr("ui_missionscreen/item_count"), { ALIGN_TOP_LEFT | SCALE } },
|
||||
{ HashStr("ui_missionscreen/time_count/position_S/bg_1"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
|
||||
{ HashStr("ui_missionscreen/time_count/position_S/bg_1/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
|
||||
{ HashStr("ui_missionscreen/time_count/position_S/bg_2"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
|
||||
{ HashStr("ui_missionscreen/time_count/position_S/bg_2/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
|
||||
{ HashStr("ui_missionscreen/time_count/position_L/bg_1"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
|
||||
{ HashStr("ui_missionscreen/time_count/position_L/bg_1/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
|
||||
{ HashStr("ui_missionscreen/time_count/position_L/bg_2"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
|
||||
{ HashStr("ui_missionscreen/time_count/position_L/bg_2/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
|
||||
{ HashStr("ui_missionscreen/laptime_count"), { ALIGN_TOP_LEFT | SCALE } },
|
||||
{ HashStr("ui_missionscreen/laptime_count/position_S/bg_1"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
|
||||
{ HashStr("ui_missionscreen/laptime_count/position_S/bg_1/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
|
||||
{ HashStr("ui_missionscreen/laptime_count/position_S/bg_2"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
|
||||
{ HashStr("ui_missionscreen/laptime_count/position_S/bg_2/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
|
||||
{ HashStr("ui_missionscreen/laptime_count/position_L/bg_1"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
|
||||
{ HashStr("ui_missionscreen/laptime_count/position_L/bg_1/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
|
||||
{ HashStr("ui_missionscreen/laptime_count/position_L/bg_2"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
|
||||
{ HashStr("ui_missionscreen/laptime_count/position_L/bg_2/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
|
||||
{ HashStr("ui_missionscreen/score_count"), { ALIGN_TOP_LEFT | SCALE } },
|
||||
{ HashStr("ui_missionscreen/score_count/position_S/bg_1"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
|
||||
{ HashStr("ui_missionscreen/score_count/position_S/bg_1/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
|
||||
{ HashStr("ui_missionscreen/score_count/position_S/bg_2"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
|
||||
{ HashStr("ui_missionscreen/score_count/position_S/bg_2/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
|
||||
{ HashStr("ui_missionscreen/score_count/position_L/bg_1"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
|
||||
{ HashStr("ui_missionscreen/score_count/position_L/bg_1/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
|
||||
{ HashStr("ui_missionscreen/score_count/position_L/bg_2"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
|
||||
{ HashStr("ui_missionscreen/score_count/position_L/bg_2/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
|
||||
{ HashStr("ui_missionscreen/item_count"), { ALIGN_TOP_LEFT | SCALE } },
|
||||
{ HashStr("ui_missionscreen/item_count/position_L/bg_1"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
|
||||
{ HashStr("ui_missionscreen/item_count/position_L/bg_1/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
|
||||
{ HashStr("ui_missionscreen/item_count/position_L/bg_2"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
|
||||
{ HashStr("ui_missionscreen/item_count/position_L/bg_2/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } },
|
||||
{ HashStr("ui_missionscreen/lap_count"), { ALIGN_TOP_RIGHT | SCALE } },
|
||||
{ HashStr("ui_missionscreen/lap_count/position/bar"), { ALIGN_RIGHT | SCALE | EXTEND_RIGHT } },
|
||||
{ HashStr("ui_missionscreen/lap_count/position/bar/R"), { SKIP } },
|
||||
|
||||
// ui_misson
|
||||
{ HashStr("ui_misson/bg"), { STRETCH } },
|
||||
@@ -419,27 +477,67 @@ static const ankerl::unordered_dense::map<XXH64_hash_t, CsdModifier> g_modifiers
|
||||
// ui_playscreen
|
||||
{ HashStr("ui_playscreen/player_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/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/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_ringenagy_gauge"), { 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_get"), { ALIGN_BOTTOM_LEFT | 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/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/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/position/bar"), { ALIGN_BOTTOM_RIGHT | SCALE | EXTEND_RIGHT } },
|
||||
{ HashStr("ui_playscreen/add/medal_get_m/position/bar/R"), { SKIP } },
|
||||
|
||||
// ui_playscreen_ev
|
||||
{ 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/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/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/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/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/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/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/life_bg"), { ALIGN_BOTTOM_LEFT | SCALE } },
|
||||
{ HashStr("ui_playscreen_ev/gauge/unleash_body"), { ALIGN_BOTTOM_LEFT | SCALE } },
|
||||
@@ -468,6 +566,9 @@ static const ankerl::unordered_dense::map<XXH64_hash_t, CsdModifier> g_modifiers
|
||||
|
||||
// ui_playscreen_ev_hit
|
||||
{ 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_txt_1"), { ALIGN_RIGHT | SCALE } },
|
||||
{ HashStr("ui_playscreen_ev_hit/hit_counter_txt_2"), { ALIGN_RIGHT | SCALE } },
|
||||
@@ -479,12 +580,20 @@ static const ankerl::unordered_dense::map<XXH64_hash_t, CsdModifier> g_modifiers
|
||||
{ HashStr("ui_playscreen_su/footer"), { ALIGN_BOTTOM_RIGHT | SCALE } },
|
||||
|
||||
// ui_prov_playscreen
|
||||
{ HashStr("ui_prov_playscreen/so_speed_gauge"), { ALIGN_BOTTOM_LEFT | SCALE } },
|
||||
{ HashStr("ui_prov_playscreen/so_ringenagy_gauge"), { ALIGN_BOTTOM_LEFT | SCALE } },
|
||||
{ HashStr("ui_prov_playscreen/bg"), { ALIGN_TOP_LEFT | SCALE } },
|
||||
{ HashStr("ui_prov_playscreen/info_1"), { ALIGN_TOP_LEFT | SCALE } },
|
||||
{ HashStr("ui_prov_playscreen/info_2"), { ALIGN_TOP_LEFT | SCALE } },
|
||||
{ HashStr("ui_prov_playscreen/ring_get_effect"), { 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 | TORNADO_DEFENSE } },
|
||||
{ HashStr("ui_prov_playscreen/bg"), { ALIGN_TOP_LEFT | SCALE | TORNADO_DEFENSE } },
|
||||
{ HashStr("ui_prov_playscreen/info_1"), { ALIGN_TOP_LEFT | SCALE | TORNADO_DEFENSE } },
|
||||
{ HashStr("ui_prov_playscreen/info_1/position/bg_1"), { ALIGN_TOP_LEFT | SCALE | TORNADO_DEFENSE | EXTEND_LEFT } },
|
||||
{ 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
|
||||
{ HashStr("ui_result/footer/result_footer"), { ALIGN_BOTTOM } },
|
||||
@@ -592,6 +701,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/logo/logo/bg_position/c_1"), { 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/position/center/right"), { STORE_RIGHT_CORNER } },
|
||||
{ HashStr("ui_status/main/progless/prgs/prgs_bar_1"), { OFFSET_SCALE_LEFT, 586.0f } },
|
||||
@@ -623,6 +734,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/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/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_2"), { ALIGN_TOP_LEFT | WORLD_MAP } },
|
||||
{ HashStr("ui_worldmap/contents/info/img/info_img_3"), { ALIGN_TOP_LEFT | WORLD_MAP } },
|
||||
@@ -630,7 +757,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_img_A"), { ALIGN_BOTTOM } },
|
||||
{ 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)
|
||||
@@ -659,39 +791,80 @@ static bool g_cornerExtract;
|
||||
|
||||
//#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
|
||||
PPC_FUNC_IMPL(__imp__sub_830C6A00);
|
||||
PPC_FUNC(sub_830C6A00)
|
||||
{
|
||||
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())
|
||||
{
|
||||
auto r3 = ctx.r3;
|
||||
auto r4 = ctx.r4;
|
||||
auto r5 = ctx.r5;
|
||||
auto r6 = ctx.r6;
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Queue draw calls, but don't actually draw anything. We just want to extract the corner.
|
||||
g_cornerExtract = true;
|
||||
__imp__sub_830C6A00(ctx, base);
|
||||
g_cornerExtract = false;
|
||||
if ((g_sceneModifier->flags & (OFFSET_SCALE_LEFT | OFFSET_SCALE_RIGHT)) != 0)
|
||||
{
|
||||
auto r3 = ctx.r3;
|
||||
auto r4 = ctx.r4;
|
||||
auto r5 = ctx.r5;
|
||||
auto r6 = ctx.r6;
|
||||
|
||||
// Queue draw calls, but don't actually draw anything. We just want to extract the corner.
|
||||
g_cornerExtract = true;
|
||||
__imp__sub_830C6A00(ctx, base);
|
||||
g_cornerExtract = false;
|
||||
|
||||
#ifdef CORNER_DEBUG
|
||||
if (g_sceneModifier->cornerMax == FLT_MAX)
|
||||
{
|
||||
fmt::print("Corners: ");
|
||||
for (auto corner : g_corners)
|
||||
fmt::print("{} ", corner);
|
||||
if (g_sceneModifier->cornerMax == FLT_MAX)
|
||||
{
|
||||
fmt::print("Corners: ");
|
||||
for (auto corner : g_corners)
|
||||
fmt::print("{} ", corner);
|
||||
|
||||
fmt::println("");
|
||||
}
|
||||
fmt::println("");
|
||||
}
|
||||
#endif
|
||||
|
||||
ctx.r3 = r3;
|
||||
ctx.r4 = r4;
|
||||
ctx.r5 = r5;
|
||||
ctx.r6 = r6;
|
||||
ctx.r3 = r3;
|
||||
ctx.r4 = r4;
|
||||
ctx.r5 = r5;
|
||||
ctx.r6 = r6;
|
||||
}
|
||||
}
|
||||
|
||||
__imp__sub_830C6A00(ctx, base);
|
||||
@@ -744,24 +917,34 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str
|
||||
return;
|
||||
}
|
||||
|
||||
if (Config::UIScaleMode == EUIScaleMode::Centre)
|
||||
if (Config::UIAlignmentMode == EUIAlignmentMode::Centre)
|
||||
{
|
||||
if (g_aspectRatio >= WIDE_ASPECT_RATIO)
|
||||
modifier.flags &= ~(ALIGN_LEFT | ALIGN_RIGHT);
|
||||
}
|
||||
|
||||
// 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;
|
||||
ctx.r1.u32 -= size;
|
||||
|
||||
uint8_t* stack = base + ctx.r1.u32;
|
||||
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 offsetY = 0.0f;
|
||||
float pivotX = 0.0f;
|
||||
float pivotY = 0.0f;
|
||||
float scaleX = 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;
|
||||
}
|
||||
@@ -777,11 +960,14 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str
|
||||
if ((modifier.flags & SCALE) != 0)
|
||||
{
|
||||
scaleX *= g_aspectRatioGameplayScale;
|
||||
pivotX = g_scenePositionX;
|
||||
|
||||
if ((modifier.flags & ALIGN_RIGHT) != 0)
|
||||
offsetX += 1280.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale;
|
||||
else if ((modifier.flags & ALIGN_LEFT) == 0)
|
||||
offsetX += 640.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale;
|
||||
|
||||
offsetX += pivotX * g_aspectRatioScale;
|
||||
}
|
||||
|
||||
if ((modifier.flags & WORLD_MAP) != 0)
|
||||
@@ -791,7 +977,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;
|
||||
}
|
||||
@@ -807,11 +993,14 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str
|
||||
if ((modifier.flags & SCALE) != 0)
|
||||
{
|
||||
scaleY *= g_aspectRatioGameplayScale;
|
||||
pivotY = g_scenePositionY;
|
||||
|
||||
if ((modifier.flags & ALIGN_BOTTOM) != 0)
|
||||
offsetY += 720.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale;
|
||||
else if ((modifier.flags & ALIGN_TOP) == 0)
|
||||
offsetY += 360.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale;
|
||||
|
||||
offsetY += pivotY * g_aspectRatioScale;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -825,7 +1014,7 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str
|
||||
offsetScaleModifier = g_castModifier.value();
|
||||
|
||||
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())
|
||||
@@ -853,10 +1042,10 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str
|
||||
|
||||
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 y = offsetY + position[1] * scaleY;
|
||||
float x = offsetX + (position[0] - pivotX) * scaleX;
|
||||
float y = offsetY + (position[1] - pivotY) * scaleY;
|
||||
|
||||
if ((modifier.flags & EXTEND_LEFT) != 0 && (i == 0 || i == 1))
|
||||
{
|
||||
@@ -871,9 +1060,50 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str
|
||||
position[1] = round(y);
|
||||
}
|
||||
|
||||
ctx.r4.u32 = ctx.r1.u32;
|
||||
original(ctx, base);
|
||||
ctx.r1.u32 += size;
|
||||
if ((modifier.flags & LOADING_BLACK_BAR_MIN) != 0)
|
||||
{
|
||||
auto position = getPosition(0);
|
||||
BlackBar::g_loadingBlackBarMin = ImVec2{ position[0], position[1] };
|
||||
BlackBar::g_loadingBlackBarAlpha = *(base + ctx.r1.u32 + 0xB);
|
||||
}
|
||||
else if ((modifier.flags & LOADING_BLACK_BAR_MAX) != 0)
|
||||
{
|
||||
auto position = getPosition(3);
|
||||
BlackBar::g_loadingBlackBarMax = ImVec2{ position[0], position[1] };
|
||||
}
|
||||
|
||||
if ((modifier.flags & REPEAT_LEFT) != 0)
|
||||
{
|
||||
float width = *getPosition(2) - *getPosition(0);
|
||||
|
||||
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;
|
||||
original(ctx, base);
|
||||
ctx.r1.u32 += size;
|
||||
}
|
||||
}
|
||||
|
||||
// SWA::CCsdPlatformMirage::Draw
|
||||
@@ -968,8 +1198,8 @@ PPC_FUNC(sub_830D1EF0)
|
||||
y = g_aspectRatioOffsetY + (y + 0.5f) * g_aspectRatioScale;
|
||||
}
|
||||
|
||||
vertex[i].x = ((x - 0.5f) / Video::s_viewportWidth) * 2.0f - 1.0f;
|
||||
vertex[i].y = ((y - 0.5f) / Video::s_viewportHeight) * -2.0f + 1.0f;
|
||||
vertex[i].x = ((round(x) - 0.5f) / Video::s_viewportWidth) * 2.0f - 1.0f;
|
||||
vertex[i].y = ((round(y) - 0.5f) / Video::s_viewportHeight) * -2.0f + 1.0f;
|
||||
}
|
||||
|
||||
bool letterboxTop = PPC_LOAD_U8(r3.u32 + PRIMITIVE_2D_PADDING_OFFSET + 0x1);
|
||||
@@ -1044,12 +1274,12 @@ static double ComputeObjGetItemX(uint32_t type)
|
||||
|
||||
double scaleOffset = (1280.0 * (1.0 - g_aspectRatioGameplayScale)) * g_aspectRatioScale;
|
||||
|
||||
if (Config::UIScaleMode == EUIScaleMode::Edge)
|
||||
if (Config::UIAlignmentMode == EUIAlignmentMode::Edge)
|
||||
{
|
||||
if (type != 47) // Medal
|
||||
x += g_aspectRatioOffsetX * 2.0 + scaleOffset;
|
||||
}
|
||||
else if (Config::UIScaleMode == EUIScaleMode::Centre)
|
||||
else if (Config::UIAlignmentMode == EUIAlignmentMode::Centre)
|
||||
{
|
||||
x += g_aspectRatioOffsetX + scaleOffset;
|
||||
}
|
||||
@@ -1115,8 +1345,12 @@ PPC_FUNC(sub_826906A8)
|
||||
|
||||
void WorldMapProjectionMidAsmHook(PPCVRegister& v63, PPCVRegister& v62)
|
||||
{
|
||||
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);
|
||||
// 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;
|
||||
v62.f32[2] *= NARROW_ASPECT_RATIO / std::min(NARROW_ASPECT_RATIO, g_aspectRatio);
|
||||
}
|
||||
}
|
||||
|
||||
// CViewRing has the same exact incorrect math as CObjGetItem.
|
||||
@@ -1146,11 +1380,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
|
||||
PPC_FUNC_IMPL(__imp__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);
|
||||
if (shouldDrawLetterbox)
|
||||
@@ -1163,7 +1408,17 @@ PPC_FUNC(sub_82B8AA40)
|
||||
PPC_STORE_U32(ctx.r3.u32 + 0x14, (720 - width * 9 / 16) / 2);
|
||||
}
|
||||
|
||||
auto r3 = ctx.r3;
|
||||
__imp__sub_82B8AA40(ctx, base);
|
||||
|
||||
// Restore the original letterbox value.
|
||||
PPC_STORE_U8(r3.u32, letterbox);
|
||||
|
||||
if (letterbox)
|
||||
{
|
||||
// Would be nice to also push this as a 2D primitive but I really cannot be bothered right now...
|
||||
BlackBar::g_inspirePillarbox = Config::CutsceneAspectRatio != ECutsceneAspectRatio::Unlocked && g_aspectRatio > WIDE_ASPECT_RATIO;
|
||||
}
|
||||
}
|
||||
|
||||
void InspireLetterboxTopMidAsmHook(PPCRegister& r3)
|
||||
@@ -1185,3 +1440,116 @@ void InspireSubtitleMidAsmHook(PPCRegister& r3)
|
||||
|
||||
*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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Explicit CSD set position calls don't seem to care about the
|
||||
// viewport size. This causes them to appear shifted by 1.5x,
|
||||
// as the backbuffer resolution is 640x480 at 4:3. We need to account
|
||||
// for this manually to make the positioning match with the original game.
|
||||
static constexpr uint32_t EVIL_HUD_GUIDE_BYTE_SIZE = 0x154;
|
||||
|
||||
void EvilHudGuideAllocMidAsmHook(PPCRegister& r3)
|
||||
{
|
||||
r3.u32 += sizeof(float);
|
||||
}
|
||||
|
||||
// SWA::Player::CEvilHudGuide::CEvilHudGuide
|
||||
PPC_FUNC_IMPL(__imp__sub_82448CF0);
|
||||
PPC_FUNC(sub_82448CF0)
|
||||
{
|
||||
*reinterpret_cast<float*>(base + ctx.r3.u32 + EVIL_HUD_GUIDE_BYTE_SIZE) = 0.0f;
|
||||
__imp__sub_82448CF0(ctx, base);
|
||||
}
|
||||
|
||||
void EvilHudGuideUpdateMidAsmHook(PPCRegister& r30, PPCRegister& f30)
|
||||
{
|
||||
*reinterpret_cast<float*>(g_memory.base + r30.u32 + EVIL_HUD_GUIDE_BYTE_SIZE) = f30.f64;
|
||||
}
|
||||
|
||||
// SWA::Player::CEvilHudGuide::Update
|
||||
PPC_FUNC_IMPL(__imp__sub_82449088);
|
||||
PPC_FUNC(sub_82449088)
|
||||
{
|
||||
auto r3 = ctx.r3;
|
||||
__imp__sub_82449088(ctx, base);
|
||||
|
||||
float positionX = *reinterpret_cast<float*>(base + r3.u32 + EVIL_HUD_GUIDE_BYTE_SIZE);
|
||||
constexpr uint32_t OFFSETS[] = { 312, 320 };
|
||||
|
||||
for (const auto offset : OFFSETS)
|
||||
{
|
||||
uint32_t scene = PPC_LOAD_U32(r3.u32 + offset + 0x4);
|
||||
if (scene != NULL)
|
||||
{
|
||||
scene = PPC_LOAD_U32(scene + 0x4);
|
||||
if (scene != NULL)
|
||||
{
|
||||
ctx.r3.u32 = scene;
|
||||
ctx.f1.f64 = (1.5 - 0.5 * g_aspectRatioNarrowScale) * positionX;
|
||||
ctx.f2.f64 = 0.0;
|
||||
|
||||
sub_830BB3D0(ctx, base);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,16 +52,44 @@ void AudioPatches::Update(float deltaTime)
|
||||
}
|
||||
else
|
||||
{
|
||||
*pMusicVolume = std::lerp(*pMusicVolume, Config::MusicVolume * Config::MasterVolume, time);
|
||||
*pMusicVolume = std::lerp(*pMusicVolume, Config::MusicVolume, time);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*pMusicVolume = Config::MusicVolume * Config::MasterVolume;
|
||||
*pMusicVolume = Config::MusicVolume;
|
||||
}
|
||||
|
||||
*pEffectsVolume = Config::EffectsVolume * Config::MasterVolume;
|
||||
*pEffectsVolume = Config::EffectsVolume;
|
||||
}
|
||||
|
||||
// Stub volume setter.
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -43,8 +43,25 @@ PPC_FUNC(sub_824697B0)
|
||||
{
|
||||
auto pCamera = (SWA::CCamera*)g_memory.Translate(ctx.r3.u32);
|
||||
|
||||
pCamera->m_InvertX = Config::InvertCameraX;
|
||||
pCamera->m_InvertY = Config::InvertCameraY;
|
||||
pCamera->m_InvertX = Config::HorizontalCamera == ECameraRotationMode::Reverse;
|
||||
pCamera->m_InvertY = Config::VerticalCamera == ECameraRotationMode::Reverse;
|
||||
|
||||
__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);
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ bool LoadingUpdateMidAsmHook(PPCRegister& r31)
|
||||
g_ppcContext->f1.f64 = deltaTime;
|
||||
g_memory.FindFunction(update)(*g_ppcContext, base);
|
||||
|
||||
bool loading = PPC_LOAD_U8(0x83367A4C);
|
||||
bool loading = *SWA::SGlobals::ms_IsLoading;
|
||||
if (loading)
|
||||
{
|
||||
now = std::chrono::steady_clock::now();
|
||||
@@ -115,8 +115,47 @@ PPC_FUNC(sub_8312DBF8)
|
||||
constexpr auto INTERVAL = 1000000000ns / 60;
|
||||
auto next = now + (INTERVAL - now.time_since_epoch() % INTERVAL);
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::floor<std::chrono::milliseconds>(next - now - 1ms));
|
||||
|
||||
while (std::chrono::steady_clock::now() < next)
|
||||
std::this_thread::yield();
|
||||
std::this_thread::sleep_until(next);
|
||||
}
|
||||
|
||||
void WaitVsyncMidAsmHook()
|
||||
{
|
||||
}
|
||||
|
||||
void ApplicationFrameLimiterMidAsmHook()
|
||||
{
|
||||
}
|
||||
|
||||
// Tornado Defense boss increments timers without respecting delta time.
|
||||
// We run the update function with a 30 FPS time step to ensure all timers update at the correct rate.
|
||||
static constexpr size_t EX_STAGE_BOSS_STATE_BATTLE_SIZE = 0x70;
|
||||
|
||||
void CExStageBossCStateBattleAllocMidAsmHook(PPCRegister& r3)
|
||||
{
|
||||
r3.u32 += sizeof(float);
|
||||
}
|
||||
|
||||
void CExStageBossCStateBattleCtorMidAsmHook(PPCRegister& r3)
|
||||
{
|
||||
new (g_memory.base + r3.u32 + EX_STAGE_BOSS_STATE_BATTLE_SIZE) float(0);
|
||||
}
|
||||
|
||||
// SWA::CExStageBoss::CStateBattle::Update
|
||||
PPC_FUNC_IMPL(__imp__sub_82B00D00);
|
||||
PPC_FUNC(sub_82B00D00)
|
||||
{
|
||||
constexpr auto referenceDeltaTime = 1.0f / 30.0f;
|
||||
constexpr auto deltaTimeTolerance = 0.0001f;
|
||||
|
||||
auto pElapsedTime = (float*)(base + ctx.r3.u32 + EX_STAGE_BOSS_STATE_BATTLE_SIZE);
|
||||
|
||||
*pElapsedTime += std::min(App::s_deltaTime, 1.0 / 15.0);
|
||||
|
||||
if ((*pElapsedTime + deltaTimeTolerance) > referenceDeltaTime)
|
||||
{
|
||||
__imp__sub_82B00D00(ctx, base);
|
||||
*pElapsedTime -= referenceDeltaTime;
|
||||
}
|
||||
|
||||
*pElapsedTime = std::max(*pElapsedTime, 0.0f);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
#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:
|
||||
bool OnSDLEvent(SDL_Event* event) override
|
||||
{
|
||||
if (!Config::HUDToggleHotkey || OptionsMenu::s_isVisible)
|
||||
return false;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
g_frontendListener;
|
||||
@@ -1,13 +1,15 @@
|
||||
#include <api/SWA.h>
|
||||
#include <hid/hid.h>
|
||||
#include <ui/sdl_listener.h>
|
||||
#include <app.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:
|
||||
float CancelDeadzone{ 0.31f };
|
||||
float Damping{ 0.99f };
|
||||
float FlickAccelX{ 0.25f };
|
||||
float FlickAccelY{ 0.1f };
|
||||
@@ -18,45 +20,63 @@ public:
|
||||
float Smoothing{ 0.8f };
|
||||
};
|
||||
|
||||
class WorldMapTouchParamsProspero : public WorldMapTouchParams
|
||||
class WorldMapCursorParamsProspero : public WorldMapCursorParams
|
||||
{
|
||||
public:
|
||||
WorldMapTouchParamsProspero()
|
||||
WorldMapCursorParamsProspero()
|
||||
{
|
||||
SensitivityX = 1.15f;
|
||||
SensitivityY = 1.05f;
|
||||
}
|
||||
}
|
||||
g_worldMapTouchParamsProspero;
|
||||
g_worldMapCursorParamsProspero;
|
||||
|
||||
class WorldMapTouchParamsOrbis : public WorldMapTouchParams
|
||||
class WorldMapCursorParamsOrbis : public WorldMapCursorParams
|
||||
{
|
||||
public:
|
||||
WorldMapTouchParamsOrbis()
|
||||
WorldMapCursorParamsOrbis()
|
||||
{
|
||||
SensitivityX = 0.95f;
|
||||
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 float g_worldMapTouchVelocityY;
|
||||
static bool g_isCursorActive;
|
||||
|
||||
static float g_worldMapCursorVelocityX;
|
||||
static float g_worldMapCursorVelocityY;
|
||||
|
||||
class SDLEventListenerForInputPatches : public SDLEventListener
|
||||
{
|
||||
static inline bool ms_isMouseDown;
|
||||
|
||||
static inline int ms_touchpadFingerCount;
|
||||
|
||||
static inline float ms_touchpadX;
|
||||
static inline float ms_touchpadY;
|
||||
static inline float ms_touchpadDeltaX;
|
||||
static inline float ms_touchpadDeltaY;
|
||||
static inline float ms_touchpadPrevX;
|
||||
static inline float ms_touchpadPrevY;
|
||||
static inline float ms_cursorX;
|
||||
static inline float ms_cursorY;
|
||||
static inline float ms_cursorDeltaX;
|
||||
static inline float ms_cursorDeltaY;
|
||||
static inline float ms_cursorPrevX;
|
||||
static inline float ms_cursorPrevY;
|
||||
|
||||
public:
|
||||
static void Update(float deltaTime)
|
||||
@@ -67,89 +87,130 @@ public:
|
||||
all the constants that I had tuned. */
|
||||
constexpr auto referenceDeltaTime = 1.0f / 144.0f;
|
||||
|
||||
if (g_isTouchActive)
|
||||
if (g_isCursorActive)
|
||||
{
|
||||
auto dxNorm = ms_touchpadDeltaX / referenceDeltaTime;
|
||||
auto dyNorm = ms_touchpadDeltaY / referenceDeltaTime;
|
||||
auto dxSens = dxNorm * g_worldMapTouchParams.SensitivityX;
|
||||
auto dySens = dyNorm * g_worldMapTouchParams.SensitivityY;
|
||||
auto dxNorm = ms_cursorDeltaX / referenceDeltaTime;
|
||||
auto dyNorm = ms_cursorDeltaY / referenceDeltaTime;
|
||||
auto dxSens = dxNorm * g_worldMapCursorParams.SensitivityX;
|
||||
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_worldMapTouchVelocityY = smoothing * g_worldMapTouchVelocityY + (1.0f - smoothing) * dySens;
|
||||
g_worldMapCursorVelocityX = smoothing * g_worldMapCursorVelocityX + (1.0f - smoothing) * dxSens;
|
||||
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)
|
||||
{
|
||||
g_worldMapTouchVelocityX += dxNorm * g_worldMapTouchParams.FlickAccelX * (deltaTime / referenceDeltaTime);
|
||||
g_worldMapTouchVelocityY += dyNorm * g_worldMapTouchParams.FlickAccelY * (deltaTime / referenceDeltaTime);
|
||||
g_worldMapCursorVelocityX += dxNorm * g_worldMapCursorParams.FlickAccelX * (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_worldMapTouchVelocityY = std::clamp(g_worldMapTouchVelocityY, -terminalVelocity, terminalVelocity);
|
||||
g_worldMapCursorVelocityX = std::clamp(g_worldMapCursorVelocityX, -terminalVelocity, terminalVelocity);
|
||||
g_worldMapCursorVelocityY = std::clamp(g_worldMapCursorVelocityY, -terminalVelocity, terminalVelocity);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto dampingFactor = powf(g_worldMapTouchParams.Damping, deltaTime / referenceDeltaTime);
|
||||
auto dampingFactor = powf(g_worldMapCursorParams.Damping, deltaTime / referenceDeltaTime);
|
||||
|
||||
g_worldMapTouchVelocityX *= dampingFactor;
|
||||
g_worldMapTouchVelocityY *= dampingFactor;
|
||||
g_worldMapCursorVelocityX *= dampingFactor;
|
||||
g_worldMapCursorVelocityY *= dampingFactor;
|
||||
}
|
||||
}
|
||||
|
||||
void OnSDLEvent(SDL_Event* event) override
|
||||
bool OnSDLEvent(SDL_Event* event) override
|
||||
{
|
||||
if (!hid::IsInputAllowed())
|
||||
return false;
|
||||
|
||||
switch (event->type)
|
||||
{
|
||||
#ifdef UNLEASHED_RECOMP_UI_KBM_SUPPORT
|
||||
case SDL_MOUSEMOTION:
|
||||
{
|
||||
if (!ms_isMouseDown)
|
||||
break;
|
||||
|
||||
g_isCursorActive = true;
|
||||
|
||||
ms_cursorDeltaX = (float)event->motion.xrel / 100.0f;
|
||||
ms_cursorDeltaY = (float)event->motion.yrel / 100.0f;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
{
|
||||
if (event->button.button == SDL_BUTTON_LEFT)
|
||||
{
|
||||
g_worldMapCursorParams = g_worldMapCursorParamsMouse;
|
||||
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_isTouchActive = true;
|
||||
g_isCursorActive = true;
|
||||
|
||||
if (ms_touchpadFingerCount > 1)
|
||||
{
|
||||
g_isTouchActive = false;
|
||||
g_isCursorActive = false;
|
||||
break;
|
||||
}
|
||||
|
||||
ms_touchpadX = event->ctouchpad.x;
|
||||
ms_touchpadY = event->ctouchpad.y;
|
||||
ms_touchpadDeltaX = ms_touchpadX - ms_touchpadPrevX;
|
||||
ms_touchpadDeltaY = ms_touchpadY - ms_touchpadPrevY;
|
||||
ms_touchpadPrevX = ms_touchpadX;
|
||||
ms_touchpadPrevY = ms_touchpadY;
|
||||
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;
|
||||
}
|
||||
|
||||
case SDL_CONTROLLERTOUCHPADDOWN:
|
||||
{
|
||||
g_worldMapTouchParams = hid::g_inputDeviceExplicit == hid::EInputDeviceExplicit::DualSense
|
||||
? (WorldMapTouchParams)g_worldMapTouchParamsProspero
|
||||
: (WorldMapTouchParams)g_worldMapTouchParamsOrbis;
|
||||
g_worldMapCursorParams = hid::g_inputDeviceExplicit == hid::EInputDeviceExplicit::DualSense
|
||||
? (WorldMapCursorParams)g_worldMapCursorParamsProspero
|
||||
: (WorldMapCursorParams)g_worldMapCursorParamsOrbis;
|
||||
|
||||
ms_touchpadFingerCount++;
|
||||
ms_touchpadPrevX = event->ctouchpad.x;
|
||||
ms_touchpadPrevY = event->ctouchpad.y;
|
||||
ms_cursorPrevX = event->ctouchpad.x;
|
||||
ms_cursorPrevY = event->ctouchpad.y;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL_CONTROLLERTOUCHPADUP:
|
||||
g_isTouchActive = false;
|
||||
g_isCursorActive = false;
|
||||
ms_touchpadFingerCount--;
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
g_sdlEventListenerForInputPatches;
|
||||
|
||||
// -------------- COMMON --------------- //
|
||||
|
||||
static bool IsDPadActive(SWA::SPadState* pPadState)
|
||||
static bool IsDPadThreshold(const SWA::SPadState* pPadState)
|
||||
{
|
||||
return pPadState->IsDown(SWA::eKeyState_DpadUp) ||
|
||||
pPadState->IsDown(SWA::eKeyState_DpadDown) ||
|
||||
@@ -157,6 +218,23 @@ static bool IsDPadActive(SWA::SPadState* pPadState)
|
||||
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)
|
||||
{
|
||||
auto pGuestPadState = (SWA::SPadState*)g_memory.Translate(pPadState.u32);
|
||||
@@ -227,71 +305,69 @@ void PostureSpaceHurrierDPadSupportYMidAsmHook(PPCRegister& pPadState, PPCVRegis
|
||||
|
||||
// ------------- WORLD MAP ------------- //
|
||||
|
||||
bool WorldMapTouchSupportMidAsmHook()
|
||||
{
|
||||
SDLEventListenerForInputPatches::Update(App::s_deltaTime);
|
||||
|
||||
auto vxAbs = fabs(g_worldMapTouchVelocityX);
|
||||
auto vyAbs = fabs(g_worldMapTouchVelocityY);
|
||||
|
||||
/* Reduce touch noise if the player has
|
||||
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 fabs(g_worldMapTouchVelocityX) > f0.f64 || fabs(g_worldMapTouchVelocityY) > f0.f64;
|
||||
}
|
||||
|
||||
void TouchAndDPadSupportWorldMapXMidAsmHook(PPCRegister& pPadState, PPCRegister& x)
|
||||
bool WorldMapDeadzoneMidAsmHook(PPCRegister& pPadState)
|
||||
{
|
||||
auto pGuestPadState = (SWA::SPadState*)g_memory.Translate(pPadState.u32);
|
||||
|
||||
if (fabs(pGuestPadState->LeftStickHorizontal) > g_worldMapTouchParams.CancelDeadzone ||
|
||||
fabs(pGuestPadState->LeftStickVertical) > g_worldMapTouchParams.CancelDeadzone)
|
||||
if (IsDPadThreshold(pGuestPadState) || IsLeftStickThreshold(pGuestPadState))
|
||||
{
|
||||
g_worldMapTouchVelocityX = 0;
|
||||
g_worldMapCursorVelocityX = 0;
|
||||
g_worldMapCursorVelocityY = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
SDLEventListenerForInputPatches::Update(App::s_deltaTime);
|
||||
|
||||
/* Reduce noise if the cursor is resting in
|
||||
place, but allow much precise values for
|
||||
proper interpolation to zero. */
|
||||
if (IsCursorThreshold(0.05, true))
|
||||
return !g_isCursorActive;
|
||||
|
||||
return IsCursorThreshold();
|
||||
}
|
||||
|
||||
if (IsDPadActive(pGuestPadState))
|
||||
{
|
||||
g_worldMapTouchVelocityX = 0;
|
||||
return IsDPadThreshold(pGuestPadState) || IsLeftStickThreshold(pGuestPadState, WORLD_MAP_ROTATE_DEADZONE);
|
||||
}
|
||||
|
||||
bool WorldMapMagnetismMidAsmHook(PPCRegister& f0)
|
||||
{
|
||||
if (IsCursorThreshold(f0.f64, true))
|
||||
{
|
||||
g_worldMapCursorVelocityX = 0;
|
||||
g_worldMapCursorVelocityY = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void WorldMapHidSupportXMidAsmHook(PPCRegister& pPadState, PPCRegister& x)
|
||||
{
|
||||
auto pGuestPadState = (SWA::SPadState*)g_memory.Translate(pPadState.u32);
|
||||
|
||||
if (IsDPadThreshold(pGuestPadState))
|
||||
{
|
||||
SetDPadAnalogDirectionX(pPadState, x, false);
|
||||
}
|
||||
else
|
||||
else if (fabs(g_worldMapCursorVelocityX) > 0)
|
||||
{
|
||||
if (fabs(g_worldMapTouchVelocityX) > 0)
|
||||
x.f64 = -g_worldMapTouchVelocityX;
|
||||
x.f64 = -g_worldMapCursorVelocityX;
|
||||
}
|
||||
}
|
||||
|
||||
void TouchAndDPadSupportWorldMapYMidAsmHook(PPCRegister& pPadState, PPCRegister& y)
|
||||
void WorldMapHidSupportYMidAsmHook(PPCRegister& pPadState, PPCRegister& y)
|
||||
{
|
||||
auto pGuestPadState = (SWA::SPadState*)g_memory.Translate(pPadState.u32);
|
||||
|
||||
if (fabs(pGuestPadState->LeftStickHorizontal) > g_worldMapTouchParams.CancelDeadzone ||
|
||||
fabs(pGuestPadState->LeftStickVertical) > g_worldMapTouchParams.CancelDeadzone)
|
||||
if (IsDPadThreshold(pGuestPadState))
|
||||
{
|
||||
g_worldMapTouchVelocityY = 0;
|
||||
}
|
||||
|
||||
if (IsDPadActive(pGuestPadState))
|
||||
{
|
||||
g_worldMapTouchVelocityY = 0;
|
||||
|
||||
SetDPadAnalogDirectionY(pPadState, y, false);
|
||||
}
|
||||
else
|
||||
else if (fabs(g_worldMapCursorVelocityY) > 0)
|
||||
{
|
||||
if (fabs(g_worldMapTouchVelocityY) > 0)
|
||||
y.f64 = g_worldMapTouchVelocityY;
|
||||
y.f64 = g_worldMapCursorVelocityY;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -303,7 +379,7 @@ PPC_FUNC(sub_82486968)
|
||||
|
||||
// Reset vertical velocity if maximum pitch reached.
|
||||
if (fabs(pWorldMapCamera->m_Pitch) >= 80.0f)
|
||||
g_worldMapTouchVelocityY = 0;
|
||||
g_worldMapCursorVelocityY = 0;
|
||||
|
||||
__imp__sub_82486968(ctx, base);
|
||||
}
|
||||
@@ -313,7 +389,7 @@ PPC_FUNC(sub_8256C938)
|
||||
{
|
||||
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)
|
||||
{
|
||||
@@ -339,8 +415,8 @@ PPC_FUNC(sub_8256C938)
|
||||
if (rPadState.IsDown(SWA::eKeyState_DpadRight))
|
||||
pWorldMapCursor->m_LeftStickHorizontal = 1.0f;
|
||||
|
||||
if (sqrtf((pWorldMapCursor->m_LeftStickHorizontal * pWorldMapCursor->m_LeftStickHorizontal) +
|
||||
(pWorldMapCursor->m_LeftStickVertical * pWorldMapCursor->m_LeftStickVertical)) > 0.7f)
|
||||
if (sqrtl((pWorldMapCursor->m_LeftStickHorizontal * pWorldMapCursor->m_LeftStickHorizontal) +
|
||||
(pWorldMapCursor->m_LeftStickVertical * pWorldMapCursor->m_LeftStickVertical)) > WORLD_MAP_ROTATE_DEADZONE)
|
||||
{
|
||||
pWorldMapCursor->m_IsCursorMoving = true;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#include "inspire_patches.h"
|
||||
#include <api/SWA.h>
|
||||
#include <ui/game_window.h>
|
||||
#include <ui/window_events.h>
|
||||
#include <os/logger.h>
|
||||
#include <app.h>
|
||||
#include <sdl_events.h>
|
||||
|
||||
static SWA::Inspire::CScene* g_pScene;
|
||||
static std::string g_sceneName;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#include <api/SWA.h>
|
||||
#include <ui/game_window.h>
|
||||
#include <user/achievement_data.h>
|
||||
#include <user/achievement_manager.h>
|
||||
#include <user/config.h>
|
||||
|
||||
void AchievementManagerUnlockMidAsmHook(PPCRegister& id)
|
||||
{
|
||||
AchievementData::Unlock(id.u32);
|
||||
AchievementManager::Unlock(id.u32);
|
||||
}
|
||||
|
||||
bool DisableHintsMidAsmHook()
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <user/config.h>
|
||||
#include <SWA/CharacterUtility/CharacterProxy.h>
|
||||
#include <hid/hid.h>
|
||||
|
||||
// CObjFlame::CObjFlame
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#include <api/SWA.h>
|
||||
#include <ui/game_window.h>
|
||||
#include <ui/window_events.h>
|
||||
#include <user/config.h>
|
||||
#include <os/logger.h>
|
||||
#include <app.h>
|
||||
#include <sdl_events.h>
|
||||
|
||||
static uint32_t g_lastEnemyScore;
|
||||
static uint32_t g_lastTrickScore;
|
||||
@@ -50,11 +50,8 @@ PPC_FUNC(sub_8245F048)
|
||||
|
||||
void ResetScoreOnRestartMidAsmHook()
|
||||
{
|
||||
if (auto pGameDocument = SWA::CGameDocument::GetInstance())
|
||||
{
|
||||
pGameDocument->m_pMember->m_ScoreInfo.EnemyScore = 0;
|
||||
pGameDocument->m_pMember->m_ScoreInfo.TrickScore = 0;
|
||||
}
|
||||
g_lastEnemyScore = 0;
|
||||
g_lastTrickScore = 0;
|
||||
}
|
||||
|
||||
// Dark Gaia energy change hook.
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#include <user/achievement_data.h>
|
||||
#include <user/config.h>
|
||||
#include <api/SWA.h>
|
||||
#include <os/logger.h>
|
||||
#include <hid/hid.h>
|
||||
#include <os/logger.h>
|
||||
#include <user/achievement_manager.h>
|
||||
#include <user/config.h>
|
||||
#include <app.h>
|
||||
|
||||
bool m_isSavedAchievementData = false;
|
||||
@@ -11,13 +11,17 @@ bool m_isSavedAchievementData = false;
|
||||
PPC_FUNC_IMPL(__imp__sub_824DCF38);
|
||||
PPC_FUNC(sub_824DCF38)
|
||||
{
|
||||
auto pLoading = (SWA::CLoading*)g_memory.Translate(ctx.r3.u32);
|
||||
|
||||
App::s_isLoading = true;
|
||||
|
||||
// TODO: use the actual PS3 loading screen ("n_2_d").
|
||||
if (Config::TimeOfDayTransition == ETimeOfDayTransition::PlayStation)
|
||||
{
|
||||
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())
|
||||
@@ -36,6 +40,26 @@ PPC_FUNC(sub_824DCF38)
|
||||
__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
|
||||
PPC_FUNC_IMPL(__imp__sub_824DAB60);
|
||||
PPC_FUNC(sub_824DAB60)
|
||||
@@ -73,9 +97,7 @@ PPC_FUNC(sub_824E5170)
|
||||
|
||||
if (!m_isSavedAchievementData)
|
||||
{
|
||||
LOGN("Saving achievements...");
|
||||
|
||||
AchievementData::Save();
|
||||
AchievementManager::Save();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -68,3 +68,48 @@ bool MotionBlurMidAsmHook()
|
||||
{
|
||||
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,3 +1,4 @@
|
||||
![Ww][Ii][Nn]32/
|
||||
*.c
|
||||
*.h
|
||||
*.h
|
||||
!credits.h
|
||||
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
inline std::array<const char*, 14> g_credits =
|
||||
{
|
||||
"Skyth",
|
||||
"Hyper",
|
||||
"Darío",
|
||||
"Sajid",
|
||||
"DeaThProj",
|
||||
"RadiantDerg",
|
||||
"PTKay",
|
||||
"SuperSonic16",
|
||||
"NextinHKRY",
|
||||
"saguinee",
|
||||
"LadyLunanova",
|
||||
"LJSTAR",
|
||||
"Goalringmod27",
|
||||
"M&M"
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
VERSION_MILESTONE="Beta 1"
|
||||
VERSION_MILESTONE="Beta 2"
|
||||
VERSION_MAJOR=1
|
||||
VERSION_MINOR=0
|
||||
VERSION_REVISION=0
|
||||
|
||||
@@ -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
|
||||
|
||||
#include <SDL.h>
|
||||
#include "ui/game_window.h"
|
||||
#include <ui/game_window.h>
|
||||
|
||||
#define SDL_USER_EVILSONIC (SDL_USEREVENT + 1)
|
||||
|
||||
@@ -4,7 +4,7 @@ class ISDLEventListener
|
||||
{
|
||||
public:
|
||||
virtual ~ISDLEventListener() = default;
|
||||
virtual void OnSDLEvent(SDL_Event* event) = 0;
|
||||
virtual bool OnSDLEvent(SDL_Event* event) = 0;
|
||||
};
|
||||
|
||||
extern std::vector<ISDLEventListener*>& GetEventListeners();
|
||||
@@ -17,5 +17,5 @@ public:
|
||||
GetEventListeners().emplace_back(this);
|
||||
}
|
||||
|
||||
void OnSDLEvent(SDL_Event* event) override {}
|
||||
bool OnSDLEvent(SDL_Event* event) override { return false; }
|
||||
};
|
||||
@@ -1,20 +1,20 @@
|
||||
#include "achievement_menu.h"
|
||||
#include "imgui_utils.h"
|
||||
#include <api/SWA.h>
|
||||
#include <gpu/imgui/imgui_snapshot.h>
|
||||
#include <gpu/video.h>
|
||||
#include <hid/hid.h>
|
||||
#include <kernel/xdbf.h>
|
||||
#include <locale/locale.h>
|
||||
#include <patches/aspect_ratio_patches.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 <app.h>
|
||||
#include <exports.h>
|
||||
#include <decompressor.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_END = 15;
|
||||
@@ -44,8 +44,6 @@ static ImFont* g_fntNewRodinDB;
|
||||
static ImFont* g_fntNewRodinUB;
|
||||
|
||||
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_selectedRowIndex;
|
||||
@@ -71,43 +69,11 @@ static void DrawContainer(ImVec2 min, ImVec2 max, ImU32 gradientTop, ImU32 gradi
|
||||
{
|
||||
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) });
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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 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));
|
||||
|
||||
@@ -231,7 +197,7 @@ static void DrawAchievement(int rowIndex, float yOffset, Achievement& achievemen
|
||||
ImVec2 marqueeMin = { textMarqueeX, min.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))
|
||||
{
|
||||
@@ -276,7 +242,7 @@ static void DrawAchievement(int rowIndex, float yOffset, Achievement& achievemen
|
||||
if (!isUnlocked)
|
||||
return;
|
||||
|
||||
auto timestamp = AchievementData::GetTimestamp(achievement.ID);
|
||||
auto timestamp = AchievementManager::GetTimestamp(achievement.ID);
|
||||
|
||||
if (!timestamp)
|
||||
return;
|
||||
@@ -485,7 +451,7 @@ static void DrawAchievementTotal(ImVec2 min, ImVec2 max)
|
||||
auto uv1 = ImVec2((columnIndex + 1) * spriteSize / textureWidth, (rowIndex + 1) * spriteSize / textureHeight);
|
||||
|
||||
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 colSilver = ImGui::ColorConvertU32ToFloat4(IM_COL32(220, 220, 220, 255 * alpha));
|
||||
@@ -580,11 +546,13 @@ static void DrawContentContainer()
|
||||
|
||||
if (motion < 1.0f)
|
||||
{
|
||||
drawList->PopClipRect();
|
||||
return;
|
||||
}
|
||||
else if (g_isClosing)
|
||||
{
|
||||
AchievementMenu::s_isVisible = false;
|
||||
drawList->PopClipRect();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -613,7 +581,7 @@ static void DrawContentContainer()
|
||||
{
|
||||
auto achievement = std::get<0>(tpl);
|
||||
|
||||
if (AchievementData::IsUnlocked(achievement.ID))
|
||||
if (AchievementManager::IsUnlocked(achievement.ID))
|
||||
DrawAchievement(rowCount++, yOffset, achievement, true);
|
||||
}
|
||||
|
||||
@@ -621,7 +589,7 @@ static void DrawContentContainer()
|
||||
{
|
||||
auto achievement = std::get<0>(tpl);
|
||||
|
||||
if (!AchievementData::IsUnlocked(achievement.ID))
|
||||
if (!AchievementManager::IsUnlocked(achievement.ID))
|
||||
DrawAchievement(rowCount++, yOffset, achievement, false);
|
||||
}
|
||||
|
||||
@@ -778,8 +746,6 @@ void AchievementMenu::Init()
|
||||
g_fntNewRodinUB = ImFontAtlasSnapshot::GetFont("FOT-NewRodinPro-UB.otf");
|
||||
|
||||
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()
|
||||
@@ -804,7 +770,7 @@ void AchievementMenu::Open()
|
||||
if (Config::Language == ELanguage::English)
|
||||
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)
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
#include "achievement_overlay.h"
|
||||
#include "imgui_utils.h"
|
||||
#include <gpu/imgui/imgui_snapshot.h>
|
||||
#include <gpu/video.h>
|
||||
#include <kernel/memory.h>
|
||||
#include <kernel/xdbf.h>
|
||||
#include <locale/locale.h>
|
||||
#include <user/config.h>
|
||||
#include <ui/imgui_utils.h>
|
||||
#include <user/achievement_data.h>
|
||||
#include <user/config.h>
|
||||
#include <app.h>
|
||||
#include <exports.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_END = 11;
|
||||
@@ -29,8 +28,6 @@ static Achievement g_achievement;
|
||||
|
||||
static ImFont* g_fntSeurat;
|
||||
|
||||
static std::unique_ptr<GuestTexture> g_upWindow;
|
||||
|
||||
static bool DrawContainer(ImVec2 min, ImVec2 max, float cornerRadius = 25)
|
||||
{
|
||||
auto drawList = ImGui::GetForegroundDrawList();
|
||||
@@ -65,11 +62,15 @@ static bool DrawContainer(ImVec2 min, ImVec2 max, float cornerRadius = 25)
|
||||
? Hermite(1, 0, colourMotion)
|
||||
: Hermite(0, 1, colourMotion);
|
||||
|
||||
DrawPauseContainer(g_upWindow.get(), min, max, alpha);
|
||||
DrawPauseContainer(min, max, alpha);
|
||||
|
||||
drawList->PushClipRect(min, max);
|
||||
if (containerMotion >= 1.0f)
|
||||
{
|
||||
drawList->PushClipRect(min, max);
|
||||
return true;
|
||||
}
|
||||
|
||||
return containerMotion >= 1.0f;
|
||||
return false;
|
||||
}
|
||||
|
||||
void AchievementOverlay::Init()
|
||||
@@ -77,8 +78,6 @@ void AchievementOverlay::Init()
|
||||
auto& io = ImGui::GetIO();
|
||||
|
||||
g_fntSeurat = ImFontAtlasSnapshot::GetFont("FOT-SeuratPro-M.otf");
|
||||
|
||||
g_upWindow = LOAD_ZSTD_TEXTURE(g_general_window);
|
||||
}
|
||||
|
||||
void AchievementOverlay::Draw()
|
||||
@@ -117,49 +116,50 @@ void AchievementOverlay::Draw()
|
||||
|
||||
if (DrawContainer(min, max))
|
||||
{
|
||||
if (g_isClosing)
|
||||
if (!g_isClosing)
|
||||
{
|
||||
// Draw achievement icon.
|
||||
drawList->AddImage
|
||||
(
|
||||
g_xdbfTextureCache[g_achievement.ID], // user_texture_id
|
||||
{ /* X */ min.x + imageMarginX, /* Y */ min.y + imageMarginY }, // p_min
|
||||
{ /* X */ min.x + imageMarginX + imageSize, /* Y */ min.y + imageMarginY + imageSize }, // p_max
|
||||
{ 0, 0 }, // uv_min
|
||||
{ 1, 1 }, // uv_max
|
||||
IM_COL32(255, 255, 255, 255) // col
|
||||
);
|
||||
|
||||
// Draw header text.
|
||||
DrawTextWithShadow
|
||||
(
|
||||
g_fntSeurat, // font
|
||||
fontSize, // fontSize
|
||||
{ /* X */ min.x + textMarginX + (maxSize - headerSize.x) / 2, /* Y */ min.y + textMarginY }, // pos
|
||||
IM_COL32(252, 243, 5, 255), // colour
|
||||
strAchievementUnlocked, // text
|
||||
2, // offset
|
||||
1.0f, // radius
|
||||
IM_COL32(0, 0, 0, 255) // shadowColour
|
||||
);
|
||||
|
||||
// Draw achievement name.
|
||||
DrawTextWithShadow
|
||||
(
|
||||
g_fntSeurat, // font
|
||||
fontSize, // fontSize
|
||||
{ /* X */ min.x + textMarginX + (maxSize - bodySize.x) / 2, /* Y */ min.y + textMarginY + bodySize.y + Scale(6) }, // pos
|
||||
IM_COL32(255, 255, 255, 255), // colour
|
||||
strAchievementName, // text
|
||||
2, // offset
|
||||
1.0f, // radius
|
||||
IM_COL32(0, 0, 0, 255) // shadowColour
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
s_isVisible = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Draw achievement icon.
|
||||
drawList->AddImage
|
||||
(
|
||||
g_xdbfTextureCache[g_achievement.ID], // user_texture_id
|
||||
{ /* X */ min.x + imageMarginX, /* Y */ min.y + imageMarginY }, // p_min
|
||||
{ /* X */ min.x + imageMarginX + imageSize, /* Y */ min.y + imageMarginY + imageSize }, // p_max
|
||||
{ 0, 0 }, // uv_min
|
||||
{ 1, 1 }, // uv_max
|
||||
IM_COL32(255, 255, 255, 255) // col
|
||||
);
|
||||
|
||||
// Draw header text.
|
||||
DrawTextWithShadow
|
||||
(
|
||||
g_fntSeurat, // font
|
||||
fontSize, // fontSize
|
||||
{ /* X */ min.x + textMarginX + (maxSize - headerSize.x) / 2, /* Y */ min.y + textMarginY }, // pos
|
||||
IM_COL32(252, 243, 5, 255), // colour
|
||||
strAchievementUnlocked, // text
|
||||
2, // offset
|
||||
1.0f, // radius
|
||||
IM_COL32(0, 0, 0, 255) // shadowColour
|
||||
);
|
||||
|
||||
// Draw achievement name.
|
||||
DrawTextWithShadow
|
||||
(
|
||||
g_fntSeurat, // font
|
||||
fontSize, // fontSize
|
||||
{ /* X */ min.x + textMarginX + (maxSize - bodySize.x) / 2, /* Y */ min.y + textMarginY + bodySize.y + Scale(6) }, // pos
|
||||
IM_COL32(255, 255, 255, 255), // colour
|
||||
strAchievementName, // text
|
||||
2, // offset
|
||||
1.0f, // radius
|
||||
IM_COL32(0, 0, 0, 255) // shadowColour
|
||||
);
|
||||
|
||||
// Pop clip rect from DrawContainer.
|
||||
drawList->PopClipRect();
|
||||
}
|
||||
@@ -167,7 +167,7 @@ void AchievementOverlay::Draw()
|
||||
|
||||
void AchievementOverlay::Open(int id)
|
||||
{
|
||||
if (s_isVisible)
|
||||
if (s_isVisible && !g_isClosing)
|
||||
{
|
||||
s_queue.emplace(id);
|
||||
return;
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
#include "black_bar.h"
|
||||
#include <patches/aspect_ratio_patches.h>
|
||||
|
||||
void BlackBar::Draw()
|
||||
{
|
||||
if (g_inspirePillarbox)
|
||||
{
|
||||
auto drawList = ImGui::GetBackgroundDrawList();
|
||||
auto& res = ImGui::GetIO().DisplaySize;
|
||||
|
||||
float width = (res.x - (res.y * 16.0f / 9.0f)) / 2.0f;
|
||||
|
||||
drawList->AddRectFilled(
|
||||
{ 0.0f, 0.0f },
|
||||
{ width, res.y },
|
||||
IM_COL32_BLACK);
|
||||
|
||||
drawList->AddRectFilled(
|
||||
{ res.x - width, 0.0f },
|
||||
res,
|
||||
IM_COL32_BLACK);
|
||||
|
||||
g_inspirePillarbox = false;
|
||||
}
|
||||
|
||||
if (g_loadingBlackBarAlpha != 0)
|
||||
{
|
||||
auto drawList = ImGui::GetBackgroundDrawList();
|
||||
auto& res = ImGui::GetIO().DisplaySize;
|
||||
|
||||
if (g_aspectRatio > WIDE_ASPECT_RATIO)
|
||||
{
|
||||
drawList->AddRectFilled(
|
||||
{ 0.0f, 0.0f },
|
||||
{ g_loadingBlackBarMin.x, g_loadingBlackBarMax.y },
|
||||
IM_COL32(0, 0, 0, g_loadingBlackBarAlpha));
|
||||
|
||||
drawList->AddRectFilled(
|
||||
{ g_loadingBlackBarMax.x, g_loadingBlackBarMin.y },
|
||||
res,
|
||||
IM_COL32(0, 0, 0, g_loadingBlackBarAlpha));
|
||||
}
|
||||
else if (g_aspectRatio < NARROW_ASPECT_RATIO)
|
||||
{
|
||||
drawList->AddRectFilled(
|
||||
{ 0.0f, 0.0f },
|
||||
{ g_loadingBlackBarMax.x, g_loadingBlackBarMin.y },
|
||||
IM_COL32(0, 0, 0, g_loadingBlackBarAlpha));
|
||||
|
||||
drawList->AddRectFilled(
|
||||
{ g_loadingBlackBarMin.x, g_loadingBlackBarMax.y },
|
||||
res,
|
||||
IM_COL32(0, 0, 0, g_loadingBlackBarAlpha));
|
||||
}
|
||||
|
||||
g_loadingBlackBarAlpha = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
struct BlackBar
|
||||
{
|
||||
static inline bool g_inspirePillarbox;
|
||||
|
||||
static inline ImVec2 g_loadingBlackBarMin;
|
||||
static inline ImVec2 g_loadingBlackBarMax;
|
||||
static inline uint8_t g_loadingBlackBarAlpha;
|
||||
|
||||
static void Draw();
|
||||
};
|
||||
@@ -3,9 +3,11 @@
|
||||
#include <gpu/imgui/imgui_snapshot.h>
|
||||
#include <gpu/video.h>
|
||||
#include <hid/hid.h>
|
||||
#include <patches/aspect_ratio_patches.h>
|
||||
#include <user/config.h>
|
||||
#include <app.h>
|
||||
#include <decompressor.h>
|
||||
|
||||
#include <res/images/common/controller.dds.h>
|
||||
#include <res/images/common/kbm.dds.h>
|
||||
|
||||
@@ -36,7 +38,8 @@ std::unordered_map<EButtonIcon, float> g_iconWidths =
|
||||
{ EButtonIcon::Start, 40 },
|
||||
{ EButtonIcon::Back, 40 },
|
||||
{ EButtonIcon::LMB, 40 },
|
||||
{ EButtonIcon::Enter, 40 }
|
||||
{ EButtonIcon::Enter, 40 },
|
||||
{ EButtonIcon::Escape, 40 },
|
||||
};
|
||||
|
||||
std::unordered_map<EButtonIcon, float> g_iconHeights =
|
||||
@@ -54,7 +57,8 @@ std::unordered_map<EButtonIcon, float> g_iconHeights =
|
||||
{ EButtonIcon::Start, 40 },
|
||||
{ EButtonIcon::Back, 40 },
|
||||
{ EButtonIcon::LMB, 40 },
|
||||
{ EButtonIcon::Enter, 40 }
|
||||
{ EButtonIcon::Enter, 40 },
|
||||
{ EButtonIcon::Escape, 40 },
|
||||
};
|
||||
|
||||
std::tuple<std::tuple<ImVec2, ImVec2>, GuestTexture*> GetButtonIcon(EButtonIcon icon)
|
||||
@@ -122,12 +126,17 @@ std::tuple<std::tuple<ImVec2, ImVec2>, GuestTexture*> GetButtonIcon(EButtonIcon
|
||||
break;
|
||||
|
||||
case EButtonIcon::LMB:
|
||||
btn = PIXELS_TO_UV_COORDS(256, 128, 0, 0, 128, 128);
|
||||
btn = PIXELS_TO_UV_COORDS(384, 128, 0, 0, 128, 128);
|
||||
texture = g_upKBMIcons.get();
|
||||
break;
|
||||
|
||||
case EButtonIcon::Enter:
|
||||
btn = PIXELS_TO_UV_COORDS(256, 128, 128, 0, 128, 128);
|
||||
btn = PIXELS_TO_UV_COORDS(384, 128, 128, 0, 128, 128);
|
||||
texture = g_upKBMIcons.get();
|
||||
break;
|
||||
|
||||
case EButtonIcon::Escape:
|
||||
btn = PIXELS_TO_UV_COORDS(384, 128, 256, 0, 128, 128);
|
||||
texture = g_upKBMIcons.get();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -20,7 +20,8 @@ enum class EButtonIcon
|
||||
|
||||
// Keyboard + Mouse (temporary)
|
||||
LMB,
|
||||
Enter
|
||||
Enter,
|
||||
Escape
|
||||
};
|
||||
|
||||
enum class EButtonAlignment
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
#include <os/logger.h>
|
||||
#include <os/user.h>
|
||||
#include <os/version.h>
|
||||
#include <ui/sdl_listener.h>
|
||||
#include <app.h>
|
||||
#include <sdl_listener.h>
|
||||
#include <SDL_syswm.h>
|
||||
|
||||
#if _WIN32
|
||||
#include <dwmapi.h>
|
||||
#pragma comment(lib, "dwmapi.lib")
|
||||
#include <shellscalingapi.h>
|
||||
#endif
|
||||
|
||||
#include <res/images/game_icon.bmp.h>
|
||||
@@ -24,7 +24,12 @@ int Window_OnSDLEvent(void*, SDL_Event* event)
|
||||
ImGui_ImplSDL2_ProcessEvent(event);
|
||||
|
||||
for (auto listener : GetEventListeners())
|
||||
listener->OnSDLEvent(event);
|
||||
{
|
||||
if (listener->OnSDLEvent(event))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
switch (event->type)
|
||||
{
|
||||
@@ -141,20 +146,6 @@ int Window_OnSDLEvent(void*, SDL_Event* event)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!GameWindow::IsFullscreen())
|
||||
{
|
||||
if (event->type == SDL_CONTROLLERBUTTONDOWN || event->type == SDL_CONTROLLERBUTTONUP || event->type == SDL_CONTROLLERAXISMOTION)
|
||||
{
|
||||
// Hide mouse cursor when controller input is detected.
|
||||
SDL_ShowCursor(SDL_DISABLE);
|
||||
}
|
||||
else if (event->type == SDL_MOUSEMOTION)
|
||||
{
|
||||
// Restore mouse cursor when mouse input is detected.
|
||||
SDL_ShowCursor(SDL_ENABLE);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -179,7 +170,7 @@ void GameWindow::Init(const char* sdlVideoDriver)
|
||||
SDL_AddEventWatch(Window_OnSDLEvent, s_pWindow);
|
||||
|
||||
#ifdef _WIN32
|
||||
SetProcessDPIAware();
|
||||
SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
|
||||
#endif
|
||||
|
||||
Config::WindowSize.LockCallback = [](ConfigDef<int32_t>* def)
|
||||
@@ -231,6 +222,12 @@ void GameWindow::Init(const char* sdlVideoDriver)
|
||||
|
||||
#if defined(_WIN32)
|
||||
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)
|
||||
s_renderWindow = s_pWindow;
|
||||
#elif defined(__linux__)
|
||||
@@ -431,7 +428,7 @@ void GameWindow::ResetDimensions()
|
||||
|
||||
uint32_t GameWindow::GetWindowFlags()
|
||||
{
|
||||
uint32_t flags = SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE;
|
||||
uint32_t flags = SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI;
|
||||
|
||||
if (Config::WindowState == EWindowState::Maximised)
|
||||
flags |= SDL_WINDOW_MAXIMIZED;
|
||||
@@ -469,6 +466,9 @@ void GameWindow::SetDisplay(int displayIndex)
|
||||
if (!IsFullscreen())
|
||||
return;
|
||||
|
||||
if (GetDisplay() == displayIndex)
|
||||
return;
|
||||
|
||||
s_isChangingDisplay = true;
|
||||
|
||||
SDL_Rect bounds;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user