34 Commits

Author SHA1 Message Date
Skyth
17d7b97dd6 Update version milestone to Beta 1. 2025-01-23 23:20:28 +03:00
Hyper
79b966586d version.cmake: don't show Git info or build type for Release 2025-01-23 20:00:48 +00:00
Hyper
686ef22c4d Implemented D-Pad support for the World Map, Gaia Colossus, Super Sonic and the Bobsleigh (#150)
* Implemented D-Pad support for World Map, Super Sonic (WIP) and Bobsleigh

* Implemented D-Pad support for Gaia Colossus and Super Sonic

* Improved touchpad sensitivity
2025-01-23 17:43:08 +00:00
Skyth (Asilkan)
ca6b42e20e Interpolate barrels in Spagonia Night to fix softlock at high frame rates. (#153) 2025-01-23 19:41:15 +03:00
Skyth (Asilkan)
372c04fedd Implement ImGui additive rendering. (#152) 2025-01-23 16:25:31 +03:00
Skyth (Asilkan)
b4de79720c Remove lzcnt instruction usage. (#151) 2025-01-23 15:18:19 +03:00
Hyper
e10eb80e38 config: move Voice Language and Subtitles to System 2025-01-22 23:50:10 +00:00
Skyth (Asilkan)
4e149b0640 Implement proper post process scaling based on aspect ratio. (#148)
* Fix post process scaling for narrow aspect ratios.

* Compare width to pick gaussian blur quality for narrow aspect ratios.

* Account for world map disabling VERT+ for post process scaling.
2025-01-22 19:14:40 +03:00
Skyth (Asilkan)
85b5ebf9a7 Fix pause menu selection cast being visible while exiting options menu. (#139) 2025-01-22 19:13:53 +03:00
Skyth (Asilkan)
bdbdd42bd0 Implement config option to toggle surround sound. (#144) 2025-01-22 18:51:51 +03:00
Skyth (Asilkan)
fbc4bfa4d6 Fix save files not getting redirected without include directories. (#146) 2025-01-22 17:01:37 +03:00
Skyth
010c0a9fdf Use clamp texture addressing by default. 2025-01-22 15:49:30 +03:00
João Moura
e6251bdbc3 Add GitHub Actions workflow files for Flatpak and Windows (#142)
Co-authored-by: SuperSonic16 <25761885+thesupersonic16@users.noreply.github.com>
Co-authored-by: Dario <dariosamo@gmail.com>
2025-01-22 14:29:02 +03:00
Skyth (Asilkan)
fefd177b3a Replace indirect guest function calls with direct ones. (#141) 2025-01-20 18:18:20 +03:00
Skyth
e8d36998a5 Fix gameplay UI scaling at narrow aspect ratios. 2025-01-20 02:08:43 +03:00
Skyth (Asilkan)
1efb4943b3 Replace uses of SWA with UnleashedRecomp & rebrand the recompilers. (#138) 2025-01-19 23:21:05 +03:00
Skyth (Asilkan)
312f913a92 Remove detail namespaces. (#137) 2025-01-19 21:09:47 +03:00
NextinHKRY
d10f4bac3c Change Xbox Color Correction option to have 1 image (#136)
* Options: Color Correction with 1 thumbnail

* Update UnleashedRecompResources

* Update UnleashedRecompResources
2025-01-19 20:56:51 +03:00
Skyth (Asilkan)
535bca7d9f Move config implementations to a .cpp file. (#133) 2025-01-19 19:05:44 +03:00
Hyper
614106c868 input_patches: add missing hook functions 2025-01-19 15:58:02 +00:00
Hyper
fde108178d CTitleStateIntro_patches: make g_quitMessageOpen not static 2025-01-19 15:42:05 +00:00
Hyper
b56c0b8209 Implemented safer hooks for D-Pad movement (#129) 2025-01-19 15:01:11 +00:00
Hyper
cdd801dcec Fix quit message not blocking advertise movie (#127) 2025-01-19 13:06:38 +00:00
Hyper
e7919da1bd game_window: change window title to "Unleashed Recompiled" 2025-01-18 23:51:00 +00:00
Hyper
513a66657f xam: only print XamShowMessageBoxUI info on Win32 2025-01-18 22:51:49 +00:00
Hyper
b9f0742ff9 CTitleStateIntro_patches: don't worry about it 2025-01-18 21:38:09 +00:00
Hyper
7c72146705 CTitleStateIntro_patches: fix save data validation hook 2025-01-18 21:34:20 +00:00
Skyth (Asilkan)
40a8bf557b Scale 2D coordinates to backbuffer resolution. (#124)
* Starting with backbuffer scaling refactor.

* CSD & primitive 2Ds refactored.

* More refactoring.

* Fix primitive 2D, and on screen 3D items.

* Fix right side offset scaling.

* Fix Inspire letterbox.

* Fix offset scaling in world map.

* Fix custom menus.

* Remove debugging code.
2025-01-18 23:56:47 +03:00
Hyper
1cf12fa97f installer_wizard: fix credits not fading out upon exit 2025-01-18 19:39:58 +00:00
Hyper
aacb9d259c Added error message for corrupted save data, removed Win32 message box on XamShowMessageBoxUI (#122)
* xam: remove Win32 message box on XamShowMessageBoxUI

* CTitleStateIntro_patches: display error message on corrupted save data

TitleMenuRemoveContinueOnCorruptSaveMidAsmHook by @DeaTh-G

Co-Authored-By: DeaTh-G <55578911+DeaTh-G@users.noreply.github.com>

---------

Co-authored-by: DeaTh-G <55578911+DeaTh-G@users.noreply.github.com>
2025-01-18 22:32:37 +03:00
Skyth (Asilkan)
c53df15578 Fix movie patches not accounting for half pixel properly. (#119) 2025-01-18 16:52:51 +03:00
SuperSonic16
53331d5760 Added scheme handler for Linux Flatpak (#118) 2025-01-18 15:09:37 +03:00
Skyth
0b675628ea Fix compilation error on Linux. 2025-01-18 13:42:59 +03:00
Skyth
d3edee1945 Fix Vulkan dynamic depth bias validation error. 2025-01-18 12:58:02 +03:00
115 changed files with 2417 additions and 1432 deletions

73
.github/workflows/build-flatpak.yml vendored Normal file
View File

@@ -0,0 +1,73 @@
name: Build Project (Flatpak)
on:
workflow_dispatch:
env:
FLATPAK_ID: io.github.hedge_dev.unleashedrecomp
FREEDESKTOP_VERSION: 23.08
LLVM_VERSION: 18
jobs:
build-flatpak:
name: Build Flatpak
runs-on: ubuntu-24.04
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive
token: ${{ secrets.ORG_TOKEN }}
- name: Checkout private repository
uses: actions/checkout@v4
with:
repository: ${{ secrets.ASSET_REPO }}
token: ${{ secrets.ASSET_REPO_TOKEN }}
path: flatpak/private
- name: Install dependencies
run: |-
sudo apt update
sudo apt install -y flatpak-builder ccache
- name: Cache ccache directory
uses: actions/cache@v4
with:
path: /tmp/ccache
key: ccache-${{ runner.os }}
- name: Prepare Flatpak
run: |
flatpak --user remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
flatpak --user install -y flathub org.freedesktop.Sdk//${{ env.FREEDESKTOP_VERSION }}
flatpak --user install -y flathub org.freedesktop.Sdk.Extension.llvm${{ env.LLVM_VERSION }}//${{ env.FREEDESKTOP_VERSION }}
- name: Build Flatpak
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 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
# - name: Upload artifact
# uses: actions/upload-artifact@v4
# with:
# name: UnleashedRecomp-flatpak
# path: ./${{ env.FLATPAK_ID }}.flatpak
- name: Upload artifact to Discord
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
if: ${{ env.DISCORD_WEBHOOK != '' }}
uses: tsickert/discord-webhook@v6.0.0
with:
webhook-url: ${{ env.DISCORD_WEBHOOK }}
content: |
OS: Linux
Summary: ${{ env.commit_message }}
Commit: ${{ github.sha }}
Branch: ${{ github.ref_name }}
filename: ./${{ env.FLATPAK_ID }}.flatpak

94
.github/workflows/build-windows.yml vendored Normal file
View File

@@ -0,0 +1,94 @@
name: Build Project (Windows)
on:
workflow_dispatch:
env:
CMAKE_PRESET: x64-Clang-Release
jobs:
build:
name: Build Windows
runs-on: windows-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive
token: ${{ secrets.ORG_TOKEN }}
- name: Checkout private repository
uses: actions/checkout@v4
with:
repository: ${{ secrets.ASSET_REPO }}
token: ${{ secrets.ASSET_REPO_TOKEN }}
path: .\private
- name: Setup ccache
uses: hendrikmuhs/ccache-action@v1.2
with:
key: ccache-${{ runner.os }}
- name: Cache vcpkg
uses: actions/cache@v4
with:
path: |
./thirdparty/vcpkg/downloads
./thirdparty/vcpkg/packages
key: vcpkg-${{ runner.os }}-${{ hashFiles('vcpkg.json') }}
restore-keys: |
vcpkg-${{ runner.os }}-
- name: Install dependencies
run: |
choco install ninja
Remove-Item -Path "C:\ProgramData\Chocolatey\bin\ccache.exe" -Force -ErrorAction SilentlyContinue
- name: Configure Developer Command Prompt
uses: ilammy/msvc-dev-cmd@v1
- name: Prepare Project
run: |
$commitMessage = git log -1 --pretty=%s
Add-Content -Path $env:GITHUB_ENV -Value "commit_message=$commitMessage"
copy .\private\* .\UnleashedRecompLib\private
- name: Configure Project
run: cmake . --preset ${{ env.CMAKE_PRESET }} -DSDL2MIXER_VORBIS=VORBISFILE -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache
- name: Build Project
run: cmake --build .\out\build\${{ env.CMAKE_PRESET }} --target UnleashedRecomp
- name: Pack Release
run: |
New-Item -ItemType Directory -Path .\release
New-Item -ItemType Directory -Path .\release\D3D12
Move-Item -Path ".\out\build\${{ env.CMAKE_PRESET }}\UnleashedRecomp\D3D12\D3D12Core.dll" -Destination ".\release\D3D12\D3D12Core.dll"
Move-Item -Path ".\out\build\${{ env.CMAKE_PRESET }}\UnleashedRecomp\dxcompiler.dll" -Destination ".\release\dxcompiler.dll"
Move-Item -Path ".\out\build\${{ env.CMAKE_PRESET }}\UnleashedRecomp\dxil.dll" -Destination ".\release\dxil.dll"
Move-Item -Path ".\out\build\${{ env.CMAKE_PRESET }}\UnleashedRecomp\UnleashedRecomp.exe" -Destination ".\release\UnleashedRecomp.exe"
Compress-Archive -Path .\release\* -DestinationPath .\UnleashedRecomp-Windows.zip
# Uploads the packed zip file to GitHub
# - name: Upload artifact
# uses: actions/upload-artifact@v4
# with:
# name: UnleashedRecomp-Windows
# path: .\UnleashedRecomp-Windows.zip
- name: Upload artifact to Discord
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
if: ${{ env.DISCORD_WEBHOOK != '' }}
uses: tsickert/discord-webhook@v6.0.0
with:
webhook-url: ${{ env.DISCORD_WEBHOOK }}
content: |
OS: Windows
Summary: ${{ env.commit_message }}
Commit: ${{ github.sha }}
Branch: ${{ github.ref_name }}
filename: .\UnleashedRecomp-Windows.zip

12
.gitmodules vendored
View File

@@ -1,12 +1,12 @@
[submodule "thirdparty/PowerRecomp"]
path = tools/PowerRecomp
url = https://github.com/hedge-dev/PowerRecomp
[submodule "tools/XenonRecomp"]
path = tools/XenonRecomp
url = https://github.com/hedge-dev/XenonRecomp.git
[submodule "thirdparty/ddspp"]
path = thirdparty/ddspp
url = https://github.com/redorav/ddspp.git
[submodule "thirdparty/ShaderRecomp"]
path = tools/ShaderRecomp
url = https://github.com/hedge-dev/ShaderRecomp.git
[submodule "tools/XenosRecomp"]
path = tools/XenosRecomp
url = https://github.com/hedge-dev/XenosRecomp.git
[submodule "thirdparty/libmspack"]
path = thirdparty/libmspack
url = https://github.com/kyz/libmspack

View File

@@ -5,8 +5,8 @@ if(NOT DEFINED ENV{VCPKG_ROOT})
endif()
include($ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake)
set(SWA_THIRDPARTY_ROOT ${CMAKE_SOURCE_DIR}/thirdparty)
set(SWA_TOOLS_ROOT ${CMAKE_SOURCE_DIR}/tools)
set(UNLEASHED_RECOMP_THIRDPARTY_ROOT ${CMAKE_SOURCE_DIR}/thirdparty)
set(UNLEASHED_RECOMP_TOOLS_ROOT ${CMAKE_SOURCE_DIR}/tools)
set(CMAKE_CXX_STANDARD 20)
set(BUILD_SHARED_LIBS OFF)
@@ -18,8 +18,13 @@ endif()
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
add_subdirectory(${SWA_THIRDPARTY_ROOT})
add_subdirectory(${SWA_TOOLS_ROOT})
# Target Sandy Bridge for all projects
add_compile_options(
-march=sandybridge
)
add_subdirectory(${UNLEASHED_RECOMP_THIRDPARTY_ROOT})
add_subdirectory(${UNLEASHED_RECOMP_TOOLS_ROOT})
project("UnleashedRecomp-ALL")

View File

@@ -23,8 +23,7 @@
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
},
"toolset": "ClangCL"
}
},
{
"name": "x64-Clang-Debug",

View File

@@ -1,16 +1,13 @@
project("UnleashedRecomp")
set(TARGET_NAME "SWA")
if (WIN32)
option(SWA_D3D12 "Add D3D12 support for rendering" ON)
option(UNLEASHED_RECOMP_D3D12 "Add D3D12 support for rendering" ON)
endif()
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
option(SWA_FLATPAK "Configure the build for Flatpak compatibility." OFF)
option(UNLEASHED_RECOMP_FLATPAK "Configure the build for Flatpak compatibility." OFF)
endif()
option(SWA_XAUDIO2 "Use XAudio2 for audio playback" OFF)
function(BIN2C)
cmake_parse_arguments(BIN2C_ARGS "" "TARGET_OBJ;SOURCE_FILE;DEST_FILE;ARRAY_NAME;COMPRESSION_TYPE" "" ${ARGN})
@@ -42,9 +39,7 @@ function(BIN2C)
endfunction()
add_compile_options(
-march=sandybridge
-fno-strict-aliasing
-Wno-switch
-Wno-unused-function
-Wno-unused-variable
@@ -64,16 +59,15 @@ else()
endif()
add_compile_definitions(
SWA_IMPL
SDL_MAIN_HANDLED
_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR # Microsoft wtf?
_CRT_SECURE_NO_WARNINGS)
set(SWA_PRECOMPILED_HEADERS
set(UNLEASHED_RECOMP_PRECOMPILED_HEADERS
"stdafx.h"
)
set(SWA_KERNEL_CXX_SOURCES
set(UNLEASHED_RECOMP_KERNEL_CXX_SOURCES
"kernel/imports.cpp"
"kernel/xdm.cpp"
"kernel/heap.cpp"
@@ -82,21 +76,13 @@ set(SWA_KERNEL_CXX_SOURCES
"kernel/io/file_system.cpp"
)
set(SWA_LOCALE_CXX_SOURCES
set(UNLEASHED_RECOMP_LOCALE_CXX_SOURCES
"locale/config_locale.cpp"
"locale/locale.cpp"
)
set(SWA_OS_CXX_SOURCES
"os/logger.cpp"
"os/media.cpp"
"os/process.cpp"
"os/user.cpp"
"os/version.cpp"
)
if (WIN32)
list(APPEND SWA_OS_CXX_SOURCES
set(UNLEASHED_RECOMP_OS_CXX_SOURCES
"os/win32/logger_win32.cpp"
"os/win32/media_win32.cpp"
"os/win32/process_win32.cpp"
@@ -104,7 +90,7 @@ if (WIN32)
"os/win32/version_win32.cpp"
)
elseif (CMAKE_SYSTEM_NAME MATCHES "Linux")
list(APPEND SWA_OS_CXX_SOURCES
set(UNLEASHED_RECOMP_OS_CXX_SOURCES
"os/linux/logger_linux.cpp"
"os/linux/media_linux.cpp"
"os/linux/process_linux.cpp"
@@ -113,11 +99,11 @@ elseif (CMAKE_SYSTEM_NAME MATCHES "Linux")
)
endif()
set(SWA_CPU_CXX_SOURCES
set(UNLEASHED_RECOMP_CPU_CXX_SOURCES
"cpu/guest_thread.cpp"
)
set(SWA_GPU_CXX_SOURCES
set(UNLEASHED_RECOMP_GPU_CXX_SOURCES
"gpu/video.cpp"
"gpu/imgui/imgui_common.cpp"
"gpu/imgui/imgui_font_builder.cpp"
@@ -125,37 +111,34 @@ set(SWA_GPU_CXX_SOURCES
"gpu/rhi/plume_vulkan.cpp"
)
if (SWA_D3D12)
list(APPEND SWA_GPU_CXX_SOURCES
if (UNLEASHED_RECOMP_D3D12)
list(APPEND UNLEASHED_RECOMP_GPU_CXX_SOURCES
"gpu/rhi/plume_d3d12.cpp"
)
endif()
set(SWA_APU_CXX_SOURCES
set(UNLEASHED_RECOMP_APU_CXX_SOURCES
"apu/audio.cpp"
"apu/embedded_player.cpp"
"apu/embedded_player.cpp"
"apu/driver/sdl2_driver.cpp"
)
if (SWA_XAUDIO2)
list(APPEND SWA_APU_CXX_SOURCES "apu/driver/xaudio_driver.cpp")
else()
list(APPEND SWA_APU_CXX_SOURCES "apu/driver/sdl2_driver.cpp")
endif()
set(SWA_HID_CXX_SOURCES
set(UNLEASHED_RECOMP_HID_CXX_SOURCES
"hid/hid.cpp"
"hid/driver/sdl_hid.cpp"
)
set(SWA_PATCHES_CXX_SOURCES
set(UNLEASHED_RECOMP_PATCHES_CXX_SOURCES
"patches/ui/CHudPause_patches.cpp"
"patches/ui/CTitleStateIntro_patches.cpp"
"patches/ui/CTitleStateMenu_patches.cpp"
"patches/ui/frontend_listener.cpp"
"patches/aspect_ratio_patches.cpp"
"patches/audio_patches.cpp"
"patches/camera_patches.cpp"
"patches/camera_patches.cpp"
"patches/CGameModeStageTitle_patches.cpp"
"patches/fps_patches.cpp"
"patches/input_patches.cpp"
"patches/inspire_patches.cpp"
"patches/misc_patches.cpp"
"patches/object_patches.cpp"
@@ -164,7 +147,7 @@ set(SWA_PATCHES_CXX_SOURCES
"patches/video_patches.cpp"
)
set(SWA_UI_CXX_SOURCES
set(UNLEASHED_RECOMP_UI_CXX_SOURCES
"ui/achievement_menu.cpp"
"ui/achievement_overlay.cpp"
"ui/installer_wizard.cpp"
@@ -177,7 +160,7 @@ set(SWA_UI_CXX_SOURCES
"ui/game_window.cpp"
)
set(SWA_INSTALL_CXX_SOURCES
set(UNLEASHED_RECOMP_INSTALL_CXX_SOURCES
"install/installer.cpp"
"install/iso_file_system.cpp"
"install/memory_mapped_file.cpp"
@@ -193,97 +176,95 @@ set(SWA_INSTALL_CXX_SOURCES
"install/hashes/update.cpp"
)
set(SWA_USER_CXX_SOURCES
set(UNLEASHED_RECOMP_USER_CXX_SOURCES
"user/achievement_data.cpp"
"user/config.cpp"
)
set(SWA_MOD_CXX_SOURCES
"mod/mod_loader.cpp"
)
set(SWA_THIRDPARTY_SOURCES
"${SWA_THIRDPARTY_ROOT}/imgui/backends/imgui_impl_sdl2.cpp"
"${SWA_THIRDPARTY_ROOT}/imgui/imgui.cpp"
"${SWA_THIRDPARTY_ROOT}/imgui/imgui_demo.cpp"
"${SWA_THIRDPARTY_ROOT}/imgui/imgui_draw.cpp"
"${SWA_THIRDPARTY_ROOT}/imgui/imgui_tables.cpp"
"${SWA_THIRDPARTY_ROOT}/imgui/imgui_widgets.cpp"
"${SWA_THIRDPARTY_ROOT}/implot/implot.cpp"
"${SWA_THIRDPARTY_ROOT}/implot/implot_demo.cpp"
"${SWA_THIRDPARTY_ROOT}/implot/implot_items.cpp"
"${SWA_THIRDPARTY_ROOT}/libmspack/libmspack/mspack/lzxd.c"
"${SWA_THIRDPARTY_ROOT}/tiny-AES-c/aes.c"
"${SWA_TOOLS_ROOT}/ShaderRecomp/thirdparty/smol-v/source/smolv.cpp"
set(UNLEASHED_RECOMP_MOD_CXX_SOURCES
"mod/mod_loader.cpp"
)
set(SWA_THIRDPARTY_INCLUDES
"${SWA_THIRDPARTY_ROOT}/concurrentqueue"
"${SWA_THIRDPARTY_ROOT}/ddspp"
"${SWA_THIRDPARTY_ROOT}/imgui"
"${SWA_THIRDPARTY_ROOT}/implot"
"${SWA_THIRDPARTY_ROOT}/libmspack/libmspack/mspack"
"${SWA_THIRDPARTY_ROOT}/magic_enum/include"
"${SWA_THIRDPARTY_ROOT}/stb"
"${SWA_THIRDPARTY_ROOT}/tiny-AES-c"
"${SWA_THIRDPARTY_ROOT}/TinySHA1"
"${SWA_THIRDPARTY_ROOT}/unordered_dense/include"
"${SWA_THIRDPARTY_ROOT}/volk"
"${SWA_THIRDPARTY_ROOT}/Vulkan-Headers/include"
"${SWA_THIRDPARTY_ROOT}/VulkanMemoryAllocator/include"
"${SWA_TOOLS_ROOT}/bc_diff"
"${SWA_TOOLS_ROOT}/ShaderRecomp/thirdparty/smol-v/source"
set(UNLEASHED_RECOMP_THIRDPARTY_SOURCES
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/imgui/backends/imgui_impl_sdl2.cpp"
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/imgui/imgui.cpp"
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/imgui/imgui_demo.cpp"
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/imgui/imgui_draw.cpp"
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/imgui/imgui_tables.cpp"
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/imgui/imgui_widgets.cpp"
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/implot/implot.cpp"
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/implot/implot_demo.cpp"
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/implot/implot_items.cpp"
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/libmspack/libmspack/mspack/lzxd.c"
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/tiny-AES-c/aes.c"
"${UNLEASHED_RECOMP_TOOLS_ROOT}/XenosRecomp/thirdparty/smol-v/source/smolv.cpp"
)
if (SWA_D3D12)
list(APPEND SWA_THIRDPARTY_INCLUDES "${SWA_THIRDPARTY_ROOT}/D3D12MemoryAllocator/include")
list(APPEND SWA_THIRDPARTY_SOURCES "${SWA_THIRDPARTY_ROOT}/D3D12MemoryAllocator/src/D3D12MemAlloc.cpp")
set(UNLEASHED_RECOMP_THIRDPARTY_INCLUDES
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/concurrentqueue"
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/ddspp"
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/imgui"
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/implot"
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/libmspack/libmspack/mspack"
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/magic_enum/include"
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/stb"
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/tiny-AES-c"
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/TinySHA1"
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/unordered_dense/include"
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/volk"
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/Vulkan-Headers/include"
"${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/VulkanMemoryAllocator/include"
"${UNLEASHED_RECOMP_TOOLS_ROOT}/bc_diff"
"${UNLEASHED_RECOMP_TOOLS_ROOT}/XenosRecomp/thirdparty/smol-v/source"
)
if (UNLEASHED_RECOMP_D3D12)
list(APPEND UNLEASHED_RECOMP_THIRDPARTY_INCLUDES "${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/D3D12MemoryAllocator/include")
list(APPEND UNLEASHED_RECOMP_THIRDPARTY_SOURCES "${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/D3D12MemoryAllocator/src/D3D12MemAlloc.cpp")
endif()
set_source_files_properties(${SWA_THIRDPARTY_SOURCES} PROPERTIES SKIP_PRECOMPILE_HEADERS ON)
set_source_files_properties(${UNLEASHED_RECOMP_THIRDPARTY_SOURCES} PROPERTIES SKIP_PRECOMPILE_HEADERS ON)
set(SWA_CXX_SOURCES
set(UNLEASHED_RECOMP_CXX_SOURCES
"app.cpp"
"exports.cpp"
"main.cpp"
"misc_impl.cpp"
"stdafx.cpp"
"stdafx.cpp"
"version.cpp"
${SWA_KERNEL_CXX_SOURCES}
${SWA_LOCALE_CXX_SOURCES}
${SWA_OS_CXX_SOURCES}
${SWA_CPU_CXX_SOURCES}
${SWA_GPU_CXX_SOURCES}
${SWA_APU_CXX_SOURCES}
${SWA_HID_CXX_SOURCES}
${SWA_PATCHES_CXX_SOURCES}
${SWA_UI_CXX_SOURCES}
${SWA_INSTALL_CXX_SOURCES}
${SWA_USER_CXX_SOURCES}
${SWA_MOD_CXX_SOURCES}
${SWA_THIRDPARTY_SOURCES}
${UNLEASHED_RECOMP_KERNEL_CXX_SOURCES}
${UNLEASHED_RECOMP_LOCALE_CXX_SOURCES}
${UNLEASHED_RECOMP_OS_CXX_SOURCES}
${UNLEASHED_RECOMP_CPU_CXX_SOURCES}
${UNLEASHED_RECOMP_GPU_CXX_SOURCES}
${UNLEASHED_RECOMP_APU_CXX_SOURCES}
${UNLEASHED_RECOMP_HID_CXX_SOURCES}
${UNLEASHED_RECOMP_PATCHES_CXX_SOURCES}
${UNLEASHED_RECOMP_UI_CXX_SOURCES}
${UNLEASHED_RECOMP_INSTALL_CXX_SOURCES}
${UNLEASHED_RECOMP_USER_CXX_SOURCES}
${UNLEASHED_RECOMP_MOD_CXX_SOURCES}
${UNLEASHED_RECOMP_THIRDPARTY_SOURCES}
)
if (WIN32)
# Set up Win32 resources for application icon.
set(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 ${SWA_CXX_SOURCES} "${CMAKE_BINARY_DIR}/res.rc")
add_executable(UnleashedRecomp ${UNLEASHED_RECOMP_CXX_SOURCES} "${CMAKE_BINARY_DIR}/res.rc")
else()
add_executable(UnleashedRecomp ${SWA_CXX_SOURCES})
add_executable(UnleashedRecomp ${UNLEASHED_RECOMP_CXX_SOURCES})
endif()
set_target_properties(UnleashedRecomp PROPERTIES OUTPUT_NAME ${TARGET_NAME})
if (SWA_FLATPAK)
if (UNLEASHED_RECOMP_FLATPAK)
target_compile_definitions(UnleashedRecomp PRIVATE "GAME_INSTALL_DIRECTORY=\"/var/data\"")
endif()
if (SWA_D3D12)
if (UNLEASHED_RECOMP_D3D12)
find_package(directx-headers CONFIG REQUIRED)
find_package(directx12-agility CONFIG REQUIRED)
target_compile_definitions(UnleashedRecomp PRIVATE SWA_D3D12)
target_compile_definitions(UnleashedRecomp PRIVATE UNLEASHED_RECOMP_D3D12)
endif()
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
@@ -292,7 +273,7 @@ endif()
find_package(directx-dxc REQUIRED)
if (SWA_D3D12)
if (UNLEASHED_RECOMP_D3D12)
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/D3D12)
add_custom_command(TARGET UnleashedRecomp POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_PROPERTY:Microsoft::DirectX12-Core,IMPORTED_LOCATION_RELEASE> ${CMAKE_CURRENT_BINARY_DIR}/D3D12
@@ -330,7 +311,7 @@ target_link_libraries(UnleashedRecomp PRIVATE
msdf-atlas-gen::msdf-atlas-gen
nfd::nfd
o1heap
PowerUtils
XenonUtils
SDL2::SDL2-static
SDL2_mixer
tomlplusplus::tomlplusplus
@@ -341,7 +322,7 @@ target_link_libraries(UnleashedRecomp PRIVATE
target_include_directories(UnleashedRecomp PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
"${CMAKE_CURRENT_SOURCE_DIR}/api"
${SWA_THIRDPARTY_INCLUDES}
${UNLEASHED_RECOMP_THIRDPARTY_INCLUDES}
)
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
@@ -350,12 +331,12 @@ if (CMAKE_SYSTEM_NAME MATCHES "Linux")
target_link_libraries(UnleashedRecomp PRIVATE ${X11_LIBRARIES})
endif()
target_precompile_headers(UnleashedRecomp PUBLIC ${SWA_PRECOMPILED_HEADERS})
target_precompile_headers(UnleashedRecomp PUBLIC ${UNLEASHED_RECOMP_PRECOMPILED_HEADERS})
function(compile_shader FILE_PATH TARGET_NAME)
set(FILE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/gpu/shader/${FILE_PATH}.hlsl)
cmake_path(GET FILE_PATH STEM VARIABLE_NAME)
if (SWA_D3D12)
if (UNLEASHED_RECOMP_D3D12)
add_custom_command(
OUTPUT ${FILE_PATH}.dxil.h
COMMAND ${DIRECTX_DXC_TOOL} -T ${TARGET_NAME} -HV 2021 -all-resources-bound -Wno-ignored-attributes -Fh ${FILE_PATH}.dxil.h ${FILE_PATH} -Vn g_${VARIABLE_NAME}_dxil
@@ -380,8 +361,8 @@ function(compile_pixel_shader FILE_PATH)
endfunction()
compile_vertex_shader(copy_vs)
compile_pixel_shader(csd_filter_ps)
compile_vertex_shader(csd_no_tex_vs)
compile_pixel_shader(csd_filter_ps)
compile_vertex_shader(csd_no_tex_vs)
compile_vertex_shader(csd_vs)
compile_pixel_shader(enhanced_motion_blur_ps)
compile_pixel_shader(gaussian_blur_3x3)
@@ -425,22 +406,22 @@ generate_aggregate_header(
"${CMAKE_CURRENT_SOURCE_DIR}/api"
"${CMAKE_CURRENT_SOURCE_DIR}/api/SWA.h"
)
# Only show build type if not Release.
set(IS_BUILD_TYPE_IN_VER_STRING 0)
if (NOT ${CMAKE_BUILD_TYPE} MATCHES "Release")
set(IS_BUILD_TYPE_IN_VER_STRING 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}
IS_BUILD_TYPE_IN_VER_STRING ${IS_BUILD_TYPE_IN_VER_STRING}
IS_GIT_REPO 1
# 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")
@@ -469,7 +450,6 @@ BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/in
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/pulse_install.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/pulse_install.dds" ARRAY_NAME "g_pulse_install" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/achievement_notifications.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/achievement_notifications.dds" ARRAY_NAME "g_achievement_notifications" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/allow_background_input.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/allow_background_input.dds" ARRAY_NAME "g_allow_background_input" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/allow_dpad_movement.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/allow_dpad_movement.dds" ARRAY_NAME "g_allow_dpad_movement" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/antialiasing.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/antialiasing.dds" ARRAY_NAME "g_antialiasing" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/aspect_ratio.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/aspect_ratio.dds" ARRAY_NAME "g_aspect_ratio" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/battle_theme.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/battle_theme.dds" ARRAY_NAME "g_battle_theme" COMPRESSION_TYPE "zstd")
@@ -511,8 +491,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/vibration.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/vibration.dds" ARRAY_NAME "g_vibration" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/vsync.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/vsync.dds" ARRAY_NAME "g_vsync" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/window_size.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/window_size.dds" ARRAY_NAME "g_window_size" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/xbox_color_correction_false.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/xbox_color_correction_false.dds" ARRAY_NAME "g_xbox_color_correction_false" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/xbox_color_correction_true.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/xbox_color_correction_true.dds" ARRAY_NAME "g_xbox_color_correction_true" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/xbox_color_correction.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/xbox_color_correction.dds" ARRAY_NAME "g_xbox_color_correction" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/miles_electric.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/miles_electric.dds" ARRAY_NAME "g_miles_electric" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/game_icon.bmp" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/game_icon.bmp" ARRAY_NAME "g_game_icon")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/game_icon_night.bmp" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/game_icon_night.bmp" ARRAY_NAME "g_game_icon_night")

View File

@@ -9,41 +9,41 @@ namespace Chao::CSD
inline void CNode::SetText(const char* in_pText)
{
GuestToHostFunction<int>(0x830BF640, this, in_pText);
GuestToHostFunction<int>(sub_830BF640, this, in_pText);
}
inline void CNode::SetText(const wchar_t* in_pText)
{
GuestToHostFunction<int>(0x830BF640, this, in_pText);
GuestToHostFunction<int>(sub_830BF640, this, in_pText);
}
inline void CNode::GetPosition(Hedgehog::Math::CVector2& out_rResult) const
{
GuestToHostFunction<void>(0x830BF008, &out_rResult, this);
GuestToHostFunction<void>(sub_830BF008, &out_rResult, this);
}
inline void CNode::SetPosition(float in_X, float in_Y)
{
GuestToHostFunction<int>(0x830BF078, this, in_X, in_Y);
GuestToHostFunction<int>(sub_830BF078, this, in_X, in_Y);
}
inline void CNode::SetHideFlag(uint32_t in_HideFlag)
{
GuestToHostFunction<int>(0x830BF080, this, in_HideFlag);
GuestToHostFunction<int>(sub_830BF080, this, in_HideFlag);
}
inline void CNode::SetRotation(float in_Rotation)
{
GuestToHostFunction<int>(0x830BF088, this, in_Rotation);
GuestToHostFunction<int>(sub_830BF088, this, in_Rotation);
}
inline void CNode::SetScale(float in_X, float in_Y)
{
GuestToHostFunction<int>(0x830BF090, this, in_X, in_Y);
GuestToHostFunction<int>(sub_830BF090, this, in_X, in_Y);
}
inline void CNode::SetPatternIndex(uint32_t in_PatternIndex)
{
GuestToHostFunction<int>(0x830BF300, this, in_PatternIndex);
GuestToHostFunction<int>(sub_830BF300, this, in_PatternIndex);
}
}

View File

@@ -5,20 +5,20 @@ namespace Chao::CSD
inline RCPtr<CScene> CProject::CreateScene(const char* in_pName) const
{
RCPtr<CScene> rcScene;
GuestToHostFunction<void>(0x830BEE00, this, rcScene, in_pName, nullptr);
GuestToHostFunction<void>(sub_830BEE00, this, rcScene, in_pName, nullptr);
return rcScene;
}
inline RCPtr<CScene> CProject::CreateScene(const char* in_pName, const char* in_pMotionName) const
{
RCPtr<CScene> rcScene;
GuestToHostFunction<void>(0x830BECE0, this, rcScene, in_pName, in_pMotionName, nullptr);
GuestToHostFunction<void>(sub_830BECE0, this, rcScene, in_pName, in_pMotionName, nullptr);
return rcScene;
}
inline void CProject::DestroyScene(CScene* in_pScene)
{
GuestToHostFunction<void>(0x830BE298, this, in_pScene);
GuestToHostFunction<void>(sub_830BE298, this, in_pScene);
}
inline void CProject::DestroyScene(RCPtr<CScene>& inout_rcScene)

View File

@@ -30,6 +30,6 @@ namespace Chao::CSD
inline void RCPtrAbs::RCObject::Release()
{
GuestToHostFunction<void>(0x830BA068, this);
GuestToHostFunction<void>(sub_830BA068, this);
}
}

View File

@@ -27,12 +27,12 @@ namespace Chao::CSD
inline void RCPtrAbs::AttachAbs(void* in_pMemory)
{
GuestToHostFunction<void>(0x830BA298, this, in_pMemory);
GuestToHostFunction<void>(sub_830BA298, this, in_pMemory);
}
inline void RCPtrAbs::SetAbs(const RCPtrAbs& in_rPtr)
{
GuestToHostFunction<void>(0x830BA328, this, in_rPtr);
GuestToHostFunction<void>(sub_830BA328, this, in_rPtr);
}
inline void* RCPtrAbs::operator*() const

View File

@@ -22,7 +22,7 @@ namespace Chao::CSD
inline bool CScene::SetMotion(const char* in_pName)
{
return GuestToHostFunction<bool>(0x830BA760, this, in_pName);
return GuestToHostFunction<bool>(sub_830BA760, this, in_pName);
}
inline void CScene::SetMotionFrame(float in_MotionFrame)
@@ -33,21 +33,21 @@ namespace Chao::CSD
inline void CScene::SetPosition(float in_X, float in_Y)
{
GuestToHostFunction<void>(0x830BB550, this, in_X, in_Y);
GuestToHostFunction<void>(sub_830BB550, this, in_X, in_Y);
}
inline void CScene::SetHideFlag(uint32_t in_HideFlag)
{
GuestToHostFunction<void>(0x830BB378, this, in_HideFlag);
GuestToHostFunction<void>(sub_830BB378, this, in_HideFlag);
}
inline void CScene::SetRotation(float in_Angle)
{
GuestToHostFunction<void>(0x830BB5F8, this, in_Angle);
GuestToHostFunction<void>(sub_830BB5F8, this, in_Angle);
}
inline void CScene::SetScale(float in_X, float in_Y)
{
GuestToHostFunction<void>(0x830BB650, this, in_X, in_Y);
GuestToHostFunction<void>(sub_830BB650, this, in_X, in_Y);
}
}

View File

@@ -17,6 +17,7 @@ namespace Hedgehog::Math
be<float> X;
be<float> Y;
be<float> Z;
be<float> W;
};
class CVector4

View File

@@ -0,0 +1,32 @@
#pragma once
#include <SWA.inl>
#include <Hedgehog/Base/hhObject.h>
#include <Hedgehog/Universe/Engine/hhMessageProcess.h>
#include <Hedgehog/Universe/Engine/hhStateMachineMessageReceiver.h>
namespace Hedgehog::Universe
{
class CStateMachineBase : public IStateMachineMessageReceiver, public Base::CObject
{
public:
class CStateBase : public IMessageProcess
{
public:
SWA_INSERT_PADDING(0x08);
xpointer<void> m_pContext;
xpointer<CStateMachineBase> m_pStateMachine;
be<float> m_Time;
SWA_INSERT_PADDING(0x4C);
void* GetContextBase() const;
template<typename T>
T* GetContextBase() const;
};
SWA_INSERT_PADDING(0x60);
};
}
#include <Hedgehog/Universe/Engine/hhStateMachineBase.inl>

View File

@@ -0,0 +1,13 @@
namespace Hedgehog::Universe
{
inline void* CStateMachineBase::CStateBase::GetContextBase() const
{
return m_pContext;
}
template<typename T>
inline T* CStateMachineBase::CStateBase::GetContextBase() const
{
return (T*)m_pContext.get();
}
}

View File

@@ -0,0 +1,12 @@
#pragma once
#include <SWA.inl>
namespace Hedgehog::Universe
{
class IStateMachineMessageReceiver
{
public:
IStateMachineMessageReceiver(const swa_null_ctor&) {}
};
}

View File

@@ -47,6 +47,8 @@
#include "Hedgehog/Sparkle/hhParticleMaterial.h"
#include "Hedgehog/Universe/Engine/hhMessageActor.h"
#include "Hedgehog/Universe/Engine/hhMessageProcess.h"
#include "Hedgehog/Universe/Engine/hhStateMachineBase.h"
#include "Hedgehog/Universe/Engine/hhStateMachineMessageReceiver.h"
#include "Hedgehog/Universe/Engine/hhUpdateInfo.h"
#include "Hedgehog/Universe/Engine/hhUpdateUnit.h"
#include "Hedgehog/Universe/Thread/hhParallelJob.h"
@@ -57,6 +59,8 @@
#include "SWA/CSD/CsdTexListMirage.h"
#include "SWA/CSD/GameObjectCSD.h"
#include "SWA/Camera/Camera.h"
#include "SWA/Camera/CameraController.h"
#include "SWA/CharacterUtility/CharacterProxy.h"
#include "SWA/HUD/GeneralWindow/GeneralWindow.h"
#include "SWA/HUD/Loading/Loading.h"
#include "SWA/HUD/Pause/HudPause.h"
@@ -71,6 +75,7 @@
#include "SWA/Inspire/InspireTextureAnimationInfo.h"
#include "SWA/Inspire/InspireTextureOverlay.h"
#include "SWA/Inspire/InspireTextureOverlayInfo.h"
#include "SWA/Menu/MenuWindowBase.h"
#include "SWA/Movie/MovieDisplayer.h"
#include "SWA/Movie/MovieManager.h"
#include "SWA/Player/Character/EvilSonic/EvilSonic.h"
@@ -91,8 +96,13 @@
#include "SWA/System/GameMode/GameMode.h"
#include "SWA/System/GameMode/GameModeStage.h"
#include "SWA/System/GameMode/GameModeStageMovie.h"
#include "SWA/System/GameMode/GameModeStageTitle.h"
#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/TitleStateWorldMap.h"
#include "SWA/System/GameMode/WorldMap/WorldMapCamera.h"
#include "SWA/System/GameMode/WorldMap/WorldMapCursor.h"
#include "SWA/System/GameObject.h"
#include "SWA/System/GameParameter.h"
#include "SWA/System/GammaController.h"

View File

@@ -3,7 +3,7 @@ namespace SWA
inline boost::shared_ptr<CCsdProject> CCsdDatabaseWrapper::GetCsdProject(const Hedgehog::Base::CSharedString& in_rName)
{
boost::shared_ptr<CCsdProject> spCsdProject;
GuestToHostFunction<void>(0x825E2B40, &in_rName, this, &spCsdProject, 0);
GuestToHostFunction<void>(sub_825E2B40, &in_rName, this, &spCsdProject, 0);
return spCsdProject;
}
}

View File

@@ -0,0 +1,17 @@
#pragma once
#include <SWA.inl>
namespace SWA
{
class CCameraController : public Hedgehog::Universe::CStateMachineBase::CStateBase
{
public:
SWA_INSERT_PADDING(0x04);
be<float> m_FieldOfView;
SWA_INSERT_PADDING(0x68);
};
SWA_ASSERT_OFFSETOF(CCameraController, m_FieldOfView, 0x64);
SWA_ASSERT_SIZEOF(CCameraController, 0xD0);
}

View File

@@ -0,0 +1,14 @@
#pragma once
#include <Hedgehog/Math/Vector.h>
namespace SWA
{
class CCharacterProxy
{
public:
SWA_INSERT_PADDING(0x120);
Hedgehog::Math::CVector m_Position;
Hedgehog::Math::CVector m_Velocity;
};
}

View File

@@ -0,0 +1,12 @@
#pragma once
#include <SWA.inl>
namespace SWA
{
class CMenuWindowBase
{
public:
SWA_INSERT_PADDING(0x10);
};
}

View File

@@ -4,10 +4,9 @@
namespace SWA
{
class CGameMode // : Hedgehog::Universe::TStateMachine<SWA::CGame>::TState
class CGameMode : public Hedgehog::Universe::CStateMachineBase::CStateBase
{
public:
SWA_INSERT_PADDING(0x60); // base
SWA_INSERT_PADDING(0x08);
};
}

View File

@@ -0,0 +1,14 @@
#pragma once
#include <SWA.inl>
namespace SWA
{
class CGameModeStageTitle : public CGameModeStage
{
public:
SWA_INSERT_PADDING(0x0E);
bool m_IsPlayingAdvertiseMovie;
be<float> m_AdvertiseMovieWaitTime;
};
}

View File

@@ -4,19 +4,14 @@
namespace SWA
{
class CTitleStateBase // : Hedgehog::Universe::TStateMachine<SWA::CTitleManager>::TState
class CTitleStateBase : public Hedgehog::Universe::CStateMachineBase::CStateBase
{
public:
class CMember
class CTitleStateContext
{
public:
SWA_INSERT_PADDING(0x1E8);
xpointer<CTitleMenu> m_pTitleMenu;
};
SWA_INSERT_PADDING(0x08);
xpointer<CMember> m_pMember;
SWA_INSERT_PADDING(0x5C);
be<uint32_t> m_State;
};
}

View File

@@ -0,0 +1,8 @@
#pragma once
#include <SWA.inl>
namespace SWA
{
class CTitleStateIntro : public CTitleStateBase {};
}

View File

@@ -0,0 +1,14 @@
#pragma once
#include <SWA.inl>
#include <SWA/System/GameMode/WorldMap/WorldMapCursor.h>
namespace SWA
{
class CTitleStateWorldMap : public CTitleStateBase
{
public:
SWA_INSERT_PADDING(0x08);
xpointer<CWorldMapCursor> m_pWorldMapCursor;
};
}

View File

@@ -0,0 +1,26 @@
#pragma once
#include <SWA.inl>
namespace SWA
{
class CWorldMapCamera : public CCameraController
{
public:
be<float> m_Pitch;
be<float> m_Yaw;
be<float> m_Distance;
be<float> m_RotationSpeed;
SWA_INSERT_PADDING(0x08);
bool m_CanMove;
SWA_INSERT_PADDING(0x34);
be<float> m_TiltToEarthTransitionSpeed;
};
SWA_ASSERT_OFFSETOF(CWorldMapCamera, m_Pitch, 0xD0);
SWA_ASSERT_OFFSETOF(CWorldMapCamera, m_Yaw, 0xD4);
SWA_ASSERT_OFFSETOF(CWorldMapCamera, m_Distance, 0xD8);
SWA_ASSERT_OFFSETOF(CWorldMapCamera, m_RotationSpeed, 0xDC);
SWA_ASSERT_OFFSETOF(CWorldMapCamera, m_CanMove, 0xE8);
SWA_ASSERT_OFFSETOF(CWorldMapCamera, m_TiltToEarthTransitionSpeed, 0x120);
}

View File

@@ -0,0 +1,24 @@
#pragma once
#include <SWA.inl>
namespace SWA
{
class CWorldMapCursor : public CMenuWindowBase
{
public:
SWA_INSERT_PADDING(0x24);
be<float> m_LeftStickVertical;
be<float> m_LeftStickHorizontal;
bool m_IsCursorMoving;
SWA_INSERT_PADDING(0x07);
be<float> m_CursorY;
be<float> m_CursorX;
};
SWA_ASSERT_OFFSETOF(CWorldMapCursor, m_LeftStickVertical, 0x34);
SWA_ASSERT_OFFSETOF(CWorldMapCursor, m_LeftStickHorizontal, 0x38);
SWA_ASSERT_OFFSETOF(CWorldMapCursor, m_IsCursorMoving, 0x3C);
SWA_ASSERT_OFFSETOF(CWorldMapCursor, m_CursorY, 0x44);
SWA_ASSERT_OFFSETOF(CWorldMapCursor, m_CursorX, 0x48);
}

View File

@@ -7,6 +7,7 @@
#include <patches/inspire_patches.h>
#include <ui/game_window.h>
#include <user/config.h>
#include <user/paths.h>
void App::Restart(std::vector<std::string> restartArgs)
{

View File

@@ -9,6 +9,7 @@ public:
static inline bool s_isMissingDLC;
static inline bool s_isLoading;
static inline bool s_isWerehog;
static inline bool s_isSaveDataCorrupt;
static inline ELanguage s_language;

View File

@@ -7,11 +7,10 @@
// Number of samples in a frame
#define XAUDIO_NUM_SAMPLES 256
#ifdef SWA_IMPL
void XAudioInitializeSystem();
void XAudioRegisterClient(PPCFunc* callback, uint32_t param);
void XAudioSubmitFrame(void* samples);
#endif
void XAudioConfigValueChangedCallback(class IConfigDef* configDef);
uint32_t XAudioRegisterRenderDriverClient(be<uint32_t>* callback, be<uint32_t>* driver);
uint32_t XAudioUnregisterRenderDriverClient(uint32_t driver);

View File

@@ -1,26 +1,29 @@
#include "sdl2_driver.h"
#include <apu/audio.h>
#include <cpu/guest_thread.h>
#include <kernel/heap.h>
#include <user/config.h>
static PPCFunc* g_clientCallback{};
static uint32_t g_clientCallbackParam{}; // pointer in guest memory
static SDL_AudioDeviceID g_audioDevice{};
static bool g_downMixToStereo;
void XAudioInitializeSystem()
static void CreateAudioDevice()
{
SDL_SetHint(SDL_HINT_AUDIO_CATEGORY, "playback");
SDL_SetHint(SDL_HINT_AUDIO_DEVICE_APP_NAME, "Unleashed Recompiled");
SDL_InitSubSystem(SDL_INIT_AUDIO);
if (g_audioDevice != NULL)
SDL_CloseAudioDevice(g_audioDevice);
bool surround = Config::ChannelConfiguration == EChannelConfiguration::Surround;
int allowedChanges = surround ? SDL_AUDIO_ALLOW_CHANNELS_CHANGE : 0;
SDL_AudioSpec desired{}, obtained{};
desired.freq = XAUDIO_SAMPLES_HZ;
desired.format = AUDIO_F32SYS;
desired.channels = XAUDIO_NUM_CHANNELS;
desired.channels = surround ? XAUDIO_NUM_CHANNELS : 2;
desired.samples = XAUDIO_NUM_SAMPLES;
g_audioDevice = SDL_OpenAudioDevice(nullptr, 0, &desired, &obtained, SDL_AUDIO_ALLOW_CHANNELS_CHANGE);
g_audioDevice = SDL_OpenAudioDevice(nullptr, 0, &desired, &obtained, allowedChanges);
if (obtained.channels != 2 && obtained.channels != XAUDIO_NUM_CHANNELS)
if (obtained.channels != 2 && obtained.channels != XAUDIO_NUM_CHANNELS) // This check may fail only when surround sound is enabled.
{
SDL_CloseAudioDevice(g_audioDevice);
g_audioDevice = SDL_OpenAudioDevice(nullptr, 0, &desired, &obtained, 0);
@@ -29,7 +32,16 @@ void XAudioInitializeSystem()
g_downMixToStereo = (obtained.channels == 2);
}
void XAudioInitializeSystem()
{
SDL_SetHint(SDL_HINT_AUDIO_CATEGORY, "playback");
SDL_SetHint(SDL_HINT_AUDIO_DEVICE_APP_NAME, "Unleashed Recompiled");
SDL_InitSubSystem(SDL_INIT_AUDIO);
CreateAudioDevice();
}
static std::unique_ptr<std::thread> g_audioThread;
static volatile bool g_audioThreadShouldExit;
static void AudioThread()
{
@@ -39,7 +51,7 @@ static void AudioThread()
size_t channels = g_downMixToStereo ? 2 : XAUDIO_NUM_CHANNELS;
while (true)
while (!g_audioThreadShouldExit)
{
uint32_t queuedAudioSize = SDL_GetQueuedAudioSize(g_audioDevice);
constexpr size_t MAX_LATENCY = 10;
@@ -62,6 +74,13 @@ static void AudioThread()
}
}
static void CreateAudioThread()
{
SDL_PauseAudioDevice(g_audioDevice, 0);
g_audioThreadShouldExit = false;
g_audioThread = std::make_unique<std::thread>(AudioThread);
}
void XAudioRegisterClient(PPCFunc* callback, uint32_t param)
{
auto* pClientParam = static_cast<uint32_t*>(g_userHeap.Alloc(sizeof(param)));
@@ -70,8 +89,7 @@ void XAudioRegisterClient(PPCFunc* callback, uint32_t param)
g_clientCallbackParam = g_memory.MapVirtual(pClientParam);
g_clientCallback = callback;
SDL_PauseAudioDevice(g_audioDevice, 0);
g_audioThread = std::make_unique<std::thread>(AudioThread);
CreateAudioThread();
}
void XAudioSubmitFrame(void* samples)
@@ -119,3 +137,18 @@ void XAudioSubmitFrame(void* samples)
SDL_QueueAudio(g_audioDevice, &audioFrames, sizeof(audioFrames));
}
}
void XAudioConfigValueChangedCallback(IConfigDef* configDef)
{
if (configDef == &Config::ChannelConfiguration)
{
if (g_audioThread->joinable())
{
g_audioThreadShouldExit = true;
g_audioThread->join();
}
CreateAudioDevice();
CreateAudioThread();
}
}

View File

@@ -1,3 +0,0 @@
#pragma once
#include <apu/audio.h>

View File

@@ -1,121 +0,0 @@
#include <stdafx.h>
#include "xaudio_driver.h"
#include <xaudio2.h>
#include <cpu/guest_thread.h>
#include <cpu/ppc_context.h>
#include <kernel/heap.h>
#define XAUDIO_DRIVER_KEY (uint32_t)('XAUD')
PPCFunc* volatile g_clientCallback{};
DWORD g_clientCallbackParam{}; // pointer in guest memory
DWORD g_driverThread{};
// TODO: Should use a counted ptr
IXAudio2* g_audio{};
IXAudio2MasteringVoice* g_masteringVoice{};
IXAudio2SourceVoice* g_sourceVoice{};
constexpr uint32_t g_semaphoreCount = 16;
constexpr uint32_t g_audioFrameSize = 256 * 6;
HANDLE g_audioSemaphore{ CreateSemaphoreA(nullptr, g_semaphoreCount, g_semaphoreCount, nullptr) };
uint32_t g_audioFrames[g_audioFrameSize * g_semaphoreCount];
uint32_t g_audioFrameIndex = 0;
class VoiceCallback : public IXAudio2VoiceCallback
{
STDMETHOD_(void, OnVoiceProcessingPassStart)(UINT32 BytesRequired) override {}
STDMETHOD_(void, OnVoiceProcessingPassEnd)() override {}
STDMETHOD_(void, OnBufferStart)(void* pBufferContext) override {}
STDMETHOD_(void, OnBufferEnd)(void* pBufferContext) override
{
ReleaseSemaphore(g_audioSemaphore, 1, nullptr);
}
STDMETHOD_(void, OnStreamEnd)() override {}
STDMETHOD_(void, OnLoopEnd)(void* pBufferContext) override {}
STDMETHOD_(void, OnVoiceError)(void* pBufferContext, HRESULT Error) override {}
} gVoiceCallback;
PPC_FUNC(DriverLoop)
{
GuestThread::SetThreadName(GetCurrentThreadId(), "Audio Driver");
while (true)
{
if (!g_clientCallback)
{
continue;
}
WaitForSingleObject(g_audioSemaphore, INFINITE);
ctx.r3.u64 = g_clientCallbackParam;
g_clientCallback(ctx, g_memory.base);
}
}
void XAudioInitializeSystem()
{
if (g_audio)
{
return;
}
//reinterpret_cast<decltype(&XAudio2Create)>(
// GetProcAddress(LoadLibraryA("XAudio2_8.dll"), "XAudio2Create"))(&gAudio, 0, 1);
XAudio2Create(&g_audio);
g_audio->CreateMasteringVoice(&g_masteringVoice);
WAVEFORMATIEEEFLOATEX format{};
format.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
format.Format.cbSize = sizeof(format) - sizeof(format.Format);
format.Format.nChannels = XAUDIO_NUM_CHANNELS;
format.Format.nSamplesPerSec = XAUDIO_SAMPLES_HZ;
format.Format.wBitsPerSample = XAUDIO_SAMPLE_BITS;
format.Format.nBlockAlign = (format.Format.nChannels * format.Format.wBitsPerSample) / 8;
format.Format.nAvgBytesPerSec = format.Format.nSamplesPerSec * format.Format.nBlockAlign;
format.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
format.Samples.wValidBitsPerSample = format.Format.wBitsPerSample;
format.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_CENTER | SPEAKER_FRONT_RIGHT |
SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT;
g_audio->CreateSourceVoice(&g_sourceVoice, &format.Format, 0, 1024, &gVoiceCallback);
g_sourceVoice->Start();
KeInsertHostFunction(XAUDIO_DRIVER_KEY, DriverLoop);
GuestThread::Start({ XAUDIO_DRIVER_KEY, 0, 0 }, nullptr);
}
void XAudioRegisterClient(PPCFunc* callback, uint32_t param)
{
auto* pClientParam = static_cast<uint32_t*>(g_userHeap.Alloc(sizeof(param)));
ByteSwapInplace(param);
*pClientParam = param;
g_clientCallbackParam = g_memory.MapVirtual(pClientParam);
g_clientCallback = callback;
}
void XAudioSubmitFrame(void* samples)
{
uint32_t* audioFrame = &g_audioFrames[g_audioFrameSize * g_audioFrameIndex];
g_audioFrameIndex = (g_audioFrameIndex + 1) % g_semaphoreCount;
for (size_t i = 0; i < XAUDIO_NUM_SAMPLES; i++)
{
for (size_t j = 0; j < 6; j++)
audioFrame[i * XAUDIO_NUM_CHANNELS + j] = ByteSwap(((uint32_t*)samples)[j * XAUDIO_NUM_SAMPLES + i]);
}
XAUDIO2_BUFFER buffer{};
buffer.pAudioData = (BYTE*)audioFrame;
buffer.AudioBytes = XAUDIO_NUM_SAMPLES * XAUDIO_NUM_CHANNELS * sizeof(float);
buffer.PlayLength = XAUDIO_NUM_SAMPLES;
g_sourceVoice->SubmitSourceBuffer(&buffer);
}

View File

@@ -1,2 +0,0 @@
#pragma once
#include <apu/audio.h>

View File

@@ -22,6 +22,7 @@ enum class ImGuiCallback : int32_t
SetOutline = -6,
SetProceduralOrigin = -7,
// -8 is ImDrawCallback_ResetRenderState, don't use!
SetAdditive = -9
};
union ImGuiCallbackData
@@ -64,6 +65,11 @@ union ImGuiCallbackData
{
float proceduralOrigin[2];
} setProceduralOrigin;
struct
{
bool enabled;
} setAdditive;
};
extern ImGuiCallbackData* AddImGuiCallback(ImGuiCallback callback);

View File

@@ -1127,7 +1127,7 @@ namespace plume {
desc.srcBlend = RenderBlend::SRC_ALPHA;
desc.dstBlend = RenderBlend::INV_SRC_ALPHA;
desc.blendOp = RenderBlendOperation::ADD;
desc.srcBlendAlpha = RenderBlend::ONE;
desc.srcBlendAlpha = RenderBlend::SRC_ALPHA;
desc.dstBlendAlpha = RenderBlend::INV_SRC_ALPHA;
desc.blendOpAlpha = RenderBlendOperation::ADD;
return desc;
@@ -1269,9 +1269,9 @@ namespace plume {
RenderFilter minFilter = RenderFilter::LINEAR;
RenderFilter magFilter = RenderFilter::LINEAR;
RenderMipmapMode mipmapMode = RenderMipmapMode::LINEAR;
RenderTextureAddressMode addressU = RenderTextureAddressMode::WRAP;
RenderTextureAddressMode addressV = RenderTextureAddressMode::WRAP;
RenderTextureAddressMode addressW = RenderTextureAddressMode::WRAP;
RenderTextureAddressMode addressU = RenderTextureAddressMode::CLAMP;
RenderTextureAddressMode addressV = RenderTextureAddressMode::CLAMP;
RenderTextureAddressMode addressW = RenderTextureAddressMode::CLAMP;
float mipLODBias = 0.0f;
uint32_t maxAnisotropy = 16;
bool anisotropyEnabled = false;

View File

@@ -1,4 +1,4 @@
#include "../../../tools/ShaderRecomp/ShaderRecomp/shader_common.h"
#include "../../../tools/XenosRecomp/XenosRecomp/shader_common.h"
#ifdef __spirv__

View File

@@ -1,4 +1,4 @@
#include "../../../tools/ShaderRecomp/ShaderRecomp/shader_common.h"
#include "../../../tools/XenosRecomp/XenosRecomp/shader_common.h"
#ifdef __spirv__

View File

@@ -1,4 +1,4 @@
#include "../../../tools/ShaderRecomp/ShaderRecomp/shader_common.h"
#include "../../../tools/XenosRecomp/XenosRecomp/shader_common.h"
#ifdef __spirv__

View File

@@ -1,4 +1,4 @@
#include "../../../tools/ShaderRecomp/ShaderRecomp/shader_common.h"
#include "../../../tools/XenosRecomp/XenosRecomp/shader_common.h"
#ifdef __spirv__

View File

@@ -1,4 +1,4 @@
#include "../../../tools/ShaderRecomp/ShaderRecomp/shader_common.h"
#include "../../../tools/XenosRecomp/XenosRecomp/shader_common.h"
#ifdef __spirv__

View File

@@ -1,4 +1,4 @@
#include "../../../tools/ShaderRecomp/ShaderRecomp/shader_common.h"
#include "../../../tools/XenosRecomp/XenosRecomp/shader_common.h"
#ifdef __spirv__

View File

@@ -1,6 +1,6 @@
#pragma once
#include "../../../tools/ShaderRecomp/ShaderRecomp/shader_common.h"
#include "../../../tools/XenosRecomp/XenosRecomp/shader_common.h"
#ifdef __spirv__

View File

@@ -10,7 +10,7 @@
#include <decompressor.h>
#include <kernel/function.h>
#include <kernel/heap.h>
#include <hid/hid_detail.h>
#include <hid/hid.h>
#include <kernel/memory.h>
#include <kernel/xdbf.h>
#include <res/bc_diff/button_bc_diff.bin.h>
@@ -34,9 +34,9 @@
#include <magic_enum/magic_enum.hpp>
#endif
#include "../../tools/ShaderRecomp/ShaderRecomp/shader_common.h"
#include "../../tools/XenosRecomp/XenosRecomp/shader_common.h"
#ifdef SWA_D3D12
#ifdef UNLEASHED_RECOMP_D3D12
#include "shader/copy_vs.hlsl.dxil.h"
#include "shader/csd_filter_ps.hlsl.dxil.h"
#include "shader/csd_no_tex_vs.hlsl.dxil.h"
@@ -84,7 +84,7 @@ extern "C"
namespace plume
{
#ifdef SWA_D3D12
#ifdef UNLEASHED_RECOMP_D3D12
extern std::unique_ptr<RenderInterface> CreateD3D12Interface();
#endif
#ifdef SDL_VULKAN_ENABLED
@@ -198,7 +198,7 @@ static void SetDirtyValue(bool& dirtyState, T& dest, const T& src)
}
}
#ifdef SWA_D3D12
#ifdef UNLEASHED_RECOMP_D3D12
static bool g_vulkan = false;
#else
static constexpr bool g_vulkan = true;
@@ -619,7 +619,7 @@ static void LoadEmbeddedResources()
g_shaderCache = std::make_unique<uint8_t[]>(g_spirvCacheDecompressedSize);
ZSTD_decompress(g_shaderCache.get(), g_spirvCacheDecompressedSize, g_compressedSpirvCache, g_spirvCacheCompressedSize);
}
#ifdef SWA_D3D12
#ifdef UNLEASHED_RECOMP_D3D12
else
{
g_shaderCache = std::make_unique<uint8_t[]>(g_dxilCacheDecompressedSize);
@@ -1111,7 +1111,7 @@ static GuestShader* g_csdShader;
static std::unique_ptr<GuestShader> g_enhancedMotionBlurShader;
#ifdef SWA_D3D12
#ifdef UNLEASHED_RECOMP_D3D12
#define CREATE_SHADER(NAME) \
g_device->createShader( \
@@ -1123,7 +1123,7 @@ static std::unique_ptr<GuestShader> g_enhancedMotionBlurShader;
#else
#define CREATE_SHADER(NAME) \
g_device->createShader(g_##NAME##_spirv, sizeof(g_##NAME##_spirv), "main", RenderShaderFormat::SPIRV);
g_device->createShader(g_##NAME##_spirv, sizeof(g_##NAME##_spirv), "main", RenderShaderFormat::SPIRV)
#endif
@@ -1141,6 +1141,7 @@ static constexpr size_t SAMPLER_DESCRIPTOR_SIZE = 1024;
static std::unique_ptr<GuestTexture> g_imFontTexture;
static std::unique_ptr<RenderPipelineLayout> g_imPipelineLayout;
static std::unique_ptr<RenderPipeline> g_imPipeline;
static std::unique_ptr<RenderPipeline> g_imAdditivePipeline;
template<typename T>
static void ExecuteCopyCommandList(const T& function)
@@ -1309,6 +1310,9 @@ static void CreateImGuiBackend()
pipelineDesc.inputSlotsCount = 1;
g_imPipeline = g_device->createGraphicsPipeline(pipelineDesc);
pipelineDesc.renderTargetBlend[0].dstBlend = RenderBlend::ONE;
g_imAdditivePipeline = g_device->createGraphicsPipeline(pipelineDesc);
#ifndef ENABLE_IM_FONT_ATLAS_SNAPSHOT
ImFontAtlasSnapshot snapshot;
snapshot.Snap();
@@ -1355,16 +1359,8 @@ static void CheckSwapChain()
if (g_needsResize)
Video::ComputeViewportDimensions();
if (g_aspectRatio >= NARROW_ASPECT_RATIO)
{
g_backBuffer->width = Video::s_viewportWidth * 720 / Video::s_viewportHeight;
g_backBuffer->height = 720;
}
else
{
g_backBuffer->width = 960;
g_backBuffer->height = Video::s_viewportHeight * 960 / Video::s_viewportWidth;
}
g_backBuffer->width = Video::s_viewportWidth;
g_backBuffer->height = Video::s_viewportHeight;
}
static void BeginCommandList()
@@ -1448,7 +1444,7 @@ void Video::CreateHostDevice(const char *sdlVideoDriver)
GameWindow::Init(sdlVideoDriver);
#ifdef SWA_D3D12
#ifdef UNLEASHED_RECOMP_D3D12
g_vulkan = DetectWine() || Config::GraphicsAPI == EGraphicsAPI::Vulkan;
#endif
@@ -1460,7 +1456,7 @@ void Video::CreateHostDevice(const char *sdlVideoDriver)
#else
g_interface = CreateVulkanInterface();
#endif
#ifdef SWA_D3D12
#ifdef UNLEASHED_RECOMP_D3D12
else
g_interface = CreateD3D12Interface();
#endif
@@ -2099,9 +2095,10 @@ static void ProcDrawImGui(const RenderCommand& cmd)
SetFramebuffer(g_backBuffer, nullptr, false);
auto& commandList = g_commandLists[g_frame];
auto pipeline = g_imPipeline.get();
commandList->setGraphicsPipelineLayout(g_imPipelineLayout.get());
commandList->setPipeline(g_imPipeline.get());
commandList->setPipeline(pipeline);
commandList->setGraphicsDescriptorSet(g_textureDescriptorSet.get(), 0);
commandList->setGraphicsDescriptorSet(g_samplerDescriptorSet.get(), 1);
@@ -2175,6 +2172,16 @@ static void ProcDrawImGui(const RenderCommand& cmd)
case ImGuiCallback::SetProceduralOrigin:
setPushConstants(&pushConstants.proceduralOrigin, &callbackData->setProceduralOrigin, sizeof(callbackData->setProceduralOrigin));
break;
case ImGuiCallback::SetAdditive:
{
auto pipelineToSet = callbackData->setAdditive.enabled ? g_imAdditivePipeline.get() : g_imPipeline.get();
if (pipeline != pipelineToSet)
{
commandList->setPipeline(pipelineToSet);
pipeline = pipelineToSet;
}
break;
}
default:
assert(false && "Unknown ImGui callback type.");
break;
@@ -2456,11 +2463,6 @@ static GuestSurface* GetBackBuffer()
return g_backBuffer;
}
GuestSurface* Video::GetBackBuffer()
{
return g_backBuffer;
}
void Video::ComputeViewportDimensions()
{
uint32_t width = g_swapChain->getWidth();
@@ -2663,9 +2665,6 @@ static GuestSurface* CreateSurface(uint32_t width, uint32_t height, uint32_t for
static void FlushViewport()
{
bool renderingToBackBuffer = g_renderTarget == g_backBuffer &&
g_backBuffer->texture != g_backBuffer->textureHolder.get();
auto& commandList = g_commandLists[g_frame];
if (g_dirtyStates.viewport)
@@ -2677,17 +2676,6 @@ static void FlushViewport()
viewport.y += 0.5f;
}
if (renderingToBackBuffer)
{
float width = Video::s_viewportWidth;
float height = Video::s_viewportHeight;
viewport.x *= width / g_backBuffer->width;
viewport.y *= height / g_backBuffer->height;
viewport.width *= width / g_backBuffer->width;
viewport.height *= height / g_backBuffer->height;
}
if (viewport.minDepth > viewport.maxDepth)
std::swap(viewport.minDepth, viewport.maxDepth);
@@ -2704,17 +2692,6 @@ static void FlushViewport()
g_viewport.x + g_viewport.width,
g_viewport.y + g_viewport.height);
if (renderingToBackBuffer)
{
uint32_t width = Video::s_viewportWidth;
uint32_t height = Video::s_viewportHeight;
scissorRect.left = scissorRect.left * width / g_backBuffer->width;
scissorRect.top = scissorRect.top * height / g_backBuffer->height;
scissorRect.right = scissorRect.right * width / g_backBuffer->width;
scissorRect.bottom = scissorRect.bottom * height / g_backBuffer->height;
}
commandList->setScissors(scissorRect);
g_dirtyStates.scissorRect = false;
@@ -2818,7 +2795,10 @@ static void ProcStretchRect(const RenderCommand& cmd)
g_dirtyStates.pipelineState = true;
if (g_vulkan)
{
g_dirtyStates.depthBias = true; // Static depth bias in MSAA pipeline invalidates dynamic depth bias.
g_dirtyStates.vertexShaderConstants = true;
}
SetHalfPixel(oldHalfPixel);
}
@@ -3052,7 +3032,7 @@ static void SetTexture(GuestDevice* device, uint32_t index, GuestTexture* textur
auto isPlayStation = Config::ControllerIcons == EControllerIcons::PlayStation;
if (Config::ControllerIcons == EControllerIcons::Auto)
isPlayStation = hid::detail::g_inputDeviceController == hid::detail::EInputDevice::PlayStation;
isPlayStation = hid::g_inputDeviceController == hid::EInputDevice::PlayStation;
if (isPlayStation && texture != nullptr && texture->patchedTexture != nullptr)
texture = texture->patchedTexture.get();
@@ -3143,7 +3123,7 @@ static RenderShader* GetOrLinkShader(GuestShader* guestShader, uint32_t specCons
shader = guestShader->linkedShaders[specConstants].get();
}
#ifdef SWA_D3D12
#ifdef UNLEASHED_RECOMP_D3D12
if (shader == nullptr)
{
static Mutex g_compiledSpecConstantLibraryBlobMutex;
@@ -4387,16 +4367,33 @@ static void ProcSetPixelShader(const RenderCommand& cmd)
default:
{
size_t height = round(Video::s_viewportHeight * Config::ResolutionScale);
if (g_aspectRatio >= WIDE_ASPECT_RATIO)
{
size_t height = round(Video::s_viewportHeight * Config::ResolutionScale);
if (height > 1440)
shaderIndex = GAUSSIAN_BLUR_9X9;
else if (height > 1080)
shaderIndex = GAUSSIAN_BLUR_7X7;
else if (height > 720)
shaderIndex = GAUSSIAN_BLUR_5X5;
if (height > 1440)
shaderIndex = GAUSSIAN_BLUR_9X9;
else if (height > 1080)
shaderIndex = GAUSSIAN_BLUR_7X7;
else if (height > 720)
shaderIndex = GAUSSIAN_BLUR_5X5;
else
shaderIndex = GAUSSIAN_BLUR_3X3;
}
else
shaderIndex = GAUSSIAN_BLUR_3X3;
{
// Narrow aspect ratios should check for width to account for VERT+.
size_t width = round(Video::s_viewportWidth * Config::ResolutionScale);
if (width > 2560)
shaderIndex = GAUSSIAN_BLUR_9X9;
else if (width > 1920)
shaderIndex = GAUSSIAN_BLUR_7X7;
else if (width > 1280)
shaderIndex = GAUSSIAN_BLUR_5X5;
else
shaderIndex = GAUSSIAN_BLUR_3X3;
}
break;
}
@@ -5124,23 +5121,40 @@ PPC_FUNC(sub_8258CAE0)
g_renderDirectorProfiler.End();
}
// World map disables VERT+, so scaling by width does not work for it.
static uint32_t g_forceCheckHeightForPostProcessFix;
// SWA::CWorldMapCamera::CWorldMapCamera
PPC_FUNC_IMPL(__imp__sub_824860E0);
PPC_FUNC(sub_824860E0)
{
++g_forceCheckHeightForPostProcessFix;
__imp__sub_824860E0(ctx, base);
}
// SWA::CCameraController::~CCameraController
PPC_FUNC_IMPL(__imp__sub_824831D0);
PPC_FUNC(sub_824831D0)
{
if (PPC_LOAD_U32(ctx.r3.u32) == 0x8202BF1C) // SWA::CWorldMapCamera
--g_forceCheckHeightForPostProcessFix;
__imp__sub_824831D0(ctx, base);
}
void PostProcessResolutionFix(PPCRegister& r4, PPCRegister& f1, PPCRegister& f2)
{
auto device = reinterpret_cast<be<uint32_t>*>(g_memory.Translate(r4.u32));
uint32_t width = device[46].get();
uint32_t height = device[47].get();
double aspectRatio = double(width) / double(height);
#if 0
// TODO: Figure out why this breaks for height > weight
double factor;
if (width > height)
if ((aspectRatio >= WIDE_ASPECT_RATIO) || (g_forceCheckHeightForPostProcessFix != 0))
factor = 720.0 / double(height);
else
factor = 1280.0 / double(width);
#else
double factor = 720.0 / double(height);
#endif
f1.f64 *= factor;
f2.f64 *= factor;

View File

@@ -23,7 +23,6 @@ struct Video
static void Present();
static void StartPipelinePrecompilation();
static void WaitForGPU();
static struct GuestSurface* GetBackBuffer();
static void ComputeViewportDimensions();
};
@@ -286,7 +285,7 @@ struct GuestShader : GuestResource
std::unique_ptr<RenderShader> shader;
struct ShaderCacheEntry* shaderCacheEntry = nullptr;
ankerl::unordered_dense::map<uint32_t, std::unique_ptr<RenderShader>> linkedShaders;
#ifdef SWA_D3D12
#ifdef UNLEASHED_RECOMP_D3D12
std::vector<ComPtr<IDxcBlob>> shaderBlobs;
ComPtr<IDxcBlobEncoding> libraryBlob;
#endif

View File

@@ -1,7 +1,8 @@
#include <stdafx.h>
#include <SDL.h>
#include <user/config.h>
#include <hid/hid_detail.h>
#include <hid/hid.h>
#include <os/logger.h>
#include <ui/game_window.h>
#include <kernel/xdm.h>
#include <app.h>
@@ -40,17 +41,17 @@ public:
return SDL_GameControllerTypeForIndex(index);
}
hid::detail::EInputDevice GetInputDevice() const
hid::EInputDevice GetInputDevice() const
{
switch (GetControllerType())
{
case SDL_CONTROLLER_TYPE_PS3:
case SDL_CONTROLLER_TYPE_PS4:
case SDL_CONTROLLER_TYPE_PS5:
return hid::detail::EInputDevice::PlayStation;
return hid::EInputDevice::PlayStation;
}
return hid::detail::EInputDevice::Xbox;
return hid::EInputDevice::Xbox;
}
void Close()
@@ -168,8 +169,17 @@ static void SetControllerInputDevice(Controller* controller)
if (App::s_isLoading)
return;
hid::detail::g_inputDevice = controller->GetInputDevice();
hid::detail::g_inputDeviceController = hid::detail::g_inputDevice;
hid::g_inputDevice = controller->GetInputDevice();
hid::g_inputDeviceController = hid::g_inputDevice;
auto controllerType = (hid::EInputDeviceExplicit)controller->GetControllerType();
if (hid::g_inputDeviceExplicit != controllerType)
{
hid::g_inputDeviceExplicit = controllerType;
LOGFN("Detected controller: {}", hid::GetInputDeviceName());
}
}
int HID_OnSDLEvent(void*, SDL_Event* event)
@@ -199,6 +209,7 @@ int HID_OnSDLEvent(void*, SDL_Event* event)
case SDL_CONTROLLERBUTTONDOWN:
case SDL_CONTROLLERBUTTONUP:
case SDL_CONTROLLERAXISMOTION:
case SDL_CONTROLLERTOUCHPADDOWN:
{
auto* controller = FindController(event->cdevice.which);
@@ -224,13 +235,13 @@ int HID_OnSDLEvent(void*, SDL_Event* event)
case SDL_KEYDOWN:
case SDL_KEYUP:
hid::detail::g_inputDevice = hid::detail::EInputDevice::Keyboard;
hid::g_inputDevice = hid::EInputDevice::Keyboard;
break;
case SDL_MOUSEMOTION:
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
hid::detail::g_inputDevice = hid::detail::EInputDevice::Mouse;
hid::g_inputDevice = hid::EInputDevice::Mouse;
break;
case SDL_WINDOWEVENT:
@@ -266,7 +277,7 @@ int HID_OnSDLEvent(void*, SDL_Event* event)
return 0;
}
void hid::detail::Init()
void hid::Init()
{
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS3, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4, "1");
@@ -282,7 +293,7 @@ void hid::detail::Init()
SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER);
}
uint32_t hid::detail::GetState(uint32_t dwUserIndex, XAMINPUT_STATE* pState)
uint32_t hid::GetState(uint32_t dwUserIndex, XAMINPUT_STATE* pState)
{
static uint32_t packet;
@@ -301,7 +312,7 @@ uint32_t hid::detail::GetState(uint32_t dwUserIndex, XAMINPUT_STATE* pState)
return ERROR_SUCCESS;
}
uint32_t hid::detail::SetState(uint32_t dwUserIndex, XAMINPUT_VIBRATION* pVibration)
uint32_t hid::SetState(uint32_t dwUserIndex, XAMINPUT_VIBRATION* pVibration)
{
if (!pVibration)
return ERROR_BAD_ARGUMENTS;
@@ -314,7 +325,7 @@ uint32_t hid::detail::SetState(uint32_t dwUserIndex, XAMINPUT_VIBRATION* pVibrat
return ERROR_SUCCESS;
}
uint32_t hid::detail::GetCapabilities(uint32_t dwUserIndex, XAMINPUT_CAPABILITIES* pCaps)
uint32_t hid::GetCapabilities(uint32_t dwUserIndex, XAMINPUT_CAPABILITIES* pCaps)
{
if (!pCaps)
return ERROR_BAD_ARGUMENTS;

View File

@@ -1,39 +1,74 @@
#include <stdafx.h>
#include "hid.h"
#include "hid_detail.h"
hid::detail::EInputDevice hid::detail::g_inputDevice;
hid::detail::EInputDevice hid::detail::g_inputDeviceController;
hid::EInputDevice hid::g_inputDevice;
hid::EInputDevice hid::g_inputDeviceController;
hid::EInputDeviceExplicit hid::g_inputDeviceExplicit;
uint16_t hid::detail::g_prohibitedButtons;
void hid::Init()
{
detail::Init();
}
uint16_t hid::g_prohibitedButtons;
void hid::SetProhibitedButtons(uint16_t wButtons)
{
hid::detail::g_prohibitedButtons = wButtons;
hid::g_prohibitedButtons = wButtons;
}
uint32_t hid::GetState(uint32_t dwUserIndex, XAMINPUT_STATE* pState)
bool hid::IsInputDeviceController()
{
return detail::GetState(dwUserIndex, pState);
return hid::g_inputDevice != hid::EInputDevice::Keyboard &&
hid::g_inputDevice != hid::EInputDevice::Mouse;
}
uint32_t hid::SetState(uint32_t dwUserIndex, XAMINPUT_VIBRATION* pVibration)
std::string hid::GetInputDeviceName()
{
return detail::SetState(dwUserIndex, pVibration);
}
switch (g_inputDevice)
{
case EInputDevice::Keyboard:
return "Keyboard";
uint32_t hid::GetCapabilities(uint32_t dwUserIndex, XAMINPUT_CAPABILITIES* pCaps)
{
return detail::GetCapabilities(dwUserIndex, pCaps);
}
case EInputDevice::Mouse:
return "Mouse";
}
bool hid::detail::IsInputDeviceController()
{
return hid::detail::g_inputDevice != hid::detail::EInputDevice::Keyboard &&
hid::detail::g_inputDevice != hid::detail::EInputDevice::Mouse;
switch (g_inputDeviceExplicit)
{
case EInputDeviceExplicit::Xbox360:
return "Xbox 360";
case EInputDeviceExplicit::XboxOne:
return "Xbox One";
case EInputDeviceExplicit::DualShock3:
return "DualShock 3";
case EInputDeviceExplicit::DualShock4:
return "DualShock 4";
case EInputDeviceExplicit::SwitchPro:
return "Nintendo Switch Pro";
case EInputDeviceExplicit::Virtual:
return "Virtual";
case EInputDeviceExplicit::DualSense:
return "DualSense";
case EInputDeviceExplicit::Luna:
return "Amazon Luna";
case EInputDeviceExplicit::Stadia:
return "Google Stadia";
case EInputDeviceExplicit::NvShield:
return "NVIDIA Shield";
case EInputDeviceExplicit::SwitchJCLeft:
return "Nintendo Switch Joy-Con (Left)";
case EInputDeviceExplicit::SwitchJCRight:
return "Nintendo Switch Joy-Con (Right)";
case EInputDeviceExplicit::SwitchJCPair:
return "Nintendo Switch Joy-Con (Pair)";
}
return "Unknown";
}

View File

@@ -1,13 +1,46 @@
#pragma once
union SDL_Event;
namespace hid
{
enum class EInputDevice
{
Keyboard,
Mouse,
Xbox,
PlayStation
};
enum class EInputDeviceExplicit
{
Unknown,
Xbox360,
XboxOne,
DualShock3,
DualShock4,
SwitchPro,
Virtual,
DualSense,
Luna,
Stadia,
NvShield,
SwitchJCLeft,
SwitchJCRight,
SwitchJCPair
};
extern EInputDevice g_inputDevice;
extern EInputDevice g_inputDeviceController;
extern EInputDeviceExplicit g_inputDeviceExplicit;
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);
bool IsInputDeviceController();
std::string GetInputDeviceName();
}

View File

@@ -1,25 +0,0 @@
#pragma once
namespace hid::detail
{
enum class EInputDevice
{
Keyboard,
Mouse,
Xbox,
PlayStation
};
extern EInputDevice g_inputDevice;
extern EInputDevice g_inputDeviceController;
extern uint16_t g_prohibitedButtons;
void Init();
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);
bool IsInputDeviceController();
}

View File

@@ -2,7 +2,6 @@
#include "xam.h"
#include "xdm.h"
#include <hid/hid.h>
#include <hid/hid_detail.h>
#include <ui/game_window.h>
#include <cpu/guest_thread.h>
#include <ranges>
@@ -11,12 +10,6 @@
#include <user/paths.h>
#include <SDL.h>
#ifdef _WIN32
#include <CommCtrl.h>
// Needed for commctrl
#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"")
#endif
struct XamListener : KernelObject
{
uint32_t id{};
@@ -208,11 +201,15 @@ bool XNotifyGetNext(uint32_t hNotification, uint32_t dwMsgFilter, be<uint32_t>*
uint32_t XamShowMessageBoxUI(uint32_t dwUserIndex, be<uint16_t>* wszTitle, be<uint16_t>* wszText, uint32_t cButtons,
xpointer<be<uint16_t>>* pwszButtons, uint32_t dwFocusButton, uint32_t dwFlags, be<uint32_t>* pResult, XXOVERLAPPED* pOverlapped)
{
int button{};
*pResult = cButtons ? cButtons - 1 : 0;
#ifdef _WIN32
#if _DEBUG
assert("XamShowMessageBoxUI encountered!" && false);
#elif _WIN32
// This code is Win32-only as it'll most likely crash, misbehave or
// cause corruption due to using a different type of memory than what
// wchar_t is on Linux. Windows uses 2 bytes while Linux uses 4 bytes.
std::vector<std::wstring> texts{};
std::vector<TASKDIALOG_BUTTON> buttons{};
texts.emplace_back(reinterpret_cast<wchar_t*>(wszTitle));
texts.emplace_back(reinterpret_cast<wchar_t*>(wszText));
@@ -226,23 +223,27 @@ uint32_t XamShowMessageBoxUI(uint32_t dwUserIndex, be<uint16_t>* wszTitle, be<ui
ByteSwapInplace(text[i]);
}
wprintf(L"[XamShowMessageBoxUI] !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
wprintf(L"[XamShowMessageBoxUI] If you are encountering this message and the game has ceased functioning,\n");
wprintf(L"[XamShowMessageBoxUI] please create an issue at https://github.com/hedge-dev/UnleashedRecomp/issues.\n");
wprintf(L"[XamShowMessageBoxUI] !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
wprintf(L"[XamShowMessageBoxUI] %ls\n", texts[0].c_str());
wprintf(L"[XamShowMessageBoxUI] %ls\n", texts[1].c_str());
wprintf(L"[XamShowMessageBoxUI] ");
for (size_t i = 0; i < cButtons; i++)
buttons.emplace_back(i, texts[2 + i].c_str());
{
wprintf(L"%ls", texts[2 + i].c_str());
XamNotifyEnqueueEvent(9, 1);
if (i != cButtons - 1)
wprintf(L" | ");
}
TASKDIALOGCONFIG config{};
config.cbSize = sizeof(config);
config.pszWindowTitle = texts[0].c_str();
config.pszContent = texts[1].c_str();
config.cButtons = cButtons;
config.pButtons = buttons.data();
TaskDialogIndirect(&config, &button, nullptr, nullptr);
wprintf(L"\n");
wprintf(L"[XamShowMessageBoxUI] Defaulted to button: %d\n", pResult->get());
wprintf(L"[XamShowMessageBoxUI] !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
#endif
*pResult = button;
if (pOverlapped)
{
pOverlapped->dwCompletionContext = GuestThread::GetCurrentThreadId();
@@ -453,7 +454,7 @@ uint32_t XamInputGetState(uint32_t userIndex, uint32_t flags, XAMINPUT_STATE* st
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_Y;
}
state->Gamepad.wButtons &= ~hid::detail::g_prohibitedButtons;
state->Gamepad.wButtons &= ~hid::g_prohibitedButtons;
ByteSwapInplace(state->Gamepad.wButtons);
ByteSwapInplace(state->Gamepad.sThumbLX);
@@ -466,7 +467,7 @@ uint32_t XamInputGetState(uint32_t userIndex, uint32_t flags, XAMINPUT_STATE* st
uint32_t XamInputSetState(uint32_t userIndex, uint32_t flags, XAMINPUT_VIBRATION* vibration)
{
if (!hid::detail::IsInputDeviceController() || !Config::Vibration)
if (!hid::IsInputDeviceController() || !Config::Vibration)
return ERROR_SUCCESS;
ByteSwapInplace(vibration->wLeftMotorSpeed);

View File

@@ -19,10 +19,10 @@
*/
#define CONFIG_DEFINE_LOCALE(name) \
CONFIG_LOCALE Config::g_##name##_locale =
CONFIG_LOCALE g_##name##_locale =
#define CONFIG_DEFINE_ENUM_LOCALE(type) \
CONFIG_ENUM_LOCALE(type) Config::g_##type##_locale =
CONFIG_ENUM_LOCALE(type) g_##type##_locale =
CONFIG_DEFINE_LOCALE(Language)
{
@@ -117,11 +117,6 @@ CONFIG_DEFINE_LOCALE(AllowBackgroundInput)
{ ELanguage::English, { "Allow Background Input", "Allow controller input whilst the game window is unfocused." } }
};
CONFIG_DEFINE_LOCALE(AllowDPadMovement)
{
{ ELanguage::English, { "Allow D-Pad Movement", "Allow the player to also be controlled using the directional pad." } }
};
CONFIG_DEFINE_LOCALE(MasterVolume)
{
{ ELanguage::English, { "Master Volume", "Adjust the overall volume." } }
@@ -142,6 +137,22 @@ CONFIG_DEFINE_LOCALE(MusicAttenuation)
{ ELanguage::English, { "Music Attenuation", "Fade out the game's music when external media is playing." } }
};
CONFIG_DEFINE_LOCALE(ChannelConfiguration)
{
{ ELanguage::English, { "Channel Configuration", "" } }
};
CONFIG_DEFINE_ENUM_LOCALE(EChannelConfiguration)
{
{
ELanguage::English,
{
{ EChannelConfiguration::Stereo, { "STEREO", "" } },
{ EChannelConfiguration::Surround, { "SURROUND", "" } }
}
}
};
CONFIG_DEFINE_LOCALE(VoiceLanguage)
{
{ ELanguage::English, { "Voice Language", "Change the language used for character voices." } }

View File

@@ -342,6 +342,20 @@ std::unordered_map<std::string, std::unordered_map<ELanguage, std::string>> g_lo
{ ELanguage::Italian, "Sei sicuro di voler uscire?" }
}
},
{
// Notes: message appears when the SYS-DATA is corrupted (mismatching file size).
// 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",
{
{ ELanguage::English, "The save file appears to be\ncorrupted and cannot be loaded." },
{ ELanguage::Japanese, "ゲームデータの読み込みに失敗しました。\nこのまま続けるとゲームデータをセーブすることはできません" },
{ ELanguage::German, "Diese Speicherdatei ist beschädigt\nund kann nicht geladen werden." },
{ ELanguage::French, "Le fichier de sauvegarde semble être\nendommagé et ne peut être chargé." },
{ ELanguage::Spanish, "El archivo parece estar dañado\ny no se puede cargar." },
{ ELanguage::Italian, "I file di salvataggio sembrano danneggiati\ne non possono essere caricati." }
}
},
{
"Common_On",
{

View File

@@ -26,9 +26,6 @@ static std::vector<Mod> g_mods;
std::filesystem::path ModLoader::ResolvePath(std::string_view path)
{
if (g_mods.empty())
return {};
std::string_view root;
size_t sepIndex = path.find(":\\");
@@ -51,6 +48,9 @@ std::filesystem::path ModLoader::ResolvePath(std::string_view path)
return {};
}
if (g_mods.empty())
return {};
thread_local xxHashMap<std::filesystem::path> s_cache;
XXH64_hash_t hash = XXH3_64bits(path.data(), path.size());

View File

@@ -1,10 +1,10 @@
#include <os/logger_detail.h>
#include <os/logger.h>
void os::logger::detail::Init()
void os::logger::Init()
{
}
void os::logger::detail::Log(const std::string_view str, detail::ELogType type, const char* func)
void os::logger::Log(const std::string_view str, ELogType type, const char* func)
{
if (func)
{

View File

@@ -1,6 +1,6 @@
#include <os/media_detail.h>
#include <os/media.h>
bool os::media::detail::IsExternalMediaPlaying()
bool os::media::IsExternalMediaPlaying()
{
// This functionality is not supported in Linux.
return false;

View File

@@ -1,8 +1,8 @@
#include <os/process_detail.h>
#include <os/process.h>
#include <signal.h>
std::filesystem::path os::process::detail::GetExecutablePath()
std::filesystem::path os::process::GetExecutablePath()
{
char exePath[PATH_MAX] = {};
if (readlink("/proc/self/exe", exePath, PATH_MAX) > 0)
@@ -15,7 +15,7 @@ std::filesystem::path os::process::detail::GetExecutablePath()
}
}
std::filesystem::path os::process::detail::GetWorkingDirectory()
std::filesystem::path os::process::GetWorkingDirectory()
{
char cwd[PATH_MAX] = {};
char *res = getcwd(cwd, sizeof(cwd));
@@ -29,7 +29,7 @@ std::filesystem::path os::process::detail::GetWorkingDirectory()
}
}
bool os::process::detail::StartProcess(const std::filesystem::path path, const std::vector<std::string> args, std::filesystem::path work)
bool os::process::StartProcess(const std::filesystem::path& path, const std::vector<std::string>& args, std::filesystem::path work)
{
pid_t pid = fork();
if (pid < 0)

View File

@@ -1,6 +1,6 @@
#include <os/user_detail.h>
#include <os/user.h>
bool os::user::detail::IsDarkTheme()
bool os::user::IsDarkTheme()
{
return false;
}

View File

@@ -1,7 +1,7 @@
#include <os/version_detail.h>
#include <os/version.h>
os::version::detail::OSVersion os::version::detail::GetOSVersion()
os::version::OSVersion os::version::GetOSVersion()
{
assert(false && "Unimplemented.");
return os::version::detail::OSVersion();
return os::version::OSVersion();
}

View File

@@ -1,12 +0,0 @@
#include <os/logger.h>
#include <os/logger_detail.h>
void os::logger::Init()
{
detail::Init();
}
void os::logger::Log(const std::string_view str, detail::ELogType type, const char* func)
{
detail::Log(str, type, func);
}

View File

@@ -1,9 +1,9 @@
#pragma once
#include <os/logger_detail.h>
#include <source_location>
#define LOG_IMPL(type, func, str) os::logger::Log(str, os::logger::detail::ELogType::type, func)
#define LOGF_IMPL(type, func, str, ...) os::logger::Log(fmt::format(str, __VA_ARGS__), os::logger::detail::ELogType::type, func)
#define LOG_IMPL(type, func, str) os::logger::Log(str, os::logger::ELogType::type, func)
#define LOGF_IMPL(type, func, str, ...) os::logger::Log(fmt::format(str, __VA_ARGS__), os::logger::ELogType::type, func)
// Function-specific logging.
@@ -51,6 +51,14 @@
namespace os::logger
{
enum class ELogType
{
None,
Utility,
Warning,
Error
};
void Init();
void Log(const std::string_view str, detail::ELogType type = detail::ELogType::None, const char* func = nullptr);
void Log(const std::string_view str, ELogType type = ELogType::None, const char* func = nullptr);
}

View File

@@ -1,17 +0,0 @@
#pragma once
#include <source_location>
namespace os::logger::detail
{
enum class ELogType
{
None,
Utility,
Warning,
Error
};
void Init();
void Log(const std::string_view str, ELogType type = ELogType::None, const char* func = nullptr);
}

View File

@@ -1,7 +0,0 @@
#include <os/media.h>
#include <os/media_detail.h>
bool os::media::IsExternalMediaPlaying()
{
return detail::IsExternalMediaPlaying();
}

View File

@@ -1,6 +0,0 @@
#pragma once
namespace os::media::detail
{
bool IsExternalMediaPlaying();
}

View File

@@ -1,17 +0,0 @@
#include <os/process.h>
#include <os/process_detail.h>
std::filesystem::path os::process::GetExecutablePath()
{
return detail::GetExecutablePath();
}
std::filesystem::path os::process::GetWorkingDirectory()
{
return detail::GetWorkingDirectory();
}
bool os::process::StartProcess(const std::filesystem::path path, const std::vector<std::string> args, std::filesystem::path work)
{
return detail::StartProcess(path, args, work);
}

View File

@@ -4,5 +4,5 @@ namespace os::process
{
std::filesystem::path GetExecutablePath();
std::filesystem::path GetWorkingDirectory();
bool StartProcess(const std::filesystem::path path, const std::vector<std::string> args, std::filesystem::path work = {});
bool StartProcess(const std::filesystem::path& path, const std::vector<std::string>& args, std::filesystem::path work = {});
}

View File

@@ -1,8 +0,0 @@
#pragma once
namespace os::process::detail
{
std::filesystem::path GetExecutablePath();
std::filesystem::path GetWorkingDirectory();
bool StartProcess(const std::filesystem::path path, const std::vector<std::string> args, std::filesystem::path work = {});
}

View File

@@ -1,7 +0,0 @@
#include <os/user.h>
#include <os/user_detail.h>
bool os::user::IsDarkTheme()
{
return detail::IsDarkTheme();
}

View File

@@ -1,6 +0,0 @@
#pragma once
namespace os::user::detail
{
bool IsDarkTheme();
}

View File

@@ -1,7 +0,0 @@
#include <os/version.h>
#include <os/version_detail.h>
os::version::detail::OSVersion os::version::GetOSVersion()
{
return detail::GetOSVersion();
}

View File

@@ -1,8 +1,13 @@
#pragma once
#include <os/version_detail.h>
namespace os::version
{
detail::OSVersion GetOSVersion();
struct OSVersion
{
uint32_t Major{};
uint32_t Minor{};
uint32_t Build{};
};
OSVersion GetOSVersion();
}

View File

@@ -1,13 +0,0 @@
#pragma once
namespace os::version::detail
{
struct OSVersion
{
uint32_t Major{};
uint32_t Minor{};
uint32_t Build{};
};
OSVersion GetOSVersion();
}

View File

@@ -1,17 +1,16 @@
#include <os/logger_detail.h>
#include <print>
#include <os/logger.h>
#define FOREGROUND_WHITE (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE)
#define FOREGROUND_YELLOW (FOREGROUND_RED | FOREGROUND_GREEN)
HANDLE g_hStandardOutput;
void os::logger::detail::Init()
void os::logger::Init()
{
g_hStandardOutput = GetStdHandle(STD_OUTPUT_HANDLE);
}
void os::logger::detail::Log(const std::string_view str, detail::ELogType type, const char* func)
void os::logger::Log(const std::string_view str, ELogType type, const char* func)
{
switch (type)
{

View File

@@ -1,4 +1,4 @@
#include <os/media_detail.h>
#include <os/media.h>
#include <os/logger.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Media.Control.h>
@@ -62,7 +62,7 @@ static GlobalSystemMediaTransportControlsSessionPlaybackInfo GetPlaybackInfo()
}
}
bool os::media::detail::IsExternalMediaPlaying()
bool os::media::IsExternalMediaPlaying()
{
auto playbackInfo = GetPlaybackInfo();

View File

@@ -1,6 +1,6 @@
#include <os/process_detail.h>
#include <os/process.h>
std::filesystem::path os::process::detail::GetExecutablePath()
std::filesystem::path os::process::GetExecutablePath()
{
WCHAR exePath[MAX_PATH];
@@ -10,7 +10,7 @@ std::filesystem::path os::process::detail::GetExecutablePath()
return std::filesystem::path(exePath);
}
std::filesystem::path os::process::detail::GetWorkingDirectory()
std::filesystem::path os::process::GetWorkingDirectory()
{
WCHAR workPath[MAX_PATH];
@@ -20,7 +20,7 @@ std::filesystem::path os::process::detail::GetWorkingDirectory()
return std::filesystem::path(workPath);
}
bool os::process::detail::StartProcess(const std::filesystem::path path, const std::vector<std::string> args, std::filesystem::path work)
bool os::process::StartProcess(const std::filesystem::path& path, const std::vector<std::string>& args, std::filesystem::path work)
{
if (path.empty())
return false;

View File

@@ -1,6 +1,6 @@
#include <os/user_detail.h>
#include <os/user.h>
bool os::user::detail::IsDarkTheme()
bool os::user::IsDarkTheme()
{
HKEY hKey;

View File

@@ -1,10 +1,10 @@
#include <os/version_detail.h>
#include <os/version.h>
LIB_FUNCTION(LONG, "ntdll.dll", RtlGetVersion, PRTL_OSVERSIONINFOW);
os::version::detail::OSVersion os::version::detail::GetOSVersion()
os::version::OSVersion os::version::GetOSVersion()
{
auto result = os::version::detail::OSVersion{};
auto result = os::version::OSVersion{};
OSVERSIONINFOEXW osvi = { 0 };
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);

View File

@@ -0,0 +1,14 @@
#include <api/SWA.h>
#include <patches/ui/CTitleStateIntro_patches.h>
// SWA::CGameModeStageTitle::Update
PPC_FUNC_IMPL(__imp__sub_825518B8);
PPC_FUNC(sub_825518B8)
{
auto pGameModeStageTitle = (SWA::CGameModeStageTitle*)g_memory.Translate(ctx.r3.u32);
__imp__sub_825518B8(ctx, base);
if (g_quitMessageOpen)
pGameModeStageTitle->m_AdvertiseMovieWaitTime = 0;
}

View File

@@ -185,13 +185,13 @@ void AspectRatioPatches::ComputeOffsets()
float height = Video::s_viewportHeight;
g_aspectRatio = width / height;
g_aspectRatioScale = 1.0f;
g_aspectRatioGameplayScale = 1.0f;
if (g_aspectRatio >= NARROW_ASPECT_RATIO)
{
// height is locked to 720 in this case
g_aspectRatioOffsetX = 0.5f * (g_aspectRatio * 720.0f - 1280.0f);
g_aspectRatioOffsetX = (width - height * WIDE_ASPECT_RATIO) / 2.0f;
g_aspectRatioOffsetY = 0.0f;
g_aspectRatioScale = height / 720.0f;
// keep same scale above Steam Deck aspect ratio
if (g_aspectRatio < WIDE_ASPECT_RATIO)
@@ -201,20 +201,29 @@ void AspectRatioPatches::ComputeOffsets()
float narrowScale = ComputeScale(NARROW_ASPECT_RATIO);
float lerpFactor = std::clamp((g_aspectRatio - NARROW_ASPECT_RATIO) / (STEAM_DECK_ASPECT_RATIO - NARROW_ASPECT_RATIO), 0.0f, 1.0f);
g_aspectRatioScale = narrowScale + (steamDeckScale - narrowScale) * lerpFactor;
g_aspectRatioGameplayScale = narrowScale + (steamDeckScale - narrowScale) * lerpFactor;
}
}
else
{
// width is locked to 960 in this case to have 4:3 crop
g_aspectRatioOffsetX = 0.5f * (960.0f - 1280.0f);
g_aspectRatioOffsetY = 0.5f * (960.0f / g_aspectRatio - 720.0f);
g_aspectRatioScale = ComputeScale(NARROW_ASPECT_RATIO);
// 4:3 crop
g_aspectRatioOffsetX = (width - width * NARROW_ASPECT_RATIO) / 2.0f;
g_aspectRatioOffsetY = (height - width / NARROW_ASPECT_RATIO) / 2.0f;
g_aspectRatioScale = width / 960.0f;
g_aspectRatioGameplayScale = ComputeScale(NARROW_ASPECT_RATIO);
}
g_narrowOffsetScale = std::clamp((g_aspectRatio - NARROW_ASPECT_RATIO) / (WIDE_ASPECT_RATIO - NARROW_ASPECT_RATIO), 0.0f, 1.0f);
g_aspectRatioNarrowScale = std::clamp((g_aspectRatio - NARROW_ASPECT_RATIO) / (WIDE_ASPECT_RATIO - NARROW_ASPECT_RATIO), 0.0f, 1.0f);
}
static void GetViewport(void* application, be<uint32_t>* width, be<uint32_t>* height)
{
*width = 1280;
*height = 720;
}
GUEST_FUNCTION_HOOK(sub_82E169B8, GetViewport);
// SWA::CGameDocument::ComputeScreenPosition
PPC_FUNC_IMPL(__imp__sub_8250FC70);
PPC_FUNC(sub_8250FC70)
@@ -222,14 +231,15 @@ PPC_FUNC(sub_8250FC70)
__imp__sub_8250FC70(ctx, base);
auto position = reinterpret_cast<be<float>*>(base + ctx.r3.u32);
position[0] = position[0] - g_aspectRatioOffsetX;
position[1] = position[1] - g_aspectRatioOffsetY;
position[0] = (position[0] / 1280.0f * Video::s_viewportWidth - g_aspectRatioOffsetX) / g_aspectRatioScale;
position[1] = (position[1] / 720.0f * Video::s_viewportHeight - g_aspectRatioOffsetY) / g_aspectRatioScale;
}
void ComputeScreenPositionMidAsmHook(PPCRegister& f1, PPCRegister& f2)
{
f1.f64 -= g_aspectRatioOffsetX;
f2.f64 -= g_aspectRatioOffsetY;
f1.f64 = (f1.f64 / 1280.0 * Video::s_viewportWidth - g_aspectRatioOffsetX) / g_aspectRatioScale;
f2.f64 = (f2.f64 / 720.0 * Video::s_viewportHeight - g_aspectRatioOffsetY) / g_aspectRatioScale;
}
void WorldMapInfoMidAsmHook(PPCRegister& r4)
@@ -258,11 +268,11 @@ PPC_FUNC(sub_8258B558)
if (scene != NULL)
{
ctx.r3.u32 = scene;
ctx.f1.f64 = offsetX + g_narrowOffsetScale * 140.0f;
ctx.f1.f64 = offsetX + g_aspectRatioNarrowScale * 140.0f;
ctx.f2.f64 = offsetY;
if (Config::UIScaleMode == EUIScaleMode::Edge && g_narrowOffsetScale >= 1.0f)
ctx.f1.f64 += g_aspectRatioOffsetX;
if (Config::UIScaleMode == EUIScaleMode::Edge && g_aspectRatioNarrowScale >= 1.0f)
ctx.f1.f64 += g_aspectRatioOffsetX / g_aspectRatioScale;
sub_830BB3D0(ctx, base);
}
@@ -282,9 +292,9 @@ PPC_FUNC(sub_8258B558)
uint32_t textBox = PPC_LOAD_U32(menuTextBox + 0x4);
if (textBox != NULL)
{
float value = 708.0f + g_narrowOffsetScale * 140.0f;
if (Config::UIScaleMode == EUIScaleMode::Edge && g_narrowOffsetScale >= 1.0f)
value += g_aspectRatioOffsetX;
float value = 708.0f + g_aspectRatioNarrowScale * 140.0f;
if (Config::UIScaleMode == EUIScaleMode::Edge && g_aspectRatioNarrowScale >= 1.0f)
value += g_aspectRatioOffsetX / g_aspectRatioScale;
PPC_STORE_U32(textBox + 0x38, reinterpret_cast<uint32_t&>(value));
}
@@ -740,8 +750,6 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str
modifier.flags &= ~(ALIGN_LEFT | ALIGN_RIGHT);
}
auto backBuffer = Video::GetBackBuffer();
uint32_t size = ctx.r5.u32 * stride;
ctx.r1.u32 -= size;
@@ -755,10 +763,12 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str
if ((modifier.flags & STRETCH_HORIZONTAL) != 0)
{
scaleX = backBuffer->width / 1280.0f;
scaleX = Video::s_viewportWidth / 1280.0f;
}
else
{
scaleX = g_aspectRatioScale;
if ((modifier.flags & ALIGN_RIGHT) != 0)
offsetX = g_aspectRatioOffsetX * 2.0f;
else if ((modifier.flags & ALIGN_LEFT) == 0)
@@ -766,27 +776,29 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str
if ((modifier.flags & SCALE) != 0)
{
scaleX = g_aspectRatioScale;
scaleX *= g_aspectRatioGameplayScale;
if ((modifier.flags & ALIGN_RIGHT) != 0)
offsetX += 1280.0f * (1.0f - scaleX);
offsetX += 1280.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale;
else if ((modifier.flags & ALIGN_LEFT) == 0)
offsetX += 640.0f * (1.0f - scaleX);
offsetX += 640.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale;
}
if ((modifier.flags & WORLD_MAP) != 0)
{
if ((modifier.flags & ALIGN_LEFT) != 0)
offsetX += (1.0f - g_narrowOffsetScale) * -20.0f;
offsetX += (1.0f - g_aspectRatioNarrowScale) * g_aspectRatioScale * -20.0f;
}
}
if ((modifier.flags & STRETCH_VERTICAL) != 0)
{
scaleY = backBuffer->height / 720.0f;
scaleY = Video::s_viewportHeight / 720.0f;
}
else
{
scaleY = g_aspectRatioScale;
if ((modifier.flags & ALIGN_BOTTOM) != 0)
offsetY = g_aspectRatioOffsetY * 2.0f;
else if ((modifier.flags & ALIGN_TOP) == 0)
@@ -794,12 +806,12 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str
if ((modifier.flags & SCALE) != 0)
{
scaleY = g_aspectRatioScale;
scaleY *= g_aspectRatioGameplayScale;
if ((modifier.flags & ALIGN_BOTTOM) != 0)
offsetY += 720.0f * (1.0f - scaleY);
offsetY += 720.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale;
else if ((modifier.flags & ALIGN_TOP) == 0)
offsetY += 360.0f * (1.0f - scaleY);
offsetY += 360.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale;
}
}
@@ -836,7 +848,7 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str
if ((offsetScaleModifier.flags & OFFSET_SCALE_LEFT) != 0)
offsetX *= corner / offsetScaleModifier.cornerMax;
else if ((offsetScaleModifier.flags & OFFSET_SCALE_RIGHT) != 0)
offsetX = 1280.0f - (1280.0f - offsetX) * (1280.0f - corner) / (1280.0f - offsetScaleModifier.cornerMax);
offsetX = Video::s_viewportWidth - (Video::s_viewportWidth - offsetX) * (1280.0f - corner) / (1280.0f - offsetScaleModifier.cornerMax);
}
for (size_t i = 0; i < ctx.r5.u32; i++)
@@ -852,11 +864,11 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str
}
else if ((modifier.flags & EXTEND_RIGHT) != 0 && (i == 2 || i == 3))
{
x = std::max(x, float(backBuffer->width));
x = std::max(x, float(Video::s_viewportWidth));
}
position[0] = round(x / backBuffer->width * Video::s_viewportWidth) / Video::s_viewportWidth * backBuffer->width;
position[1] = round(y / backBuffer->height * Video::s_viewportHeight) / Video::s_viewportHeight * backBuffer->height;
position[0] = round(x);
position[1] = round(y);
}
ctx.r4.u32 = ctx.r1.u32;
@@ -882,13 +894,12 @@ PPC_FUNC(sub_825E2E88)
PPC_FUNC_IMPL(__imp__sub_82E16C70);
PPC_FUNC(sub_82E16C70)
{
auto backBuffer = Video::GetBackBuffer();
auto scissorRect = reinterpret_cast<GuestRect*>(base + ctx.r4.u32);
scissorRect->left = scissorRect->left + g_aspectRatioOffsetX;
scissorRect->top = scissorRect->top + g_aspectRatioOffsetY;
scissorRect->right = scissorRect->right + g_aspectRatioOffsetX;
scissorRect->bottom = scissorRect->bottom + g_aspectRatioOffsetY;
scissorRect->left = scissorRect->left * g_aspectRatioScale + g_aspectRatioOffsetX;
scissorRect->top = scissorRect->top * g_aspectRatioScale + g_aspectRatioOffsetY;
scissorRect->right = scissorRect->right * g_aspectRatioScale + g_aspectRatioOffsetX;
scissorRect->bottom = scissorRect->bottom * g_aspectRatioScale + g_aspectRatioOffsetY;
__imp__sub_82E16C70(ctx, base);
}
@@ -926,66 +937,77 @@ PPC_FUNC(sub_830D1EF0)
__imp__sub_830D1EF0(ctx, base);
if (!PPC_LOAD_U8(r3.u32 + PRIMITIVE_2D_PADDING_OFFSET))
struct Vertex
{
auto backBuffer = Video::GetBackBuffer();
be<float> x;
be<float> y;
be<float> z;
be<float> w;
be<uint32_t> color;
be<float> u;
be<float> v;
};
struct Vertex
auto vertex = reinterpret_cast<Vertex*>(base + ctx.r4.u32);
for (size_t i = 0; i < 4; i++)
{
float x = vertex[i].x * 640.0f + 640.0f;
float y = vertex[i].y * -360.0f + 360.0f;
if (PPC_LOAD_U8(r3.u32 + PRIMITIVE_2D_PADDING_OFFSET))
{
be<float> x;
be<float> y;
be<float> z;
be<float> w;
be<uint32_t> color;
be<float> u;
be<float> v;
};
auto vertex = reinterpret_cast<Vertex*>(base + ctx.r4.u32);
for (size_t i = 0; i < 4; i++)
// Stretch
x = ((x + 0.5f) / 1280.0f) * Video::s_viewportWidth;
y = ((y + 0.5f) / 720.0f) * Video::s_viewportHeight;
}
else
{
vertex[i].x = vertex[i].x * 1280.0f / backBuffer->width;
vertex[i].y = vertex[i].y * 720.0f / backBuffer->height;
// Center
x = g_aspectRatioOffsetX + (x + 0.5f) * g_aspectRatioScale;
y = g_aspectRatioOffsetY + (y + 0.5f) * g_aspectRatioScale;
}
bool letterboxTop = PPC_LOAD_U8(r3.u32 + PRIMITIVE_2D_PADDING_OFFSET + 0x1);
bool letterboxBottom = PPC_LOAD_U8(r3.u32 + PRIMITIVE_2D_PADDING_OFFSET + 0x2);
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;
}
if (letterboxTop || letterboxBottom)
bool letterboxTop = PPC_LOAD_U8(r3.u32 + PRIMITIVE_2D_PADDING_OFFSET + 0x1);
bool letterboxBottom = PPC_LOAD_U8(r3.u32 + PRIMITIVE_2D_PADDING_OFFSET + 0x2);
if (letterboxTop || letterboxBottom)
{
float halfPixelX = 1.0f / Video::s_viewportWidth;
float halfPixelY = 1.0f / Video::s_viewportHeight;
if (letterboxTop)
{
float halfPixelX = 1.0f / backBuffer->width;
float halfPixelY = 1.0f / backBuffer->height;
vertex[0].x = -1.0f - halfPixelX;
vertex[0].y = 1.0f + halfPixelY;
if (letterboxTop)
{
vertex[0].x = -1.0f - halfPixelX;
vertex[0].y = 1.0f + halfPixelY;
vertex[1].x = 1.0f - halfPixelX;
vertex[1].y = 1.0f + halfPixelY;
vertex[2].x = -1.0f - halfPixelX;
// vertex[2].y untouched
vertex[1].x = 1.0f - halfPixelX;
vertex[1].y = 1.0f + halfPixelY;
vertex[3].x = 1.0f - halfPixelX;
// vertex[3].y untouched
}
else if (letterboxBottom)
{
vertex[0].x = -1.0f - halfPixelX;
// vertex[0].y untouched
vertex[2].x = -1.0f - halfPixelX;
// vertex[2].y untouched
vertex[1].x = 1.0f - halfPixelX;
// vertex[1].y untouched
vertex[3].x = 1.0f - halfPixelX;
// vertex[3].y untouched
}
else if (letterboxBottom)
{
vertex[0].x = -1.0f - halfPixelX;
// vertex[0].y untouched
vertex[2].x = -1.0f - halfPixelX;
vertex[2].y = -1.0f + halfPixelY;
vertex[1].x = 1.0f - halfPixelX;
// vertex[1].y untouched
vertex[3].x = 1.0f - halfPixelX;
vertex[3].y = -1.0f + halfPixelY;
}
}
vertex[2].x = -1.0f - halfPixelX;
vertex[2].y = -1.0f + halfPixelY;
vertex[3].x = 1.0f - halfPixelX;
vertex[3].y = -1.0f + halfPixelY;
}
}
}
@@ -1018,8 +1040,9 @@ static double ComputeObjGetItemX(uint32_t type)
x = 1058.0;
x *= g_aspectRatioScale;
x *= g_aspectRatioGameplayScale;
double scaleOffset = (1280.0 * (1.0 - g_aspectRatioScale));
double scaleOffset = (1280.0 * (1.0 - g_aspectRatioGameplayScale)) * g_aspectRatioScale;
if (Config::UIScaleMode == EUIScaleMode::Edge)
{
@@ -1031,8 +1054,7 @@ static double ComputeObjGetItemX(uint32_t type)
x += g_aspectRatioOffsetX + scaleOffset;
}
auto backBuffer = Video::GetBackBuffer();
return (x - (0.5 * backBuffer->width)) / (0.5 * backBuffer->height) * OBJ_GET_ITEM_TANGENT;
return (x - (0.5 * Video::s_viewportWidth)) / (0.5 * Video::s_viewportHeight) * OBJ_GET_ITEM_TANGENT;
}
return 0.0;
@@ -1066,10 +1088,10 @@ static double ComputeObjGetItemY(uint32_t type)
y = 582.0;
y *= g_aspectRatioScale;
y += g_aspectRatioOffsetY * 2.0 + 720.0 * (1.0 - g_aspectRatioScale);
y *= g_aspectRatioGameplayScale;
y += g_aspectRatioOffsetY * 2.0 + 720.0 * (1.0 - g_aspectRatioGameplayScale) * g_aspectRatioScale;
auto backBuffer = Video::GetBackBuffer();
return ((0.5 * backBuffer->height) - y) / (0.5 * backBuffer->height) * OBJ_GET_ITEM_TANGENT;
return ((0.5 * Video::s_viewportHeight) - y) / (0.5 * Video::s_viewportHeight) * OBJ_GET_ITEM_TANGENT;
}
return 0.25;
@@ -1133,12 +1155,12 @@ PPC_FUNC(sub_82B8AA40)
PPC_STORE_U8(ctx.r3.u32, shouldDrawLetterbox);
if (shouldDrawLetterbox)
{
auto backBuffer = Video::GetBackBuffer();
uint32_t height = std::min(720u, backBuffer->height);
float aspectRatio = std::max(NARROW_ASPECT_RATIO, g_aspectRatio);
uint32_t width = aspectRatio * 720;
PPC_STORE_U32(ctx.r3.u32 + 0xC, backBuffer->width);
PPC_STORE_U32(ctx.r3.u32 + 0x10, height);
PPC_STORE_U32(ctx.r3.u32 + 0x14, (height - backBuffer->width * 9 / 16) / 2);
PPC_STORE_U32(ctx.r3.u32 + 0xC, width);
PPC_STORE_U32(ctx.r3.u32 + 0x10, 720);
PPC_STORE_U32(ctx.r3.u32 + 0x14, (720 - width * 9 / 16) / 2);
}
__imp__sub_82B8AA40(ctx, base);
@@ -1161,5 +1183,5 @@ void InspireSubtitleMidAsmHook(PPCRegister& r3)
constexpr float NARROW_OFFSET = 485.0f;
constexpr float WIDE_OFFSET = 560.0f;
*reinterpret_cast<be<float>*>(g_memory.base + r3.u32 + 0x3C) = NARROW_OFFSET + (WIDE_OFFSET - NARROW_OFFSET) * g_narrowOffsetScale;
*reinterpret_cast<be<float>*>(g_memory.base + r3.u32 + 0x3C) = NARROW_OFFSET + (WIDE_OFFSET - NARROW_OFFSET) * g_aspectRatioNarrowScale;
}

View File

@@ -8,7 +8,8 @@ inline float g_aspectRatio;
inline float g_aspectRatioOffsetX;
inline float g_aspectRatioOffsetY;
inline float g_aspectRatioScale;
inline float g_narrowOffsetScale;
inline float g_aspectRatioGameplayScale;
inline float g_aspectRatioNarrowScale;
struct AspectRatioPatches
{

View File

@@ -0,0 +1,348 @@
#include <api/SWA.h>
#include <hid/hid.h>
#include <ui/sdl_listener.h>
#include <app.h>
#include <exports.h>
class WorldMapTouchParams
{
public:
float CancelDeadzone{ 0.31f };
float Damping{ 0.99f };
float FlickAccelX{ 0.25f };
float FlickAccelY{ 0.1f };
float FlickTerminalVelocity{ 40.0f };
float FlickThreshold{ 2.25f };
float SensitivityX{};
float SensitivityY{};
float Smoothing{ 0.8f };
};
class WorldMapTouchParamsProspero : public WorldMapTouchParams
{
public:
WorldMapTouchParamsProspero()
{
SensitivityX = 1.15f;
SensitivityY = 1.05f;
}
}
g_worldMapTouchParamsProspero;
class WorldMapTouchParamsOrbis : public WorldMapTouchParams
{
public:
WorldMapTouchParamsOrbis()
{
SensitivityX = 0.95f;
SensitivityY = 1.0f;
}
}
g_worldMapTouchParamsOrbis;
WorldMapTouchParams g_worldMapTouchParams{};
static bool g_isTouchActive;
static float g_worldMapTouchVelocityX;
static float g_worldMapTouchVelocityY;
class SDLEventListenerForInputPatches : public SDLEventListener
{
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;
public:
static void Update(float deltaTime)
{
/* NOTE (Hyper): this code was written at 144Hz and was
discovered later to be faulty at any other frame rate,
so this is here to account for that without changing
all the constants that I had tuned. */
constexpr auto referenceDeltaTime = 1.0f / 144.0f;
if (g_isTouchActive)
{
auto dxNorm = ms_touchpadDeltaX / referenceDeltaTime;
auto dyNorm = ms_touchpadDeltaY / referenceDeltaTime;
auto dxSens = dxNorm * g_worldMapTouchParams.SensitivityX;
auto dySens = dyNorm * g_worldMapTouchParams.SensitivityY;
auto smoothing = powf(g_worldMapTouchParams.Smoothing, deltaTime / referenceDeltaTime);
g_worldMapTouchVelocityX = smoothing * g_worldMapTouchVelocityX + (1.0f - smoothing) * dxSens;
g_worldMapTouchVelocityY = smoothing * g_worldMapTouchVelocityY + (1.0f - smoothing) * dySens;
auto flickThreshold = g_worldMapTouchParams.FlickThreshold;
if (fabs(dxSens) > flickThreshold || fabs(dySens) > flickThreshold)
{
g_worldMapTouchVelocityX += dxNorm * g_worldMapTouchParams.FlickAccelX * (deltaTime / referenceDeltaTime);
g_worldMapTouchVelocityY += dyNorm * g_worldMapTouchParams.FlickAccelY * (deltaTime / referenceDeltaTime);
}
auto terminalVelocity = g_worldMapTouchParams.FlickTerminalVelocity;
g_worldMapTouchVelocityX = std::clamp(g_worldMapTouchVelocityX, -terminalVelocity, terminalVelocity);
g_worldMapTouchVelocityY = std::clamp(g_worldMapTouchVelocityY, -terminalVelocity, terminalVelocity);
}
else
{
auto dampingFactor = powf(g_worldMapTouchParams.Damping, deltaTime / referenceDeltaTime);
g_worldMapTouchVelocityX *= dampingFactor;
g_worldMapTouchVelocityY *= dampingFactor;
}
}
void OnSDLEvent(SDL_Event* event) override
{
switch (event->type)
{
case SDL_CONTROLLERTOUCHPADMOTION:
{
g_isTouchActive = true;
if (ms_touchpadFingerCount > 1)
{
g_isTouchActive = 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;
break;
}
case SDL_CONTROLLERTOUCHPADDOWN:
{
g_worldMapTouchParams = hid::g_inputDeviceExplicit == hid::EInputDeviceExplicit::DualSense
? (WorldMapTouchParams)g_worldMapTouchParamsProspero
: (WorldMapTouchParams)g_worldMapTouchParamsOrbis;
ms_touchpadFingerCount++;
ms_touchpadPrevX = event->ctouchpad.x;
ms_touchpadPrevY = event->ctouchpad.y;
break;
}
case SDL_CONTROLLERTOUCHPADUP:
g_isTouchActive = false;
ms_touchpadFingerCount--;
break;
}
}
}
g_sdlEventListenerForInputPatches;
// -------------- COMMON --------------- //
static bool IsDPadActive(SWA::SPadState* pPadState)
{
return pPadState->IsDown(SWA::eKeyState_DpadUp) ||
pPadState->IsDown(SWA::eKeyState_DpadDown) ||
pPadState->IsDown(SWA::eKeyState_DpadLeft) ||
pPadState->IsDown(SWA::eKeyState_DpadRight);
}
static void SetDPadAnalogDirectionX(PPCRegister& pPadState, PPCRegister& x, bool invert, float max = 1.0f)
{
auto pGuestPadState = (SWA::SPadState*)g_memory.Translate(pPadState.u32);
if (pGuestPadState->IsDown(SWA::eKeyState_DpadLeft))
x.f64 = invert ? max : -max;
if (pGuestPadState->IsDown(SWA::eKeyState_DpadRight))
x.f64 = invert ? -max : max;
}
static void SetDPadAnalogDirectionY(PPCRegister& pPadState, PPCRegister& y, bool invert, float max = 1.0f)
{
auto pGuestPadState = (SWA::SPadState*)g_memory.Translate(pPadState.u32);
if (pGuestPadState->IsDown(SWA::eKeyState_DpadUp))
y.f64 = invert ? -max : max;
if (pGuestPadState->IsDown(SWA::eKeyState_DpadDown))
y.f64 = invert ? max : -max;
}
// -------------- PLAYER --------------- //
void PostureDPadSupportMidAsmHook(PPCRegister& pPadState, PPCRegister& x, PPCRegister& y)
{
SetDPadAnalogDirectionX(pPadState, x, false);
SetDPadAnalogDirectionY(pPadState, y, false);
}
void PostureDPadSupportInvertYMidAsmHook(PPCRegister& pPadState, PPCRegister& x, PPCRegister& y)
{
SetDPadAnalogDirectionX(pPadState, x, false);
SetDPadAnalogDirectionY(pPadState, y, true);
}
void PostureDPadSupportXMidAsmHook(PPCRegister& pPadState, PPCRegister& x)
{
SetDPadAnalogDirectionX(pPadState, x, false);
}
void PostureDPadSupportYMidAsmHook(PPCRegister& pPadState, PPCRegister& y)
{
SetDPadAnalogDirectionY(pPadState, y, false);
}
void PostureSpaceHurrierDPadSupportXMidAsmHook(PPCRegister& pPadState, PPCVRegister& vector)
{
auto pGuestPadState = (SWA::SPadState*)g_memory.Translate(pPadState.u32);
if (pGuestPadState->IsDown(SWA::eKeyState_DpadLeft))
vector.f32[3] = -1.0f;
if (pGuestPadState->IsDown(SWA::eKeyState_DpadRight))
vector.f32[3] = 1.0f;
}
void PostureSpaceHurrierDPadSupportYMidAsmHook(PPCRegister& pPadState, PPCVRegister& vector)
{
auto pGuestPadState = (SWA::SPadState*)g_memory.Translate(pPadState.u32);
if (pGuestPadState->IsDown(SWA::eKeyState_DpadUp))
vector.f32[3] = 1.0f;
if (pGuestPadState->IsDown(SWA::eKeyState_DpadDown))
vector.f32[3] = -1.0f;
}
// ------------- 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)
{
auto pGuestPadState = (SWA::SPadState*)g_memory.Translate(pPadState.u32);
if (fabs(pGuestPadState->LeftStickHorizontal) > g_worldMapTouchParams.CancelDeadzone ||
fabs(pGuestPadState->LeftStickVertical) > g_worldMapTouchParams.CancelDeadzone)
{
g_worldMapTouchVelocityX = 0;
}
if (IsDPadActive(pGuestPadState))
{
g_worldMapTouchVelocityX = 0;
SetDPadAnalogDirectionX(pPadState, x, false);
}
else
{
if (fabs(g_worldMapTouchVelocityX) > 0)
x.f64 = -g_worldMapTouchVelocityX;
}
}
void TouchAndDPadSupportWorldMapYMidAsmHook(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)
{
g_worldMapTouchVelocityY = 0;
}
if (IsDPadActive(pGuestPadState))
{
g_worldMapTouchVelocityY = 0;
SetDPadAnalogDirectionY(pPadState, y, false);
}
else
{
if (fabs(g_worldMapTouchVelocityY) > 0)
y.f64 = g_worldMapTouchVelocityY;
}
}
// SWA::CWorldMapCamera::Update
PPC_FUNC_IMPL(__imp__sub_82486968);
PPC_FUNC(sub_82486968)
{
auto pWorldMapCamera = (SWA::CWorldMapCamera*)g_memory.Translate(ctx.r3.u32);
// Reset vertical velocity if maximum pitch reached.
if (fabs(pWorldMapCamera->m_Pitch) >= 80.0f)
g_worldMapTouchVelocityY = 0;
__imp__sub_82486968(ctx, base);
}
// World Map cursor move hook.
PPC_FUNC(sub_8256C938)
{
auto pWorldMapCursor = (SWA::CWorldMapCursor*)g_memory.Translate(ctx.r3.u32);
pWorldMapCursor->m_IsCursorMoving = g_isTouchActive;
if (ctx.r4.u8)
{
pWorldMapCursor->m_LeftStickVertical = 0;
pWorldMapCursor->m_LeftStickHorizontal = 0;
}
else if (auto pInputState = SWA::CInputState::GetInstance())
{
auto& rPadState = pInputState->GetPadState();
pWorldMapCursor->m_LeftStickVertical = rPadState.LeftStickVertical;
pWorldMapCursor->m_LeftStickHorizontal = rPadState.LeftStickHorizontal;
if (rPadState.IsDown(SWA::eKeyState_DpadUp))
pWorldMapCursor->m_LeftStickVertical = 1.0f;
if (rPadState.IsDown(SWA::eKeyState_DpadDown))
pWorldMapCursor->m_LeftStickVertical = -1.0f;
if (rPadState.IsDown(SWA::eKeyState_DpadLeft))
pWorldMapCursor->m_LeftStickHorizontal = -1.0f;
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)
{
pWorldMapCursor->m_IsCursorMoving = true;
}
}
}

View File

@@ -49,8 +49,6 @@ void WerehogBattleMusicMidAsmHook(PPCRegister& r11)
r11.u8 = 3;
}
void StorageDevicePromptMidAsmHook() {}
/* Hook function that gets the game region
and force result to zero for Japanese
to display the correct logos. */

View File

@@ -1,3 +1,6 @@
#include <user/config.h>
#include <SWA/CharacterUtility/CharacterProxy.h>
// CObjFlame::CObjFlame
// A field is not zero initialized,
// causing collisions to constantly get created
@@ -8,3 +11,59 @@ PPC_FUNC(sub_82608E60)
memset(base + ctx.r3.u32, 0, 0x154);
__imp__sub_82608E60(ctx, base);
}
// The barrel gets stuck at a slope at high frame rates and softlocks the player.
// We can update the character proxy at 30 FPS, and interpolate the visuals to work around this issue.
static constexpr size_t OBJ_BIG_BARREL_SIZE = 0x1A0;
struct ObjBigBarrelEx
{
float elapsedTime{};
bool interpolate{};
};
void ObjBigBarrelAllocMidAsmHook(PPCRegister& r3)
{
r3.u32 += sizeof(ObjBigBarrelEx);
}
// CObjBigBarrel::CObjBigBarrel
PPC_FUNC_IMPL(__imp__sub_8271AC08);
PPC_FUNC(sub_8271AC08)
{
new (base + ctx.r3.u32 + OBJ_BIG_BARREL_SIZE) ObjBigBarrelEx();
__imp__sub_8271AC08(ctx, base);
}
// CObjBigBarrel::Integrate
PPC_FUNC_IMPL(__imp__sub_8271AA30);
PPC_FUNC(sub_8271AA30)
{
auto objBigBarrelEx = reinterpret_cast<ObjBigBarrelEx*>(base + ctx.r3.u32 + OBJ_BIG_BARREL_SIZE);
objBigBarrelEx->interpolate = ctx.f1.f64 < (1.0 / 30.0);
objBigBarrelEx->elapsedTime += ctx.f1.f64;
if (!objBigBarrelEx->interpolate || objBigBarrelEx->elapsedTime >= (1.0f / 30.0f))
{
ctx.f1.f64 = objBigBarrelEx->elapsedTime;
__imp__sub_8271AA30(ctx, base);
objBigBarrelEx->elapsedTime = 0.0f;
}
}
void ObjBigBarrelSetPositionMidAsmHook(PPCRegister& r3, PPCRegister& r4)
{
uint8_t* base = g_memory.base;
auto objBigBarrelEx = reinterpret_cast<ObjBigBarrelEx*>(base + r3.u32 + OBJ_BIG_BARREL_SIZE);
if (objBigBarrelEx->interpolate)
{
auto characterProxy = reinterpret_cast<SWA::CCharacterProxy*>(base + PPC_LOAD_U32(r3.u32 + 0x100));
auto position = reinterpret_cast<Hedgehog::Math::CVector*>(base + r4.u32);
float factor = (1.0f / 30.0f) - objBigBarrelEx->elapsedTime;
position->X = position->X - characterProxy->m_Velocity.X * factor;
position->Y = position->Y - characterProxy->m_Velocity.Y * factor;
position->Z = position->Z - characterProxy->m_Velocity.Z * factor;
}
}

View File

@@ -10,26 +10,6 @@ static uint32_t g_lastTrickScore;
static float g_lastDarkGaiaEnergy;
static bool g_isUnleashCancelled;
void PostureDPadSupportMidAsmHook(PPCRegister& r3)
{
if (!Config::AllowDPadMovement)
return;
auto pPadState = (SWA::SPadState*)g_memory.Translate(r3.u32);
if (pPadState->IsDown(SWA::eKeyState_DpadUp))
pPadState->LeftStickVertical = 1.0f;
if (pPadState->IsDown(SWA::eKeyState_DpadDown))
pPadState->LeftStickVertical = -1.0f;
if (pPadState->IsDown(SWA::eKeyState_DpadLeft))
pPadState->LeftStickHorizontal = -1.0f;
if (pPadState->IsDown(SWA::eKeyState_DpadRight))
pPadState->LeftStickHorizontal = 1.0f;
}
/* Hook function for when checkpoints are activated
to preserve the current checkpoint score. */
PPC_FUNC_IMPL(__imp__sub_82624308);

View File

@@ -2,7 +2,7 @@
#include <user/config.h>
#include <api/SWA.h>
#include <os/logger.h>
#include <hid/hid_detail.h>
#include <hid/hid.h>
#include <app.h>
bool m_isSavedAchievementData = false;
@@ -69,6 +69,8 @@ PPC_FUNC(sub_824E5170)
if (pSaveIcon->m_IsVisible)
{
App::s_isSaveDataCorrupt = false;
if (!m_isSavedAchievementData)
{
LOGN("Saving achievements...");
@@ -131,7 +133,7 @@ void LoadingScreenControllerMidAsmHook()
auto isPlayStation = Config::ControllerIcons == EControllerIcons::PlayStation;
if (Config::ControllerIcons == EControllerIcons::Auto)
isPlayStation = hid::detail::g_inputDeviceController == hid::detail::EInputDevice::PlayStation;
isPlayStation = hid::g_inputDeviceController == hid::EInputDevice::PlayStation;
const char* prefix = isPlayStation ? "ps3" : "360";

View File

@@ -19,7 +19,7 @@ void CHudPauseAddOptionsItemMidAsmHook(PPCRegister& pThis)
guest_stack_var<Hedgehog::Base::CSharedString> menu("TopMenu");
guest_stack_var<Hedgehog::Base::CSharedString> name("option");
GuestToHostFunction<int>(0x824AE690, pThis.u32, menu.get(), name.get());
GuestToHostFunction<int>(sub_824AE690, pThis.u32, menu.get(), name.get());
}
bool InjectMenuBehaviour(uint32_t pThis, uint32_t count)
@@ -68,6 +68,9 @@ bool InjectMenuBehaviour(uint32_t pThis, uint32_t count)
pHudPause->m_Action = SWA::eActionType_Undefined;
pHudPause->m_Transition = SWA::eTransitionType_Hide;
if (pHudPause->m_rcBg1Select)
pHudPause->m_rcBg1Select->SetHideFlag(true);
return true;
}
else if (cursorIndex == count - 1)
@@ -125,7 +128,7 @@ PPC_FUNC(sub_824B0930)
// Re-open pause menu after achievement menu closes with delay.
if (g_achievementMenuOutroTime >= g_achievementMenuOutroThreshold)
{
GuestToHostFunction<int>(0x824AFD28, pHudPause, 0, 1, 0, 0);
GuestToHostFunction<int>(sub_824AFD28, pHudPause, 0, 1, 0, 0);
g_achievementMenuOutroTime = 0;
g_isAchievementMenuOutro = false;
@@ -152,7 +155,8 @@ PPC_FUNC(sub_824B0930)
{
OptionsMenu::Close();
GuestToHostFunction<int>(0x824AFD28, pHudPause, 0, 0, 0, 1);
GuestToHostFunction<int>(sub_824AFD28, pHudPause, 0, 0, 0, 1);
__imp__sub_824B0930(ctx, base);
}
}
else

View File

@@ -1,15 +1,23 @@
#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>
static bool g_quitMessageOpen = false;
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;
@@ -37,6 +45,33 @@ static bool ProcessQuitMessage()
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)
@@ -47,7 +82,7 @@ PPC_FUNC(sub_82587E50)
{
__imp__sub_82587E50(ctx, base);
}
else
else if (!ProcessCorruptSaveMessage())
{
auto pInputState = SWA::CInputState::GetInstance();

View File

@@ -0,0 +1,3 @@
#pragma once
extern bool g_quitMessageOpen;

View File

@@ -50,11 +50,18 @@ PPC_FUNC_IMPL(__imp__sub_825882B8);
PPC_FUNC(sub_825882B8)
{
auto pTitleState = (SWA::CTitleStateBase*)g_memory.Translate(ctx.r3.u32);
auto pInputState = SWA::CInputState::GetInstance();
auto& pPadState = pInputState->GetPadState();
auto isAccepted = pPadState.IsTapped(SWA::eKeyState_A) || pPadState.IsTapped(SWA::eKeyState_Start);
auto isOptionsIndex = pTitleState->m_pMember->m_pTitleMenu->m_CursorIndex == 2;
auto isInstallIndex = pTitleState->m_pMember->m_pTitleMenu->m_CursorIndex == 3;
auto pContext = pTitleState->GetContextBase<SWA::CTitleStateBase::CTitleStateContext>();
auto isOptionsIndex = pContext->m_pTitleMenu->m_CursorIndex == 2;
auto isInstallIndex = pContext->m_pTitleMenu->m_CursorIndex == 3;
// Always default to New Game with corrupted save data.
if (App::s_isSaveDataCorrupt && pContext->m_pTitleMenu->m_CursorIndex == 1)
pContext->m_pTitleMenu->m_CursorIndex = 0;
if (!OptionsMenu::s_isVisible && isOptionsIndex)
{
@@ -92,6 +99,14 @@ PPC_FUNC(sub_825882B8)
}
}
void TitleMenuRemoveContinueOnCorruptSaveMidAsmHook(PPCRegister& r3)
{
if (!App::s_isSaveDataCorrupt)
return;
r3.u64 = 0;
}
void TitleMenuRemoveStorageDeviceOptionMidAsmHook(PPCRegister& r11)
{
r11.u32 = 0;

View File

@@ -1,6 +1,8 @@
#include <user/config.h>
#include <api/SWA.h>
#include <ui/game_window.h>
#include <gpu/video.h>
#include "aspect_ratio_patches.h"
using SVertexData = SWA::Sequence::Utility::CPlayMovieWrapper::CRender::SVertexData;
@@ -20,40 +22,40 @@ PPC_FUNC(sub_82AE30D8)
auto quadHeight = std::fabs(pTopLeft->Y - pBottomLeft->Y) * ((float)*pViewportHeight / 2);
auto movieAspectRatio = quadWidth / quadHeight;
auto windowAspectRatio = (float)GameWindow::s_width / (float)GameWindow::s_height;
auto a = -1.00078f;
auto b = 1.00139f;
auto scaleU = 1.0f;
auto scaleV = 1.0f;
auto centreV = (pTopLeft->V + pBottomRight->V) / 2.0f;
if (windowAspectRatio > movieAspectRatio)
if (g_aspectRatio > movieAspectRatio)
{
scaleU = movieAspectRatio / windowAspectRatio;
scaleU = movieAspectRatio / g_aspectRatio;
}
else
{
scaleV = windowAspectRatio / movieAspectRatio;
scaleV = g_aspectRatio / movieAspectRatio;
}
pTopLeft->X = a;
pTopLeft->Y = b;
float halfPixelX = 1.0f / Video::s_viewportWidth;
float halfPixelY = 1.0f / Video::s_viewportHeight;
pTopLeft->X = -1.0f - halfPixelX;
pTopLeft->Y = 1.0f + halfPixelY;
pTopLeft->U = (pTopLeft->U - centreV) / scaleU + centreV;
pTopLeft->V = (pTopLeft->V - centreV) / scaleV + centreV;
pTopRight->X = b;
pTopRight->Y = b;
pTopRight->X = 1.0f - halfPixelX;
pTopRight->Y = 1.0f + halfPixelY;
pTopRight->U = (pTopRight->U - centreV) / scaleU + centreV;
pTopRight->V = (pTopRight->V - centreV) / scaleV + centreV;
pBottomLeft->X = a;
pBottomLeft->Y = a;
pBottomLeft->X = -1.0f - halfPixelX;
pBottomLeft->Y = -1.0f + halfPixelY;
pBottomLeft->U = (pBottomLeft->U - centreV) / scaleU + centreV;
pBottomLeft->V = (pBottomLeft->V - centreV) / scaleV + centreV;
pBottomRight->X = b;
pBottomRight->Y = a;
pBottomRight->X = 1.0f - halfPixelX;
pBottomRight->Y = -1.0f + halfPixelY;
pBottomRight->U = (pBottomRight->U - centreV) / scaleU + centreV;
pBottomRight->V = (pBottomRight->V - centreV) / scaleV + centreV;

View File

@@ -1,4 +1,4 @@
VERSION_MILESTONE="Alpha"
VERSION_MAJOR=0
VERSION_MILESTONE="Beta 1"
VERSION_MAJOR=1
VERSION_MINOR=0
VERSION_REVISION=0

View File

@@ -13,7 +13,7 @@ using Microsoft::WRL::ComPtr;
#include <pwd.h>
#endif
#ifdef SWA_D3D12
#ifdef UNLEASHED_RECOMP_D3D12
#include <dxcapi.h>
#endif

View File

@@ -134,8 +134,8 @@ static void DrawHeaderContainer(const char* text)
? Lerp(1, 0, colourMotion)
: Lerp(0, 1, colourMotion);
ImVec2 min = { Scale(g_aspectRatioOffsetX + containerMarginX), Scale(g_aspectRatioOffsetY + 136) };
ImVec2 max = { min.x + textMarginX * 2 + textSize.x + Scale(5), Scale(g_aspectRatioOffsetY + 196) };
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);
@@ -568,8 +568,8 @@ static void DrawContentContainer()
? Hermite(604, 573, motion)
: Hermite(573, 604, motion);
ImVec2 min = { Scale(g_aspectRatioOffsetX + minX), Scale(g_aspectRatioOffsetY + minY) };
ImVec2 max = { Scale(g_aspectRatioOffsetX + maxX), Scale(g_aspectRatioOffsetY + maxY) };
ImVec2 min = { g_aspectRatioOffsetX + Scale(minX), g_aspectRatioOffsetY + Scale(minY) };
ImVec2 max = { g_aspectRatioOffsetX + Scale(maxX), g_aspectRatioOffsetY + Scale(maxY) };
// Transparency fade animation.
auto alpha = g_isClosing

View File

@@ -2,7 +2,7 @@
#include "imgui_utils.h"
#include <gpu/imgui/imgui_snapshot.h>
#include <gpu/video.h>
#include <hid/hid_detail.h>
#include <hid/hid.h>
#include <user/config.h>
#include <app.h>
#include <decompressor.h>
@@ -63,7 +63,7 @@ std::tuple<std::tuple<ImVec2, ImVec2>, GuestTexture*> GetButtonIcon(EButtonIcon
GuestTexture* texture;
auto isPlayStation = Config::ControllerIcons == EControllerIcons::Auto
? hid::detail::g_inputDeviceController == hid::detail::EInputDevice::PlayStation
? hid::g_inputDeviceController == hid::EInputDevice::PlayStation
: Config::ControllerIcons == EControllerIcons::PlayStation;
auto yOffsetCmn = isPlayStation ? 42 : 0;
@@ -229,8 +229,8 @@ void ButtonGuide::Draw()
auto drawList = ImGui::GetForegroundDrawList();
auto& res = ImGui::GetIO().DisplaySize;
ImVec2 regionMin = { Scale(g_aspectRatioOffsetX + g_sideMargins), Scale(g_aspectRatioOffsetY * 2.0f + 720.0f - 102.0f) };
ImVec2 regionMax = { Scale(g_aspectRatioOffsetX + 1280.0f - g_sideMargins), Scale(g_aspectRatioOffsetY * 2.0f + 720.0f) };
ImVec2 regionMin = { g_aspectRatioOffsetX + Scale(g_sideMargins), g_aspectRatioOffsetY * 2.0f + Scale(720.0f - 102.0f) };
ImVec2 regionMax = { g_aspectRatioOffsetX + Scale(1280.0f - g_sideMargins), g_aspectRatioOffsetY * 2.0f + Scale(720.0f) };
auto textMarginX = Scale(57);
auto textMarginY = Scale(8);

View File

@@ -214,7 +214,7 @@ void GameWindow::Init(const char* sdlVideoDriver)
if (!IsPositionValid())
GameWindow::ResetDimensions();
s_pWindow = SDL_CreateWindow("SWA", s_x, s_y, s_width, s_height, GetWindowFlags());
s_pWindow = SDL_CreateWindow("Unleashed Recompiled", s_x, s_y, s_width, s_height, GetWindowFlags());
if (IsFullscreen())
SDL_ShowCursor(SDL_DISABLE);
@@ -298,9 +298,14 @@ void GameWindow::SetIcon(bool isNight)
const char* GameWindow::GetTitle()
{
return Config::Language == ELanguage::Japanese
? "SONIC WORLD ADVENTURE"
: "SONIC UNLEASHED";
if (Config::UseOfficialTitleOnTitleBar)
{
return Config::Language == ELanguage::Japanese
? "SONIC WORLD ADVENTURE"
: "SONIC UNLEASHED";
}
return "Unleashed Recompiled";
}
void GameWindow::SetTitle(const char* title)

View File

@@ -108,14 +108,20 @@ inline void ResetProceduralOrigin()
SetProceduralOrigin({ 0.0f, 0.0f });
}
inline void SetAdditive(bool enabled)
{
auto callbackData = AddImGuiCallback(ImGuiCallback::SetAdditive);
callbackData->setAdditive.enabled = enabled;
}
inline void ResetAdditive()
{
SetAdditive(false);
}
inline float Scale(float size)
{
auto& io = ImGui::GetIO();
if (g_aspectRatio >= NARROW_ASPECT_RATIO)
return size * (io.DisplaySize.y / 720.0f);
else
return size * (io.DisplaySize.x / 960.0f);
return size * g_aspectRatioScale;
}
inline double ComputeMotion(double duration, double offset, double total)

View File

@@ -7,7 +7,6 @@
#include <gpu/video.h>
#include <gpu/imgui/imgui_snapshot.h>
#include <hid/hid.h>
#include <hid/hid_detail.h>
#include <locale/locale.h>
#include <patches/aspect_ratio_patches.h>
#include <ui/imgui_utils.h>
@@ -16,6 +15,7 @@
#include <ui/sdl_listener.h>
#include <ui/game_window.h>
#include <decompressor.h>
#include <exports.h>
#include <res/images/common/hedge-dev.dds.h>
#include <res/images/installer/install_001.dds.h>
@@ -448,7 +448,7 @@ static void DrawLeftImage()
GuestTexture *guestTexture = g_installTextures[installTextureIndex % g_installTextures.size()].get();
auto &res = ImGui::GetIO().DisplaySize;
auto drawList = ImGui::GetForegroundDrawList();
ImVec2 min = { Scale(g_aspectRatioOffsetX + IMAGE_X), Scale(g_aspectRatioOffsetY + IMAGE_Y) };
ImVec2 min = { g_aspectRatioOffsetX + Scale(IMAGE_X), g_aspectRatioOffsetY + Scale(IMAGE_Y) };
ImVec2 max = { min.x + Scale(IMAGE_WIDTH), min.y + Scale(IMAGE_HEIGHT) };
drawList->AddImage(guestTexture, min, max, ImVec2(0, 0), ImVec2(1, 1), IM_COL32(255, 255, 255, a));
}
@@ -458,9 +458,9 @@ static void DrawHeaderIconsForInstallPhase(double iconsPosX, double iconsPosY, d
auto drawList = ImGui::GetForegroundDrawList();
// Arrow Circle Icon
ImVec2 arrowCircleMin = { Scale(iconsPosX - iconsScale / 2), Scale(iconsPosY - iconsScale / 2) };
ImVec2 arrowCircleMax = { Scale(iconsPosX + iconsScale / 2), Scale(iconsPosY + iconsScale / 2) };
ImVec2 center = { Scale(iconsPosX) + 0.5f, Scale(iconsPosY) - 0.5f };
ImVec2 arrowCircleMin = { g_aspectRatioOffsetX + Scale(iconsPosX - iconsScale / 2), Scale(iconsPosY - iconsScale / 2) };
ImVec2 arrowCircleMax = { g_aspectRatioOffsetX + Scale(iconsPosX + iconsScale / 2), Scale(iconsPosY + iconsScale / 2) };
ImVec2 center = { g_aspectRatioOffsetX + Scale(iconsPosX) + 0.5f, Scale(iconsPosY) - 0.5f };
float arrowCircleFadeMotion = ComputeMotionInstaller(g_installerStartTime, g_installerEndTime, INSTALL_ICONS_FADE_IN_ANIMATION_TIME, INSTALL_ICONS_FADE_IN_ANIMATION_DURATION);
float rotationMotion = ComputeMotionInstallerLoop(g_installerStartTime, ARROW_CIRCLE_LOOP_SPEED, 0);
@@ -503,8 +503,8 @@ static void DrawHeaderIconsForInstallPhase(double iconsPosX, double iconsPosY, d
float pulseScale = iconsScale * pulseHermiteMotion * 1.5;
ImVec2 pulseMin = { Scale(iconsPosX - pulseScale / 2), Scale(iconsPosY - pulseScale / 2) };
ImVec2 pulseMax = { Scale(iconsPosX + pulseScale / 2), Scale(iconsPosY + pulseScale / 2) };
ImVec2 pulseMin = { g_aspectRatioOffsetX + Scale(iconsPosX - pulseScale / 2), Scale(iconsPosY - pulseScale / 2) };
ImVec2 pulseMax = { g_aspectRatioOffsetX + Scale(iconsPosX + pulseScale / 2), Scale(iconsPosY + pulseScale / 2) };
drawList->AddImage(g_pulseInstall.get(), pulseMin, pulseMax, ImVec2(0, 0), ImVec2(1, 1), IM_COL32(255, 255, 255, 255 * pulseFade * pulseFadeMotion));
}
@@ -512,7 +512,7 @@ static void DrawHeaderIcons()
{
auto drawList = ImGui::GetForegroundDrawList();
float iconsPosX = g_aspectRatioOffsetX + 253.0f;
float iconsPosX = 253.0f;
float iconsPosY = 79.0f;
float iconsScale = 58;
@@ -520,8 +520,8 @@ static void DrawHeaderIcons()
float milesIconMotion = ComputeMotionInstaller(g_appearTime, g_disappearTime, MILES_ICON_ANIMATION_TIME, MILES_ICON_ANIMATION_DURATION);
float milesIconScale = iconsScale * (2 - milesIconMotion);
ImVec2 milesElectricMin = { Scale(iconsPosX - milesIconScale / 2), Scale(iconsPosY - milesIconScale / 2) };
ImVec2 milesElectricMax = { Scale(iconsPosX + milesIconScale / 2), Scale(iconsPosY + milesIconScale / 2) };
ImVec2 milesElectricMin = { g_aspectRatioOffsetX + Scale(iconsPosX - milesIconScale / 2), Scale(iconsPosY - milesIconScale / 2) };
ImVec2 milesElectricMax = { g_aspectRatioOffsetX + Scale(iconsPosX + milesIconScale / 2), Scale(iconsPosY + milesIconScale / 2) };
drawList->AddImage(g_milesElectricIcon.get(), milesElectricMin, milesElectricMax, ImVec2(0, 0), ImVec2(1, 1), IM_COL32(255, 255, 255, 255 * milesIconMotion));
if (int(g_currentPage) >= int(WizardPage::Installing))
@@ -578,7 +578,7 @@ static void DrawScanlineBars()
// Installer text
const std::string &headerText = Localise(g_currentPage == WizardPage::Installing ? "Installer_Header_Installing" : "Installer_Header_Installer");
auto alphaMotion = ComputeMotionInstaller(g_appearTime, g_disappearTime, TITLE_ANIMATION_TIME, TITLE_ANIMATION_DURATION);
DrawTextWithOutline(g_dfsogeistdFont, Scale(42.0f), { Scale(g_aspectRatioOffsetX + 285.0f), Scale(57.0f) }, IM_COL32(255, 195, 0, 255 * alphaMotion), headerText.c_str(), 4, IM_COL32(0, 0, 0, 255 * alphaMotion), IMGUI_SHADER_MODIFIER_TITLE_BEVEL);
DrawTextWithOutline(g_dfsogeistdFont, Scale(42.0f), { g_aspectRatioOffsetX + Scale(285.0f), Scale(57.0f) }, IM_COL32(255, 195, 0, 255 * alphaMotion), headerText.c_str(), 4, IM_COL32(0, 0, 0, 255 * alphaMotion), IMGUI_SHADER_MODIFIER_TITLE_BEVEL);
// Top bar line
drawList->AddLine
@@ -613,13 +613,15 @@ static void DrawContainer(ImVec2 min, ImVec2 max, bool isTextArea)
);
double gridOverlayAlpha = ComputeMotionInstaller(g_appearTime, g_disappearTime, CONTAINER_INNER_TIME, CONTAINER_INNER_DURATION);
const uint32_t gridColor = IM_COL32(0, 33, 0, (isTextArea ? 128 : 255) * gridAlpha);
const uint32_t gridColor = IM_COL32(0, 33, 0, (isTextArea ? 223 : 255) * gridAlpha);
const uint32_t gridOverlayColor = IM_COL32(0, 32, 0, 128 * gridOverlayAlpha);
float gridSize = Scale(GRID_SIZE);
SetShaderModifier(IMGUI_SHADER_MODIFIER_CHECKERBOARD);
SetAdditive(true);
drawList->AddRectFilled(min, max, gridColor);
SetAdditive(false);
SetShaderModifier(IMGUI_SHADER_MODIFIER_NONE);
if (isTextArea)
@@ -637,8 +639,8 @@ static void DrawDescriptionContainer()
auto drawList = ImGui::GetForegroundDrawList();
auto fontSize = Scale(26.0f);
ImVec2 descriptionMin = { Scale(g_aspectRatioOffsetX + CONTAINER_X), Scale(g_aspectRatioOffsetY + CONTAINER_Y) };
ImVec2 descriptionMax = { Scale(g_aspectRatioOffsetX + CONTAINER_X + CONTAINER_WIDTH), Scale(g_aspectRatioOffsetY + CONTAINER_Y + CONTAINER_HEIGHT) };
ImVec2 descriptionMin = { g_aspectRatioOffsetX + Scale(CONTAINER_X), g_aspectRatioOffsetY + Scale(CONTAINER_Y) };
ImVec2 descriptionMax = { g_aspectRatioOffsetX + Scale(CONTAINER_X + CONTAINER_WIDTH), g_aspectRatioOffsetY + Scale(CONTAINER_Y + CONTAINER_HEIGHT) };
SetProceduralOrigin(descriptionMin);
DrawContainer(descriptionMin, descriptionMax, true);
@@ -687,22 +689,24 @@ static void DrawDescriptionContainer()
auto imageScale = hedgeDevTextSize.x / 3;
auto imageMarginY = Scale(15);
auto colWhite = IM_COL32(255, 255, 255, 255 * textAlpha);
ImVec2 imageMin =
{
/* X */ Scale(g_aspectRatioOffsetX + CONTAINER_X) + (Scale(CONTAINER_WIDTH) / 2) - (imageScale / 2) - (hedgeDevTextSize.x / 2) - hedgeDevTextMarginX,
/* Y */ Scale(g_aspectRatioOffsetY + CONTAINER_Y) + (Scale(CONTAINER_HEIGHT) / 2) - (imageScale / 2) + imageMarginY
/* X */ g_aspectRatioOffsetX + Scale(CONTAINER_X) + (Scale(CONTAINER_WIDTH) / 2) - (imageScale / 2) - (hedgeDevTextSize.x / 2) - hedgeDevTextMarginX,
/* Y */ g_aspectRatioOffsetY + Scale(CONTAINER_Y) + (Scale(CONTAINER_HEIGHT) / 2) - (imageScale / 2) + imageMarginY
};
ImVec2 imageMax = { imageMin.x + imageScale, imageMin.y + imageScale };
drawList->AddImage(g_upHedgeDev.get(), imageMin, imageMax);
drawList->AddImage(g_upHedgeDev.get(), imageMin, imageMax, { 0, 0 }, { 1, 1 }, colWhite);
drawList->AddText
(
g_seuratFont,
fontSize,
{ /* X */ imageMax.x + hedgeDevTextMarginX, /* Y */ imageMin.y + (imageScale / 2) - (hedgeDevTextSize.y / 2) },
IM_COL32_WHITE,
colWhite,
hedgeDevStr
);
@@ -710,12 +714,12 @@ static void DrawDescriptionContainer()
auto marqueeTextMarginX = Scale(5);
auto marqueeTextMarginY = Scale(15);
ImVec2 textPos = { descriptionMax.x, Scale(g_aspectRatioOffsetY + CONTAINER_Y) + Scale(CONTAINER_HEIGHT) - marqueeTextSize.y - marqueeTextMarginY };
ImVec2 textMin = { Scale(g_aspectRatioOffsetX + CONTAINER_X), textPos.y };
ImVec2 textMax = { Scale(g_aspectRatioOffsetX + CONTAINER_X) + Scale(CONTAINER_WIDTH), Scale(g_aspectRatioOffsetY + CONTAINER_Y) + Scale(CONTAINER_HEIGHT) };
ImVec2 textPos = { descriptionMax.x, g_aspectRatioOffsetY + Scale(CONTAINER_Y) + Scale(CONTAINER_HEIGHT) - marqueeTextSize.y - marqueeTextMarginY };
ImVec2 textMin = { g_aspectRatioOffsetX + Scale(CONTAINER_X), textPos.y };
ImVec2 textMax = { g_aspectRatioOffsetX + Scale(CONTAINER_X) + Scale(CONTAINER_WIDTH), g_aspectRatioOffsetY + Scale(CONTAINER_Y) + Scale(CONTAINER_HEIGHT) };
SetMarqueeFade(textMin, textMax, Scale(32));
DrawTextWithMarquee(g_seuratFont, fontSize, textPos, textMin, textMax, IM_COL32_WHITE, CREDITS_TEXT, g_appearTime, 0.9, Scale(250));
DrawTextWithMarquee(g_seuratFont, fontSize, textPos, textMin, textMax, colWhite, CREDITS_TEXT, g_appearTime, 0.9, Scale(250));
ResetMarqueeFade();
}
@@ -726,9 +730,9 @@ static void DrawDescriptionContainer()
if (g_currentPage != WizardPage::Installing && textAlpha >= 1.0)
{
auto icon = hid::detail::IsInputDeviceController()
auto icon = hid::IsInputDeviceController()
? EButtonIcon::A
: hid::detail::g_inputDevice == hid::detail::EInputDevice::Keyboard
: hid::g_inputDevice == hid::EInputDevice::Keyboard
? EButtonIcon::Enter
: EButtonIcon::LMB;
@@ -845,16 +849,16 @@ static void ComputeButtonColumnCoordinates(ButtonColumn buttonColumn, float &min
switch (buttonColumn)
{
case ButtonColumnLeft:
minX = Scale(g_aspectRatioOffsetX + CONTAINER_X + CONTAINER_BUTTON_GAP);
maxX = Scale(g_aspectRatioOffsetX + CONTAINER_X + CONTAINER_BUTTON_GAP + CONTAINER_BUTTON_WIDTH);
minX = g_aspectRatioOffsetX + Scale(CONTAINER_X + CONTAINER_BUTTON_GAP);
maxX = g_aspectRatioOffsetX + Scale(CONTAINER_X + CONTAINER_BUTTON_GAP + CONTAINER_BUTTON_WIDTH);
break;
case ButtonColumnMiddle:
minX = Scale(g_aspectRatioOffsetX + CONTAINER_X + CONTAINER_WIDTH / 2.0f - CONTAINER_BUTTON_WIDTH / 2.0f);
maxX = Scale(g_aspectRatioOffsetX + CONTAINER_X + CONTAINER_WIDTH / 2.0f + CONTAINER_BUTTON_WIDTH / 2.0f);
minX = g_aspectRatioOffsetX + Scale(CONTAINER_X + CONTAINER_WIDTH / 2.0f - CONTAINER_BUTTON_WIDTH / 2.0f);
maxX = g_aspectRatioOffsetX + Scale(CONTAINER_X + CONTAINER_WIDTH / 2.0f + CONTAINER_BUTTON_WIDTH / 2.0f);
break;
case ButtonColumnRight:
minX = Scale(g_aspectRatioOffsetX + CONTAINER_X + CONTAINER_WIDTH - CONTAINER_BUTTON_GAP - CONTAINER_BUTTON_WIDTH);
maxX = Scale(g_aspectRatioOffsetX + CONTAINER_X + CONTAINER_WIDTH - CONTAINER_BUTTON_GAP);
minX = g_aspectRatioOffsetX + Scale(CONTAINER_X + CONTAINER_WIDTH - CONTAINER_BUTTON_GAP - CONTAINER_BUTTON_WIDTH);
maxX = g_aspectRatioOffsetX + Scale(CONTAINER_X + CONTAINER_WIDTH - CONTAINER_BUTTON_GAP);
break;
}
}
@@ -866,8 +870,8 @@ static void DrawSourceButton(ButtonColumn buttonColumn, float yRatio, const char
ComputeButtonColumnCoordinates(buttonColumn, minX, maxX);
float minusY = (CONTAINER_BUTTON_GAP + BUTTON_HEIGHT) * yRatio;
ImVec2 min = { minX, Scale(g_aspectRatioOffsetY + CONTAINER_Y + CONTAINER_HEIGHT - CONTAINER_BUTTON_GAP - BUTTON_HEIGHT - minusY) };
ImVec2 max = { maxX, Scale(g_aspectRatioOffsetY + CONTAINER_Y + CONTAINER_HEIGHT - CONTAINER_BUTTON_GAP - minusY) };
ImVec2 min = { minX, g_aspectRatioOffsetY + Scale(CONTAINER_Y + CONTAINER_HEIGHT - CONTAINER_BUTTON_GAP - BUTTON_HEIGHT - minusY) };
ImVec2 max = { maxX, g_aspectRatioOffsetY + Scale(CONTAINER_Y + CONTAINER_HEIGHT - CONTAINER_BUTTON_GAP - minusY) };
DrawButton(min, max, sourceText, true, sourceSet, buttonPressed);
}
@@ -880,8 +884,8 @@ static void DrawProgressBar(float progressRatio)
const uint32_t innerColor1 = IM_COL32(0, 32, 0, 255 * alpha);
float xPadding = Scale(6.0f);
float yPadding = Scale(3.0f);
ImVec2 min = { Scale(g_aspectRatioOffsetX + CONTAINER_X) + BOTTOM_X_GAP, Scale(g_aspectRatioOffsetY + CONTAINER_Y + CONTAINER_HEIGHT + BOTTOM_Y_GAP) };
ImVec2 max = { Scale(g_aspectRatioOffsetX + CONTAINER_X + CONTAINER_WIDTH - BOTTOM_X_GAP), Scale(g_aspectRatioOffsetY + CONTAINER_Y + CONTAINER_HEIGHT + BOTTOM_Y_GAP + BUTTON_HEIGHT) };
ImVec2 min = { g_aspectRatioOffsetX + Scale(CONTAINER_X) + BOTTOM_X_GAP, g_aspectRatioOffsetY + Scale(CONTAINER_Y + CONTAINER_HEIGHT + BOTTOM_Y_GAP) };
ImVec2 max = { g_aspectRatioOffsetX + Scale(CONTAINER_X + CONTAINER_WIDTH - BOTTOM_X_GAP), g_aspectRatioOffsetY + Scale(CONTAINER_Y + CONTAINER_HEIGHT + BOTTOM_Y_GAP + BUTTON_HEIGHT) };
DrawButtonContainer(min, max, 0, 0, alpha);
@@ -1068,8 +1072,8 @@ static void DrawLanguagePicker()
ComputeButtonColumnCoordinates((i < 3) ? ButtonColumnLeft : ButtonColumnRight, minX, maxX);
float minusY = (CONTAINER_BUTTON_GAP + BUTTON_HEIGHT) * (float(i % 3));
ImVec2 min = { minX, Scale(g_aspectRatioOffsetY + CONTAINER_Y + CONTAINER_HEIGHT - CONTAINER_BUTTON_GAP - BUTTON_HEIGHT - minusY) };
ImVec2 max = { maxX, Scale(g_aspectRatioOffsetY + CONTAINER_Y + CONTAINER_HEIGHT - CONTAINER_BUTTON_GAP - minusY) };
ImVec2 min = { minX, g_aspectRatioOffsetY + Scale(CONTAINER_Y + CONTAINER_HEIGHT - CONTAINER_BUTTON_GAP - BUTTON_HEIGHT - minusY) };
ImVec2 max = { maxX, g_aspectRatioOffsetY + Scale(CONTAINER_Y + CONTAINER_HEIGHT - CONTAINER_BUTTON_GAP - minusY) };
// TODO: The active button should change its style to show an enabled toggle if it matches the current language.
@@ -1094,8 +1098,8 @@ static void DrawSourcePickers()
ImVec2 textSize = ComputeTextSize(g_dfsogeistdFont, addFilesText.c_str(), 20.0f, squashRatio, ADD_BUTTON_MAX_TEXT_WIDTH);
textSize.x += BUTTON_TEXT_GAP;
ImVec2 min = { Scale(g_aspectRatioOffsetX + CONTAINER_X + BOTTOM_X_GAP), Scale(g_aspectRatioOffsetY + CONTAINER_Y + CONTAINER_HEIGHT + BOTTOM_Y_GAP) };
ImVec2 max = { Scale(g_aspectRatioOffsetX + CONTAINER_X + BOTTOM_X_GAP + textSize.x * squashRatio), Scale(g_aspectRatioOffsetY + CONTAINER_Y + CONTAINER_HEIGHT + BOTTOM_Y_GAP + BUTTON_HEIGHT) };
ImVec2 min = { g_aspectRatioOffsetX + Scale(CONTAINER_X + BOTTOM_X_GAP), g_aspectRatioOffsetY + Scale(CONTAINER_Y + CONTAINER_HEIGHT + BOTTOM_Y_GAP) };
ImVec2 max = { g_aspectRatioOffsetX + Scale(CONTAINER_X + BOTTOM_X_GAP + textSize.x * squashRatio), g_aspectRatioOffsetY + Scale(CONTAINER_Y + CONTAINER_HEIGHT + BOTTOM_Y_GAP + BUTTON_HEIGHT) };
DrawButton(min, max, addFilesText.c_str(), false, true, buttonPressed, ADD_BUTTON_MAX_TEXT_WIDTH);
if (buttonPressed)
{
@@ -1230,8 +1234,8 @@ static void DrawNextButton()
ImVec2 textSize = ComputeTextSize(g_newRodinFont, buttonText.c_str(), 20.0f, squashRatio, NEXT_BUTTON_MAX_TEXT_WIDTH);
textSize.x += BUTTON_TEXT_GAP;
ImVec2 min = { Scale(g_aspectRatioOffsetX + CONTAINER_X + CONTAINER_WIDTH - textSize.x * squashRatio - BOTTOM_X_GAP), Scale(g_aspectRatioOffsetY + CONTAINER_Y + CONTAINER_HEIGHT + BOTTOM_Y_GAP) };
ImVec2 max = { Scale(g_aspectRatioOffsetX + CONTAINER_X + CONTAINER_WIDTH - BOTTOM_X_GAP), Scale(g_aspectRatioOffsetY + CONTAINER_Y + CONTAINER_HEIGHT + BOTTOM_Y_GAP + BUTTON_HEIGHT) };
ImVec2 min = { g_aspectRatioOffsetX + Scale(CONTAINER_X + CONTAINER_WIDTH - textSize.x * squashRatio - BOTTOM_X_GAP), g_aspectRatioOffsetY + Scale(CONTAINER_Y + CONTAINER_HEIGHT + BOTTOM_Y_GAP) };
ImVec2 max = { g_aspectRatioOffsetX + Scale(CONTAINER_X + CONTAINER_WIDTH - BOTTOM_X_GAP), g_aspectRatioOffsetY + Scale(CONTAINER_Y + CONTAINER_HEIGHT + BOTTOM_Y_GAP + BUTTON_HEIGHT) };
bool buttonPressed = false;
DrawButton(min, max, buttonText.c_str(), false, nextButtonEnabled, buttonPressed, NEXT_BUTTON_MAX_TEXT_WIDTH);
@@ -1316,10 +1320,10 @@ static void DrawHorizontalBorder(bool bottomBorder)
const uint32_t FADE_COLOR_RIGHT = IM_COL32(155, 225, 155, 0);
auto drawList = ImGui::GetForegroundDrawList();
double borderScale = 1.0 - ComputeMotionInstaller(g_appearTime, g_disappearTime, CONTAINER_LINE_ANIMATION_TIME, CONTAINER_LINE_ANIMATION_DURATION);
float midX = Scale(g_aspectRatioOffsetX + CONTAINER_X + CONTAINER_WIDTH / 5);
float minX = std::lerp(Scale(g_aspectRatioOffsetX + CONTAINER_X - BORDER_SIZE - BORDER_OVERSHOOT), midX, borderScale);
float maxX = std::lerp(Scale(g_aspectRatioOffsetX + CONTAINER_X + CONTAINER_WIDTH + SIDE_CONTAINER_WIDTH + BORDER_OVERSHOOT), midX, borderScale);
float minY = bottomBorder ? Scale(g_aspectRatioOffsetY + CONTAINER_Y + CONTAINER_HEIGHT) : Scale(g_aspectRatioOffsetY + CONTAINER_Y - BORDER_SIZE);
float midX = g_aspectRatioOffsetX + Scale(CONTAINER_X + CONTAINER_WIDTH / 5);
float minX = std::lerp(g_aspectRatioOffsetX + Scale(CONTAINER_X - BORDER_SIZE - BORDER_OVERSHOOT), midX, borderScale);
float maxX = std::lerp(g_aspectRatioOffsetX + Scale(CONTAINER_X + CONTAINER_WIDTH + SIDE_CONTAINER_WIDTH + BORDER_OVERSHOOT), midX, borderScale);
float minY = g_aspectRatioOffsetY + (bottomBorder ? Scale(CONTAINER_Y + CONTAINER_HEIGHT) : Scale(CONTAINER_Y - BORDER_SIZE));
float maxY = minY + Scale(BORDER_SIZE);
drawList->AddRectFilledMultiColor
(
@@ -1348,11 +1352,11 @@ static void DrawVerticalBorder(bool rightBorder)
const uint32_t FADE_COLOR = IM_COL32(155, rightBorder ? 225 : 155, 155, 0);
auto drawList = ImGui::GetForegroundDrawList();
double borderScale = 1.0 - ComputeMotionInstaller(g_appearTime, g_disappearTime, CONTAINER_LINE_ANIMATION_TIME, CONTAINER_LINE_ANIMATION_DURATION);
float minX = rightBorder ? Scale(g_aspectRatioOffsetX + CONTAINER_X + CONTAINER_WIDTH) : Scale(g_aspectRatioOffsetX + CONTAINER_X - BORDER_SIZE);
float minX = g_aspectRatioOffsetX + (rightBorder ? Scale(CONTAINER_X + CONTAINER_WIDTH) : Scale(CONTAINER_X - BORDER_SIZE));
float maxX = minX + Scale(BORDER_SIZE);
float midY = Scale(g_aspectRatioOffsetY + CONTAINER_Y + CONTAINER_HEIGHT / 2);
float minY = std::lerp(Scale(g_aspectRatioOffsetY + CONTAINER_Y - BORDER_OVERSHOOT), midY, borderScale);
float maxY = std::lerp(Scale(g_aspectRatioOffsetY + CONTAINER_Y + CONTAINER_HEIGHT + BORDER_OVERSHOOT), midY, borderScale);
float midY = g_aspectRatioOffsetY + Scale(CONTAINER_Y + CONTAINER_HEIGHT / 2);
float minY = std::lerp(g_aspectRatioOffsetY + Scale(CONTAINER_Y - BORDER_OVERSHOOT), midY, borderScale);
float maxY = std::lerp(g_aspectRatioOffsetY + Scale(CONTAINER_Y + CONTAINER_HEIGHT + BORDER_OVERSHOOT), midY, borderScale);
drawList->AddRectFilledMultiColor
(
{ minX, minY },

View File

@@ -2,7 +2,7 @@
#include "imgui_utils.h"
#include <api/SWA.h>
#include <gpu/video.h>
#include <hid/hid_detail.h>
#include <hid/hid.h>
#include <locale/locale.h>
#include <ui/button_guide.h>
#include <ui/sdl_listener.h>
@@ -257,7 +257,7 @@ static void ResetSelection()
{
/* Always use -1 for mouse input to prevent the selection
cursor from erroneously appearing where it shouldn't. */
g_selectedRowIndex = hid::detail::g_inputDevice == hid::detail::EInputDevice::Mouse
g_selectedRowIndex = hid::g_inputDevice == hid::EInputDevice::Mouse
? -1
: g_defaultButtonIndex;
@@ -294,8 +294,8 @@ void MessageWindow::Draw()
auto textMarginX = Scale(37);
auto textMarginY = Scale(45);
bool isController = hid::detail::IsInputDeviceController();
bool isKeyboard = hid::detail::g_inputDevice == hid::detail::EInputDevice::Keyboard;
bool isController = hid::IsInputDeviceController();
bool isKeyboard = hid::g_inputDevice == hid::EInputDevice::Keyboard;
// Handle controller input when the game is booted.
if (App::s_isInit)

View File

@@ -5,6 +5,7 @@
#include "exports.h"
#include <api/SWA/System/InputState.h>
#include <apu/audio.h>
#include <gpu/imgui/imgui_common.h>
#include <gpu/video.h>
#include <gpu/imgui/imgui_snapshot.h>
@@ -161,14 +162,14 @@ static void DrawScanlineBars()
if (g_aspectRatio >= WIDE_ASPECT_RATIO)
optionsX = g_aspectRatioOffsetX;
else
optionsX = (1.0f - g_narrowOffsetScale) * -20.0f;
optionsX = (1.0f - g_aspectRatioNarrowScale) * g_aspectRatioScale * -20.0f;
// Options text
DrawTextWithOutline
(
g_dfsogeistdFont,
Scale(48.0f),
{ Scale(optionsX + 122.0f), Scale(56.0f) },
{ optionsX + Scale(122.0f), Scale(56.0f) },
IM_COL32(255, 190, 33, 255),
Localise("Options_Header_Name").c_str(),
4,
@@ -322,7 +323,7 @@ static bool DrawCategories()
constexpr float WIDE_PADDING_GRID_COUNT = 3.0f;
float gridSize = Scale(GRID_SIZE);
float textPadding = gridSize * Lerp(NARROW_PADDING_GRID_COUNT, WIDE_PADDING_GRID_COUNT, g_narrowOffsetScale);
float textPadding = gridSize * Lerp(NARROW_PADDING_GRID_COUNT, WIDE_PADDING_GRID_COUNT, g_aspectRatioNarrowScale);
float tabPadding = gridSize;
float size = Scale(32.0f);
@@ -468,7 +469,7 @@ static void DrawConfigOption(int32_t rowIndex, float yOffset, ConfigDef<T>* conf
constexpr float OPTION_WIDE_GRID_COUNT = 54.0f;
auto gridSize = Scale(GRID_SIZE);
auto optionWidth = gridSize * floor(Lerp(OPTION_NARROW_GRID_COUNT, OPTION_WIDE_GRID_COUNT, g_narrowOffsetScale));
auto optionWidth = gridSize * floor(Lerp(OPTION_NARROW_GRID_COUNT, OPTION_WIDE_GRID_COUNT, g_aspectRatioNarrowScale));
auto optionHeight = gridSize * 5.5f;
auto optionPadding = gridSize * 0.5f;
auto valueWidth = Scale(192.0f);
@@ -507,6 +508,7 @@ static void DrawConfigOption(int32_t rowIndex, float yOffset, ConfigDef<T>* conf
config->Callback(config);
VideoConfigValueChangedCallback(config);
XAudioConfigValueChangedCallback(config);
Game_PlaySound("sys_worldmap_finaldecide");
}
@@ -538,6 +540,7 @@ static void DrawConfigOption(int32_t rowIndex, float yOffset, ConfigDef<T>* conf
if (config->Value != s_oldValue)
{
VideoConfigValueChangedCallback(config);
XAudioConfigValueChangedCallback(config);
if (config->ApplyCallback)
config->ApplyCallback(config);
@@ -564,6 +567,7 @@ static void DrawConfigOption(int32_t rowIndex, float yOffset, ConfigDef<T>* conf
config->MakeDefault();
VideoConfigValueChangedCallback(config);
XAudioConfigValueChangedCallback(config);
if (config->Callback)
config->Callback(config);
@@ -883,6 +887,8 @@ static void DrawConfigOptions()
{
case 0: // SYSTEM
DrawConfigOption(rowCount++, yOffset, &Config::Language, !OptionsMenu::s_isPause, cmnReason);
DrawConfigOption(rowCount++, yOffset, &Config::VoiceLanguage, OptionsMenu::s_pauseMenuType == SWA::eMenuType_WorldMap, cmnReason);
DrawConfigOption(rowCount++, yOffset, &Config::Subtitles, true);
DrawConfigOption(rowCount++, yOffset, &Config::Hints, !isStage, cmnReason);
DrawConfigOption(rowCount++, yOffset, &Config::ControlTutorial, !isStage, cmnReason);
DrawConfigOption(rowCount++, yOffset, &Config::AchievementNotifications, true);
@@ -894,7 +900,6 @@ static void DrawConfigOptions()
DrawConfigOption(rowCount++, yOffset, &Config::InvertCameraY, true);
DrawConfigOption(rowCount++, yOffset, &Config::Vibration, true);
DrawConfigOption(rowCount++, yOffset, &Config::AllowBackgroundInput, true);
DrawConfigOption(rowCount++, yOffset, &Config::AllowDPadMovement, true);
DrawConfigOption(rowCount++, yOffset, &Config::ControllerIcons, true);
break;
@@ -902,8 +907,7 @@ static void DrawConfigOptions()
DrawConfigOption(rowCount++, yOffset, &Config::MasterVolume, true);
DrawConfigOption(rowCount++, yOffset, &Config::MusicVolume, true);
DrawConfigOption(rowCount++, yOffset, &Config::EffectsVolume, true);
DrawConfigOption(rowCount++, yOffset, &Config::VoiceLanguage, OptionsMenu::s_pauseMenuType == SWA::eMenuType_WorldMap, cmnReason);
DrawConfigOption(rowCount++, yOffset, &Config::Subtitles, true);
DrawConfigOption(rowCount++, yOffset, &Config::ChannelConfiguration, true);
DrawConfigOption(rowCount++, yOffset, &Config::MusicAttenuation, AudioPatches::CanAttenuate(), &Localise("Options_Desc_OSNotSupported"));
DrawConfigOption(rowCount++, yOffset, &Config::BattleTheme, true);
break;
@@ -1220,23 +1224,23 @@ void OptionsMenu::Draw()
DrawScanlineBars();
float settingsGridCount = floor(Lerp(SETTINGS_NARROW_GRID_COUNT, SETTINGS_WIDE_GRID_COUNT, g_narrowOffsetScale));
float paddingGridCount = Lerp(PADDING_NARROW_GRID_COUNT, PADDING_WIDE_GRID_COUNT, g_narrowOffsetScale);
float infoGridCount = floor(Lerp(INFO_NARROW_GRID_COUNT, INFO_WIDE_GRID_COUNT, g_narrowOffsetScale));
float settingsGridCount = floor(Lerp(SETTINGS_NARROW_GRID_COUNT, SETTINGS_WIDE_GRID_COUNT, g_aspectRatioNarrowScale));
float paddingGridCount = Lerp(PADDING_NARROW_GRID_COUNT, PADDING_WIDE_GRID_COUNT, g_aspectRatioNarrowScale);
float infoGridCount = floor(Lerp(INFO_NARROW_GRID_COUNT, INFO_WIDE_GRID_COUNT, g_aspectRatioNarrowScale));
float totalGridCount = settingsGridCount + paddingGridCount + infoGridCount;
float offsetX = g_aspectRatioOffsetX + (1280.0f - ((GRID_SIZE * totalGridCount) - 1)) / 2.0f;
float minY = Scale(g_aspectRatioOffsetY + CONTAINER_POS_Y);
float maxY = Scale(g_aspectRatioOffsetY + (720.0f - CONTAINER_POS_Y + 1.0f));
float offsetX = (1280.0f - ((GRID_SIZE * totalGridCount) - 1)) / 2.0f;
float minY = g_aspectRatioOffsetY + Scale(CONTAINER_POS_Y);
float maxY = g_aspectRatioOffsetY + Scale((720.0f - CONTAINER_POS_Y + 1.0f));
DrawSettingsPanel(
{ Scale(offsetX), minY },
{ Scale(offsetX + settingsGridCount * GRID_SIZE), maxY }
{ g_aspectRatioOffsetX + Scale(offsetX), minY },
{ g_aspectRatioOffsetX + Scale(offsetX + settingsGridCount * GRID_SIZE), maxY }
);
DrawInfoPanel(
{ Scale(offsetX + (settingsGridCount + paddingGridCount) * GRID_SIZE), minY },
{ Scale(offsetX + totalGridCount * GRID_SIZE), maxY }
{ g_aspectRatioOffsetX + Scale(offsetX + (settingsGridCount + paddingGridCount) * GRID_SIZE), minY },
{ g_aspectRatioOffsetX + Scale(offsetX + totalGridCount * GRID_SIZE), maxY }
);
if (g_isStage)

View File

@@ -4,7 +4,6 @@
// TODO (Hyper): lower the resolution of these textures once final.
#include <res/images/options_menu/thumbnails/achievement_notifications.dds.h>
#include <res/images/options_menu/thumbnails/allow_background_input.dds.h>
#include <res/images/options_menu/thumbnails/allow_dpad_movement.dds.h>
#include <res/images/options_menu/thumbnails/antialiasing.dds.h>
#include <res/images/options_menu/thumbnails/aspect_ratio.dds.h>
#include <res/images/options_menu/thumbnails/battle_theme.dds.h>
@@ -46,8 +45,7 @@
#include <res/images/options_menu/thumbnails/vibration.dds.h>
#include <res/images/options_menu/thumbnails/vsync.dds.h>
#include <res/images/options_menu/thumbnails/window_size.dds.h>
#include <res/images/options_menu/thumbnails/xbox_color_correction_false.dds.h>
#include <res/images/options_menu/thumbnails/xbox_color_correction_true.dds.h>
#include <res/images/options_menu/thumbnails/xbox_color_correction.dds.h>
#define VALUE_THUMBNAIL_MAP(type) std::unordered_map<type, std::unique_ptr<GuestTexture>>
@@ -67,6 +65,8 @@ void LoadThumbnails()
g_namedThumbnails["WindowSize"] = LOAD_ZSTD_TEXTURE(g_window_size);
g_configThumbnails[&Config::Language] = LOAD_ZSTD_TEXTURE(g_language);
g_configThumbnails[&Config::VoiceLanguage] = LOAD_ZSTD_TEXTURE(g_voice_language);
g_configThumbnails[&Config::Subtitles] = LOAD_ZSTD_TEXTURE(g_subtitles);
g_configThumbnails[&Config::Hints] = LOAD_ZSTD_TEXTURE(g_hints);
g_configThumbnails[&Config::ControlTutorial] = LOAD_ZSTD_TEXTURE(g_control_tutorial);
g_configThumbnails[&Config::AchievementNotifications] = LOAD_ZSTD_TEXTURE(g_achievement_notifications);
@@ -78,13 +78,10 @@ void LoadThumbnails()
g_configThumbnails[&Config::InvertCameraY] = LOAD_ZSTD_TEXTURE(g_invert_camera_y);
g_configThumbnails[&Config::Vibration] = LOAD_ZSTD_TEXTURE(g_vibration);
g_configThumbnails[&Config::AllowBackgroundInput] = LOAD_ZSTD_TEXTURE(g_allow_background_input);
g_configThumbnails[&Config::AllowDPadMovement] = LOAD_ZSTD_TEXTURE(g_allow_dpad_movement);
g_configThumbnails[&Config::ControllerIcons] = LOAD_ZSTD_TEXTURE(g_controller_icons);
g_configThumbnails[&Config::MasterVolume] = LOAD_ZSTD_TEXTURE(g_master_volume);
g_configThumbnails[&Config::MusicVolume] = LOAD_ZSTD_TEXTURE(g_music_volume);
g_configThumbnails[&Config::EffectsVolume] = LOAD_ZSTD_TEXTURE(g_effects_volume);
g_configThumbnails[&Config::VoiceLanguage] = LOAD_ZSTD_TEXTURE(g_voice_language);
g_configThumbnails[&Config::Subtitles] = LOAD_ZSTD_TEXTURE(g_subtitles);
g_configThumbnails[&Config::MusicAttenuation] = LOAD_ZSTD_TEXTURE(g_music_attenuation);
g_configThumbnails[&Config::BattleTheme] = LOAD_ZSTD_TEXTURE(g_battle_theme);
g_configThumbnails[&Config::WindowSize] = LOAD_ZSTD_TEXTURE(g_window_size);
@@ -114,9 +111,7 @@ void LoadThumbnails()
g_motionBlurThumbnails[EMotionBlur::Original] = LOAD_ZSTD_TEXTURE(g_motion_blur_original);
g_motionBlurThumbnails[EMotionBlur::Enhanced] = LOAD_ZSTD_TEXTURE(g_motion_blur_enhanced);
g_xboxColorCorrectionThumbnails[false] = LOAD_ZSTD_TEXTURE(g_xbox_color_correction_false);
g_xboxColorCorrectionThumbnails[true] = LOAD_ZSTD_TEXTURE(g_xbox_color_correction_true);
g_configThumbnails[&Config::XboxColorCorrection] = LOAD_ZSTD_TEXTURE(g_xbox_color_correction);
g_configThumbnails[&Config::UIScaleMode] = LOAD_ZSTD_TEXTURE(g_ui_scale_mode);
}
@@ -173,10 +168,6 @@ GuestTexture* GetThumbnail(const IConfigDef* cfg)
{
TryGetValueThumbnail<EMotionBlur>(cfg, &g_motionBlurThumbnails, &texture);
}
else if (cfg == &Config::XboxColorCorrection)
{
TryGetValueThumbnail<bool>(cfg, &g_xboxColorCorrectionThumbnails, &texture);
}
return texture;
}

View File

@@ -1,5 +1,475 @@
#include "config.h"
#include <os/logger.h>
#include <user/paths.h>
#include <exports.h>
std::vector<IConfigDef*> g_configDefinitions;
#define CONFIG_DEFINE_ENUM_TEMPLATE(type) \
static std::unordered_map<std::string, type> g_##type##_template =
CONFIG_DEFINE_ENUM_TEMPLATE(ELanguage)
{
{ "English", ELanguage::English },
{ "Japanese", ELanguage::Japanese },
{ "German", ELanguage::German },
{ "French", ELanguage::French },
{ "Spanish", ELanguage::Spanish },
{ "Italian", ELanguage::Italian }
};
CONFIG_DEFINE_ENUM_TEMPLATE(EUnleashGaugeBehaviour)
{
{ "Original", EUnleashGaugeBehaviour::Original },
{ "Revised", EUnleashGaugeBehaviour::Revised }
};
CONFIG_DEFINE_ENUM_TEMPLATE(ETimeOfDayTransition)
{
{ "Xbox", ETimeOfDayTransition::Xbox },
{ "PlayStation", ETimeOfDayTransition::PlayStation }
};
CONFIG_DEFINE_ENUM_TEMPLATE(EControllerIcons)
{
{ "Auto", EControllerIcons::Auto },
{ "Xbox", EControllerIcons::Xbox },
{ "PlayStation", EControllerIcons::PlayStation }
};
CONFIG_DEFINE_ENUM_TEMPLATE(EChannelConfiguration)
{
{ "Stereo", EChannelConfiguration::Stereo },
{ "Surround", EChannelConfiguration::Surround }
};
CONFIG_DEFINE_ENUM_TEMPLATE(EVoiceLanguage)
{
{ "English", EVoiceLanguage::English },
{ "Japanese", EVoiceLanguage::Japanese }
};
CONFIG_DEFINE_ENUM_TEMPLATE(EGraphicsAPI)
{
#ifdef UNLEASHED_RECOMP_D3D12
{ "D3D12", EGraphicsAPI::D3D12 },
#endif
{ "Vulkan", EGraphicsAPI::Vulkan }
};
CONFIG_DEFINE_ENUM_TEMPLATE(EWindowState)
{
{ "Normal", EWindowState::Normal },
{ "Maximised", EWindowState::Maximised },
{ "Maximized", EWindowState::Maximised }
};
CONFIG_DEFINE_ENUM_TEMPLATE(EAspectRatio)
{
{ "Auto", EAspectRatio::Auto },
{ "16:9", EAspectRatio::Wide },
{ "4:3", EAspectRatio::Narrow },
{ "Original 4:3", EAspectRatio::OriginalNarrow },
};
CONFIG_DEFINE_ENUM_TEMPLATE(ETripleBuffering)
{
{ "Auto", ETripleBuffering::Auto },
{ "On", ETripleBuffering::On },
{ "Off", ETripleBuffering::Off }
};
CONFIG_DEFINE_ENUM_TEMPLATE(EAntiAliasing)
{
{ "None", EAntiAliasing::None },
{ "2x MSAA", EAntiAliasing::MSAA2x },
{ "4x MSAA", EAntiAliasing::MSAA4x },
{ "8x MSAA", EAntiAliasing::MSAA8x }
};
CONFIG_DEFINE_ENUM_TEMPLATE(EShadowResolution)
{
{ "Original", EShadowResolution::Original },
{ "512", EShadowResolution::x512 },
{ "1024", EShadowResolution::x1024 },
{ "2048", EShadowResolution::x2048 },
{ "4096", EShadowResolution::x4096 },
{ "8192", EShadowResolution::x8192 },
};
CONFIG_DEFINE_ENUM_TEMPLATE(EGITextureFiltering)
{
{ "Bilinear", EGITextureFiltering::Bilinear },
{ "Bicubic", EGITextureFiltering::Bicubic }
};
CONFIG_DEFINE_ENUM_TEMPLATE(EDepthOfFieldQuality)
{
{ "Auto", EDepthOfFieldQuality::Auto },
{ "Low", EDepthOfFieldQuality::Low },
{ "Medium", EDepthOfFieldQuality::Medium },
{ "High", EDepthOfFieldQuality::High },
{ "Ultra", EDepthOfFieldQuality::Ultra }
};
CONFIG_DEFINE_ENUM_TEMPLATE(EMotionBlur)
{
{ "Off", EMotionBlur::Off },
{ "Original", EMotionBlur::Original },
{ "Enhanced", EMotionBlur::Enhanced }
};
CONFIG_DEFINE_ENUM_TEMPLATE(ECutsceneAspectRatio)
{
{ "Original", ECutsceneAspectRatio::Original },
{ "Unlocked", ECutsceneAspectRatio::Unlocked }
};
CONFIG_DEFINE_ENUM_TEMPLATE(EUIScaleMode)
{
{ "Edge", EUIScaleMode::Edge },
{ "Centre", EUIScaleMode::Centre },
{ "Center", EUIScaleMode::Centre }
};
#undef CONFIG_DEFINE
#define CONFIG_DEFINE(section, type, name, defaultValue) \
ConfigDef<type> Config::name{section, #name, defaultValue};
#undef CONFIG_DEFINE_HIDDEN
#define CONFIG_DEFINE_HIDDEN(section, type, name, defaultValue) \
ConfigDef<type, true> Config::name{section, #name, defaultValue};
#undef CONFIG_DEFINE_LOCALISED
#define CONFIG_DEFINE_LOCALISED(section, type, name, defaultValue) \
extern CONFIG_LOCALE g_##name##_locale; \
ConfigDef<type> Config::name{section, #name, &g_##name##_locale, defaultValue};
#undef CONFIG_DEFINE_ENUM
#define CONFIG_DEFINE_ENUM(section, type, name, defaultValue) \
ConfigDef<type> Config::name{section, #name, defaultValue, &g_##type##_template};
#undef CONFIG_DEFINE_ENUM_LOCALISED
#define CONFIG_DEFINE_ENUM_LOCALISED(section, type, name, defaultValue) \
extern CONFIG_LOCALE g_##name##_locale; \
extern CONFIG_ENUM_LOCALE(type) g_##type##_locale; \
ConfigDef<type> Config::name{section, #name, &g_##name##_locale, defaultValue, &g_##type##_template, &g_##type##_locale};
#undef CONFIG_DEFINE_CALLBACK
#define CONFIG_DEFINE_CALLBACK(section, type, name, defaultValue, readCallback) \
extern CONFIG_LOCALE g_##name##_locale; \
ConfigDef<type> Config::name{section, #name, defaultValue, [](ConfigDef<type>* def) readCallback};
#include "config_def.h"
// CONFIG_DEFINE
template<typename T, bool isHidden>
ConfigDef<T, isHidden>::ConfigDef(std::string section, std::string name, T defaultValue) : Section(section), Name(name), DefaultValue(defaultValue)
{
g_configDefinitions.emplace_back(this);
}
// CONFIG_DEFINE_LOCALISED
template<typename T, bool isHidden>
ConfigDef<T, isHidden>::ConfigDef(std::string section, std::string name, CONFIG_LOCALE* nameLocale, T defaultValue) : Section(section), Name(name), Locale(nameLocale), DefaultValue(defaultValue)
{
g_configDefinitions.emplace_back(this);
}
// CONFIG_DEFINE_ENUM
template<typename T, bool isHidden>
ConfigDef<T, isHidden>::ConfigDef(std::string section, std::string name, T defaultValue, std::unordered_map<std::string, T>* enumTemplate) : Section(section), Name(name), DefaultValue(defaultValue), EnumTemplate(enumTemplate)
{
for (const auto& pair : *EnumTemplate)
EnumTemplateReverse[pair.second] = pair.first;
g_configDefinitions.emplace_back(this);
}
// CONFIG_DEFINE_ENUM_LOCALISED
template<typename T, bool isHidden>
ConfigDef<T, isHidden>::ConfigDef(std::string section, std::string name, CONFIG_LOCALE* nameLocale, T defaultValue, std::unordered_map<std::string, T>* enumTemplate, CONFIG_ENUM_LOCALE(T)* enumLocale) : Section(section), Name(name), Locale(nameLocale), DefaultValue(defaultValue), EnumTemplate(enumTemplate), EnumLocale(enumLocale)
{
for (const auto& pair : *EnumTemplate)
EnumTemplateReverse[pair.second] = pair.first;
g_configDefinitions.emplace_back(this);
}
// CONFIG_DEFINE_CALLBACK
template<typename T, bool isHidden>
ConfigDef<T, isHidden>::ConfigDef(std::string section, std::string name, T defaultValue, std::function<void(ConfigDef<T, isHidden>*)> callback) : Section(section), Name(name), DefaultValue(defaultValue), Callback(callback)
{
g_configDefinitions.emplace_back(this);
}
template<typename T, bool isHidden>
ConfigDef<T, isHidden>::~ConfigDef() = default;
template<typename T, bool isHidden>
bool ConfigDef<T, isHidden>::IsHidden()
{
return isHidden && !IsLoadedFromConfig;
}
template<typename T, bool isHidden>
void ConfigDef<T, isHidden>::ReadValue(toml::v3::ex::parse_result& toml)
{
if (auto pSection = toml[Section].as_table())
{
const auto& section = *pSection;
if constexpr (std::is_same<T, std::string>::value)
{
Value = section[Name].value_or<std::string>(DefaultValue);
}
else if constexpr (std::is_enum_v<T>)
{
auto value = section[Name].value_or(std::string());
auto it = EnumTemplate->find(value);
if (it != EnumTemplate->end())
{
Value = it->second;
}
else
{
Value = DefaultValue;
}
}
else
{
Value = section[Name].value_or(DefaultValue);
}
if (Callback)
Callback(this);
if (pSection->contains(Name))
IsLoadedFromConfig = true;
}
}
template<typename T, bool isHidden>
void ConfigDef<T, isHidden>::MakeDefault()
{
Value = DefaultValue;
}
template<typename T, bool isHidden>
std::string_view ConfigDef<T, isHidden>::GetSection() const
{
return Section;
}
template<typename T, bool isHidden>
std::string_view ConfigDef<T, isHidden>::GetName() const
{
return Name;
}
template<typename T, bool isHidden>
std::string ConfigDef<T, isHidden>::GetNameLocalised(ELanguage language) const
{
if (!Locale)
return Name;
if (!Locale->count(language))
{
if (Locale->count(ELanguage::English))
{
return std::get<0>(Locale->at(ELanguage::English));
}
else
{
return Name;
}
}
return std::get<0>(Locale->at(language));
}
template<typename T, bool isHidden>
std::string ConfigDef<T, isHidden>::GetDescription(ELanguage language) const
{
if (!Locale)
return "";
if (!Locale->count(language))
{
if (Locale->count(ELanguage::English))
{
return std::get<1>(Locale->at(ELanguage::English));
}
else
{
return "";
}
}
return std::get<1>(Locale->at(language));
}
template<typename T, bool isHidden>
bool ConfigDef<T, isHidden>::IsDefaultValue() const
{
return Value == DefaultValue;
}
template<typename T, bool isHidden>
const void* ConfigDef<T, isHidden>::GetValue() const
{
return &Value;
}
template<typename T, bool isHidden>
std::string ConfigDef<T, isHidden>::GetValueLocalised(ELanguage language) const
{
CONFIG_ENUM_LOCALE(T)* locale = nullptr;
if constexpr (std::is_enum_v<T>)
{
locale = EnumLocale;
}
else if constexpr (std::is_same_v<T, bool>)
{
return Value
? Localise("Common_On")
: Localise("Common_Off");
}
if (!locale)
return ToString(false);
if (!locale->count(language))
{
if (locale->count(ELanguage::English))
{
language = ELanguage::English;
}
else
{
return ToString(false);
}
}
auto strings = locale->at(language);
if (!strings.count(Value))
return ToString(false);
return std::get<0>(strings.at(Value));
}
template<typename T, bool isHidden>
std::string ConfigDef<T, isHidden>::GetValueDescription(ELanguage language) const
{
CONFIG_ENUM_LOCALE(T)* locale = nullptr;
if constexpr (std::is_enum_v<T>)
{
locale = EnumLocale;
}
else if constexpr (std::is_same_v<T, bool>)
{
return "";
}
if (!locale)
return "";
if (!locale->count(language))
{
if (locale->count(ELanguage::English))
{
language = ELanguage::English;
}
else
{
return "";
}
}
auto strings = locale->at(language);
if (!strings.count(Value))
return "";
return std::get<1>(strings.at(Value));
}
template<typename T, bool isHidden>
std::string ConfigDef<T, isHidden>::GetDefinition(bool withSection) const
{
std::string result;
if (withSection)
result += "[" + Section + "]\n";
result += Name + " = " + ToString();
return result;
}
template<typename T, bool isHidden>
std::string ConfigDef<T, isHidden>::ToString(bool strWithQuotes) const
{
std::string result = "N/A";
if constexpr (std::is_same_v<T, std::string>)
{
result = fmt::format("{}", Value);
if (strWithQuotes)
result = fmt::format("\"{}\"", result);
}
else if constexpr (std::is_enum_v<T>)
{
auto it = EnumTemplateReverse.find(Value);
if (it != EnumTemplateReverse.end())
result = fmt::format("{}", it->second);
if (strWithQuotes)
result = fmt::format("\"{}\"", result);
}
else
{
result = fmt::format("{}", Value);
}
return result;
}
template<typename T, bool isHidden>
void ConfigDef<T, isHidden>::GetLocaleStrings(std::vector<std::string_view>& localeStrings) const
{
if (Locale != nullptr)
{
for (auto& [language, nameAndDesc] : *Locale)
{
localeStrings.push_back(std::get<0>(nameAndDesc));
localeStrings.push_back(std::get<1>(nameAndDesc));
}
}
if (EnumLocale != nullptr)
{
for (auto& [language, locale] : *EnumLocale)
{
for (auto& [value, nameAndDesc] : locale)
{
localeStrings.push_back(std::get<0>(nameAndDesc));
localeStrings.push_back(std::get<1>(nameAndDesc));
}
}
}
}
std::filesystem::path Config::GetConfigPath()
{
return GetUserPath() / "config.toml";
}
void Config::Load()
{

Some files were not shown because too many files have changed in this diff Show More