mirror of
https://github.com/hedge-dev/UnleashedRecomp.git
synced 2025-04-28 13:27:58 +03:00
Linux support. (#54)
* Initial Linux attempt. * Add clang toolchain & make tools compile. * vcpkg as submodule. * First implementation of IO rewrite. (#31) * Fix directory iteration resolving symlinks. * Refactor kernel objects to be lock-free. * Implement guest critical sections using std::atomic. * Make D3D12 support optional. (#33) * Make D3D12 support optional. * Update ShaderRecomp, fix macros. * Replace QueryPerformanceCounter. (#35) * Add Linux home path for GetUserPath(). (#36) * Cross-platform Sleep. (#37) * Add mmap implementations for virtual allocation. (#38) * Cross-platform TLS. (#34) * Cross-platform TLS. * Fix front() to back(), use Mutex. * Fix global variable namings. --------- Co-authored-by: Skyth <19259897+blueskythlikesclouds@users.noreply.github.com> * Unicode support. (#39) * Replace CreateDirectoryA with Unicode version. * Cross platform thread implementation. (#41) * Cross-platform thread implementation. * Put set thread name calls behind a Win32 macro. * Cross-platform semaphore implementation. (#43) * xam: use SDL for keyboard input * Cross-platform atomic operations. (#44) * Cross-platform spin lock implementation. * Cross-platform reference counting. * Cross-platform event implementation. (#47) * Compiling and running on Linux. (#49) * Current work trying to get it to compile. * Update vcpkg.json baseline. * vcpkg, memory mapped file. * Bitscan forward. * Fix localtime_s. * FPS patches high res clock. * Rename Window to GameWindow. Fix guest pointers. * GetCurrentThreadID gone. * Code cache pointers, RenderWindow type. * Add Linux stubs. * Refactor Config. * Fix paths. * Add linux-release config. * FS fixes. * Fix Windows compilation errors & unicode converter crash. * Rename physical memory allocation functions to not clash with X11. * Fix NULL character being added on RtlMultiByteToUnicodeN. * Use std::exit. * Add protection to memory on Linux. * Convert majority of dependencies to submodules. (#48) * Convert majority of dependencies to submodules. * Don't compile header-only libraries. * Fix a few incorrect data types. * Fix config directory. * Unicode fixes & sizeof asserts. * Change the exit function to not call static destructors. * Fix files picker. * Add RelWithDebInfo preset for Linux. * Implement OS Restart on Linux. (#50) --------- Co-authored-by: Dario <dariosamo@gmail.com> * Update PowerRecomp. * Add Env Var detection for VCPKG_ROOT, add DLC detection. * Use error code version on DLC directory iterator. * Set D3D12MA::ALLOCATOR_FLAG_DONT_PREFER_SMALL_BUFFERS_COMMITTED flag. * Linux flatpak. (#51) * Add flatpak support. * Add game install directory override for flatpak. * Flatpak'ing. * Flatpak it some more. * We flat it, we pak it. * Flatpak'd. * The Marvelous Misadventures of Flatpak. * Attempt to change logic of NFD and show error. * Flattenpakken. * Use game install directory instead of current path. * Attempt to fix line endings. * Update io.github.hedge_dev.unleashedrecomp.json * Fix system time query implementation. * Add Present Wait to Vulkan to improve frame pacing and reduce latency. (#53) * Add present wait support to Vulkan. * Default to triple buffering if presentWait is supported. * Bracey fellas. * Update paths.h * SDL2 audio (again). (#52) * Implement SDL2 audio (again). * Call timeBeginPeriod/timeEndPeriod. * Replace miniaudio with SDL mixer. * Queue audio samples in a separate thread. * Enable CMake option override policy & fix compilation error. * Fix compilation error on Linux. * Fix but also trim shared strings. * Wayland support. (#55) * Make channel index a global variable in embedded player. * Fix SDL Audio selection for OGG on Flatpak. * Minor installer wizard fixes. * Fix compilation error. * Yield in model consumer and pipeline compiler threads. * Special case Sleep(0) to yield on Linux. * Add App Id hint. * Correct implementation for auto reset events. (#57) --------- Co-authored-by: Dario <dariosamo@gmail.com> Co-authored-by: Hyper <34012267+hyperbx@users.noreply.github.com>
This commit is contained in:
parent
f547c7ca6d
commit
67633917bf
109 changed files with 3373 additions and 2850 deletions
45
.gitmodules
vendored
45
.gitmodules
vendored
|
@ -16,6 +16,45 @@
|
|||
[submodule "thirdparty/msdf-atlas-gen"]
|
||||
path = thirdparty/msdf-atlas-gen
|
||||
url = https://github.com/Chlumsky/msdf-atlas-gen.git
|
||||
[submodule "thirdparty/miniaudio"]
|
||||
path = thirdparty/miniaudio
|
||||
url = https://github.com/mackron/miniaudio
|
||||
[submodule "thirdparty/vcpkg"]
|
||||
path = thirdparty/vcpkg
|
||||
url = https://github.com/microsoft/vcpkg
|
||||
[submodule "thirdparty/volk"]
|
||||
path = thirdparty/volk
|
||||
url = https://github.com/zeux/volk
|
||||
[submodule "thirdparty/SDL"]
|
||||
path = thirdparty/SDL
|
||||
url = https://github.com/libsdl-org/SDL.git
|
||||
[submodule "thirdparty/Vulkan-Headers"]
|
||||
path = thirdparty/Vulkan-Headers
|
||||
url = https://github.com/KhronosGroup/Vulkan-Headers.git
|
||||
[submodule "thirdparty/VulkanMemoryAllocator"]
|
||||
path = thirdparty/VulkanMemoryAllocator
|
||||
url = https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git
|
||||
[submodule "thirdparty/D3D12MemoryAllocator"]
|
||||
path = thirdparty/D3D12MemoryAllocator
|
||||
url = https://github.com/GPUOpen-LibrariesAndSDKs/D3D12MemoryAllocator.git
|
||||
[submodule "thirdparty/stb"]
|
||||
path = thirdparty/stb
|
||||
url = https://github.com/nothings/stb.git
|
||||
[submodule "thirdparty/concurrentqueue"]
|
||||
path = thirdparty/concurrentqueue
|
||||
url = https://github.com/cameron314/concurrentqueue.git
|
||||
[submodule "thirdparty/tiny-AES-c"]
|
||||
path = thirdparty/tiny-AES-c
|
||||
url = https://github.com/kokke/tiny-AES-c.git
|
||||
[submodule "thirdparty/magic_enum"]
|
||||
path = thirdparty/magic_enum
|
||||
url = https://github.com/Neargye/magic_enum.git
|
||||
[submodule "thirdparty/nativefiledialog-extended"]
|
||||
path = thirdparty/nativefiledialog-extended
|
||||
url = https://github.com/btzy/nativefiledialog-extended.git
|
||||
[submodule "thirdparty/imgui"]
|
||||
path = thirdparty/imgui
|
||||
url = https://github.com/ocornut/imgui.git
|
||||
[submodule "thirdparty/unordered_dense"]
|
||||
path = thirdparty/unordered_dense
|
||||
url = https://github.com/martinus/unordered_dense.git
|
||||
[submodule "thirdparty/SDL_mixer"]
|
||||
path = thirdparty/SDL_mixer
|
||||
url = https://github.com/libsdl-org/SDL_mixer
|
|
@ -1,5 +1,9 @@
|
|||
cmake_minimum_required (VERSION 3.20)
|
||||
|
||||
if(NOT DEFINED ENV{VCPKG_ROOT})
|
||||
message(FATAL_ERROR "VCPKG_ROOT is not defined!")
|
||||
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)
|
||||
|
|
|
@ -16,6 +16,9 @@
|
|||
"type": "FILEPATH"
|
||||
}
|
||||
},
|
||||
"environment": {
|
||||
"VCPKG_ROOT": "${sourceDir}/thirdparty/vcpkg"
|
||||
},
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
|
@ -55,6 +58,62 @@
|
|||
"CMAKE_BUILD_TYPE": "Release",
|
||||
"CMAKE_INTERPROCEDURAL_OPTIMIZATION": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "linux-base",
|
||||
"hidden": true,
|
||||
"generator": "Ninja",
|
||||
"binaryDir": "${sourceDir}/out/build/${presetName}",
|
||||
"installDir": "${sourceDir}/out/install/${presetName}",
|
||||
"cacheVariables": {
|
||||
"CMAKE_TOOLCHAIN_FILE": {
|
||||
"value": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake",
|
||||
"type": "FILEPATH"
|
||||
},
|
||||
"VCPKG_TARGET_TRIPLET": {
|
||||
"value": "x64-linux",
|
||||
"type": "STRING"
|
||||
},
|
||||
"VCPKG_CHAINLOAD_TOOLCHAIN_FILE": "${sourceDir}/toolchains/linux-clang.cmake"
|
||||
},
|
||||
"environment": {
|
||||
"VCPKG_ROOT": "${sourceDir}/thirdparty/vcpkg"
|
||||
},
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Linux"
|
||||
},
|
||||
"vendor": {
|
||||
"microsoft.com/VisualStudioRemoteSettings/CMake/2.0": {
|
||||
"remoteSourceRootDir": "$env{HOME}/.vs/$ms{projectDirName}"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "linux-debug",
|
||||
"displayName": "Linux-Debug",
|
||||
"inherits": "linux-base",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Debug"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "linux-relwithdebinfo",
|
||||
"displayName": "Linux-RelWithDebInfo",
|
||||
"inherits": "linux-base",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "RelWithDebInfo"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "linux-release",
|
||||
"displayName": "Linux-Release",
|
||||
"inherits": "linux-base",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Release",
|
||||
"CMAKE_INTERPROCEDURAL_OPTIMIZATION": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,6 +1,14 @@
|
|||
project("UnleashedRecomp")
|
||||
set(TARGET_NAME "SWA")
|
||||
|
||||
if (WIN32)
|
||||
option(SWA_D3D12 "Add D3D12 support for rendering" ON)
|
||||
endif()
|
||||
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
option(SWA_FLATPAK "Configure the build for Flatpak compatibility." OFF)
|
||||
endif()
|
||||
|
||||
option(SWA_XAUDIO2 "Use XAudio2 for audio playback" OFF)
|
||||
|
||||
function(BIN2C)
|
||||
|
@ -23,8 +31,8 @@ function(BIN2C)
|
|||
endif()
|
||||
|
||||
add_custom_command(OUTPUT "${BIN2C_ARGS_DEST_FILE}.c"
|
||||
COMMAND file_to_c "${BIN2C_ARGS_SOURCE_FILE}" "${BIN2C_ARGS_ARRAY_NAME}" "${BIN2C_ARGS_COMPRESSION_TYPE}" "${BIN2C_ARGS_DEST_FILE}.c" "${BIN2C_ARGS_DEST_FILE}.h"
|
||||
DEPENDS "${BIN2C_ARGS_SOURCE_FILE}" file_to_c
|
||||
COMMAND $<TARGET_FILE:file_to_c> "${BIN2C_ARGS_SOURCE_FILE}" "${BIN2C_ARGS_ARRAY_NAME}" "${BIN2C_ARGS_COMPRESSION_TYPE}" "${BIN2C_ARGS_DEST_FILE}.c" "${BIN2C_ARGS_DEST_FILE}.h"
|
||||
DEPENDS "${BIN2C_ARGS_SOURCE_FILE}"
|
||||
BYPRODUCTS "${BIN2C_ARGS_DEST_FILE}.h"
|
||||
COMMENT "Generating binary header for ${BIN2C_ARGS_SOURCE_FILE}..."
|
||||
)
|
||||
|
@ -34,7 +42,6 @@ function(BIN2C)
|
|||
endfunction()
|
||||
|
||||
add_compile_options(
|
||||
/fp:strict
|
||||
-march=sandybridge
|
||||
-fno-strict-aliasing
|
||||
|
||||
|
@ -45,8 +52,17 @@ add_compile_options(
|
|||
-Wno-void-pointer-to-int-cast
|
||||
-Wno-int-to-void-pointer-cast
|
||||
-Wno-invalid-offsetof
|
||||
-Wno-null-arithmetic
|
||||
-Wno-null-conversion
|
||||
-Wno-tautological-undefined-compare
|
||||
)
|
||||
|
||||
if (WIN32)
|
||||
add_compile_options(/fp:strict)
|
||||
else()
|
||||
add_compile_options(-ffp-model=strict)
|
||||
endif()
|
||||
|
||||
add_compile_definitions(
|
||||
SWA_IMPL
|
||||
SDL_MAIN_HANDLED
|
||||
|
@ -85,6 +101,13 @@ if (WIN32)
|
|||
"os/win32/process_win32.cpp"
|
||||
"os/win32/version_win32.cpp"
|
||||
)
|
||||
elseif (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
list(APPEND SWA_OS_CXX_SOURCES
|
||||
"os/linux/logger_linux.cpp"
|
||||
"os/linux/media_linux.cpp"
|
||||
"os/linux/process_linux.cpp"
|
||||
"os/linux/version_linux.cpp"
|
||||
)
|
||||
endif()
|
||||
|
||||
set(SWA_CPU_CXX_SOURCES
|
||||
|
@ -97,10 +120,15 @@ set(SWA_GPU_CXX_SOURCES
|
|||
"gpu/imgui/imgui_common.cpp"
|
||||
"gpu/imgui/imgui_font_builder.cpp"
|
||||
"gpu/imgui/imgui_snapshot.cpp"
|
||||
"gpu/rhi/plume_d3d12.cpp"
|
||||
"gpu/rhi/plume_vulkan.cpp"
|
||||
)
|
||||
|
||||
if (SWA_D3D12)
|
||||
list(APPEND SWA_GPU_CXX_SOURCES
|
||||
"gpu/rhi/plume_d3d12.cpp"
|
||||
)
|
||||
endif()
|
||||
|
||||
set(SWA_APU_CXX_SOURCES
|
||||
"apu/audio.cpp"
|
||||
"apu/embedded_player.cpp"
|
||||
|
@ -109,7 +137,7 @@ set(SWA_APU_CXX_SOURCES
|
|||
if (SWA_XAUDIO2)
|
||||
list(APPEND SWA_APU_CXX_SOURCES "apu/driver/xaudio_driver.cpp")
|
||||
else()
|
||||
list(APPEND SWA_APU_CXX_SOURCES "apu/driver/miniaudio_driver.cpp")
|
||||
list(APPEND SWA_APU_CXX_SOURCES "apu/driver/sdl2_driver.cpp")
|
||||
endif()
|
||||
|
||||
set(SWA_HID_CXX_SOURCES
|
||||
|
@ -142,7 +170,7 @@ set(SWA_UI_CXX_SOURCES
|
|||
"ui/options_menu_thumbnails.cpp"
|
||||
"ui/options_menu.cpp"
|
||||
"ui/sdl_listener.cpp"
|
||||
"ui/window.cpp"
|
||||
"ui/game_window.cpp"
|
||||
)
|
||||
|
||||
set(SWA_INSTALL_CXX_SOURCES
|
||||
|
@ -161,25 +189,47 @@ set(SWA_INSTALL_CXX_SOURCES
|
|||
"install/hashes/update.cpp"
|
||||
)
|
||||
|
||||
set(LIBMSPACK_PATH ${SWA_THIRDPARTY_ROOT}/libmspack/libmspack/mspack)
|
||||
|
||||
set(LIBMSPACK_C_SOURCES
|
||||
${LIBMSPACK_PATH}/lzxd.c
|
||||
)
|
||||
|
||||
set_source_files_properties(${LIBMSPACK_C_SOURCES} PROPERTIES SKIP_PRECOMPILE_HEADERS ON)
|
||||
|
||||
set(SMOLV_SOURCE_DIR "${SWA_TOOLS_ROOT}/ShaderRecomp/thirdparty/smol-v/source")
|
||||
set(BC_DIFF_SOURCE_DIR "${SWA_TOOLS_ROOT}/bc_diff")
|
||||
|
||||
set(MINIAUDIO_INCLUDE_DIRS "${SWA_THIRDPARTY_ROOT}/miniaudio")
|
||||
|
||||
set(SWA_USER_CXX_SOURCES
|
||||
"user/achievement_data.cpp"
|
||||
"user/config.cpp"
|
||||
"user/config_detail.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}/libmspack/libmspack/mspack/lzxd.c"
|
||||
"${SWA_THIRDPARTY_ROOT}/tiny-AES-c/aes.c"
|
||||
"${SWA_TOOLS_ROOT}/ShaderRecomp/thirdparty/smol-v/source/smolv.cpp"
|
||||
)
|
||||
|
||||
set(SWA_THIRDPARTY_INCLUDES
|
||||
"${SWA_THIRDPARTY_ROOT}/concurrentqueue"
|
||||
"${SWA_THIRDPARTY_ROOT}/ddspp"
|
||||
"${SWA_THIRDPARTY_ROOT}/imgui"
|
||||
"${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"
|
||||
)
|
||||
|
||||
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")
|
||||
endif()
|
||||
|
||||
set_source_files_properties(${SWA_THIRDPARTY_SOURCES} PROPERTIES SKIP_PRECOMPILE_HEADERS ON)
|
||||
|
||||
set(SWA_CXX_SOURCES
|
||||
"app.cpp"
|
||||
"exports.cpp"
|
||||
|
@ -197,9 +247,8 @@ set(SWA_CXX_SOURCES
|
|||
${SWA_PATCHES_CXX_SOURCES}
|
||||
${SWA_UI_CXX_SOURCES}
|
||||
${SWA_INSTALL_CXX_SOURCES}
|
||||
${LIBMSPACK_C_SOURCES}
|
||||
"${SMOLV_SOURCE_DIR}/smolv.cpp"
|
||||
${SWA_USER_CXX_SOURCES}
|
||||
${SWA_THIRDPARTY_SOURCES}
|
||||
)
|
||||
|
||||
if (WIN32)
|
||||
|
@ -213,98 +262,98 @@ endif()
|
|||
|
||||
set_target_properties(UnleashedRecomp PROPERTIES OUTPUT_NAME ${TARGET_NAME})
|
||||
|
||||
find_package(directx-headers CONFIG REQUIRED)
|
||||
find_package(directx12-agility CONFIG REQUIRED)
|
||||
find_package(d3d12-memory-allocator CONFIG REQUIRED)
|
||||
find_package(SDL2 CONFIG REQUIRED)
|
||||
find_package(unordered_dense CONFIG REQUIRED)
|
||||
find_package(VulkanHeaders CONFIG)
|
||||
find_package(volk CONFIG REQUIRED)
|
||||
find_package(VulkanMemoryAllocator CONFIG REQUIRED)
|
||||
find_package(xxHash CONFIG REQUIRED)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(tomlplusplus REQUIRED IMPORTED_TARGET tomlplusplus)
|
||||
if (SWA_FLATPAK)
|
||||
target_compile_definitions(UnleashedRecomp PRIVATE "GAME_INSTALL_DIRECTORY=\"/var/data\"")
|
||||
endif()
|
||||
|
||||
if (SWA_D3D12)
|
||||
find_package(directx-headers CONFIG REQUIRED)
|
||||
find_package(directx12-agility CONFIG REQUIRED)
|
||||
target_compile_definitions(UnleashedRecomp PRIVATE SWA_D3D12)
|
||||
endif()
|
||||
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
target_compile_definitions(UnleashedRecomp PRIVATE SDL_VULKAN_ENABLED)
|
||||
endif()
|
||||
|
||||
find_package(directx-dxc REQUIRED)
|
||||
find_package(zstd CONFIG REQUIRED)
|
||||
find_package(Stb REQUIRED)
|
||||
find_package(unofficial-concurrentqueue REQUIRED)
|
||||
find_package(imgui CONFIG REQUIRED)
|
||||
find_package(magic_enum CONFIG REQUIRED)
|
||||
find_package(unofficial-tiny-aes-c CONFIG REQUIRED)
|
||||
find_package(nfd CONFIG REQUIRED)
|
||||
find_package(Vorbis CONFIG REQUIRED)
|
||||
find_package(fmt CONFIG REQUIRED)
|
||||
|
||||
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
|
||||
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_PROPERTY:Microsoft::DirectX12-Layers,IMPORTED_LOCATION_DEBUG> ${CMAKE_CURRENT_BINARY_DIR}/D3D12
|
||||
COMMAND_EXPAND_LISTS
|
||||
)
|
||||
if (SWA_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
|
||||
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_PROPERTY:Microsoft::DirectX12-Layers,IMPORTED_LOCATION_DEBUG> ${CMAKE_CURRENT_BINARY_DIR}/D3D12
|
||||
COMMAND_EXPAND_LISTS
|
||||
)
|
||||
|
||||
file(COPY ${PACKAGE_PREFIX_DIR}/bin/dxil.dll DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
|
||||
file(COPY ${PACKAGE_PREFIX_DIR}/bin/dxil.dll DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
target_link_libraries(UnleashedRecomp PRIVATE
|
||||
Microsoft::DirectX-Headers
|
||||
Microsoft::DirectX-Guids
|
||||
Microsoft::DirectX12-Agility
|
||||
Microsoft::DirectXShaderCompiler
|
||||
Microsoft::DXIL
|
||||
dxgi
|
||||
)
|
||||
endif()
|
||||
|
||||
file(CHMOD ${DIRECTX_DXC_TOOL} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE)
|
||||
|
||||
if (WIN32)
|
||||
target_link_libraries(UnleashedRecomp PRIVATE
|
||||
comctl32
|
||||
ntdll
|
||||
winmm
|
||||
Synchronization
|
||||
)
|
||||
endif()
|
||||
|
||||
target_link_libraries(UnleashedRecomp PRIVATE
|
||||
Microsoft::DirectX-Headers
|
||||
Microsoft::DirectX-Guids
|
||||
Microsoft::DirectX12-Agility
|
||||
Microsoft::DirectXShaderCompiler
|
||||
comctl32
|
||||
dxgi
|
||||
Vulkan::Headers
|
||||
volk::volk
|
||||
volk::volk_headers
|
||||
GPUOpen::VulkanMemoryAllocator
|
||||
ntdll
|
||||
fmt::fmt
|
||||
libzstd_static
|
||||
msdf-atlas-gen::msdf-atlas-gen
|
||||
nfd::nfd
|
||||
o1heap
|
||||
PowerUtils
|
||||
SDL2::SDL2-static
|
||||
SDL2_mixer
|
||||
tomlplusplus::tomlplusplus
|
||||
UnleashedRecompLib
|
||||
unofficial::D3D12MemoryAllocator
|
||||
unordered_dense::unordered_dense
|
||||
winmm
|
||||
xxHash::xxhash
|
||||
PkgConfig::tomlplusplus
|
||||
zstd::libzstd_static
|
||||
unofficial::concurrentqueue::concurrentqueue
|
||||
Synchronization
|
||||
imgui::imgui
|
||||
magic_enum::magic_enum
|
||||
unofficial::tiny-aes-c::tiny-aes-c
|
||||
nfd::nfd
|
||||
msdf-atlas-gen::msdf-atlas-gen
|
||||
Vorbis::vorbisfile
|
||||
fmt::fmt
|
||||
)
|
||||
|
||||
target_include_directories(UnleashedRecomp PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/api
|
||||
${SWA_THIRDPARTY_ROOT}/ddspp
|
||||
${SWA_THIRDPARTY_ROOT}/TinySHA1
|
||||
${LIBMSPACK_PATH}
|
||||
${Stb_INCLUDE_DIR}
|
||||
${SMOLV_SOURCE_DIR}
|
||||
${MINIAUDIO_INCLUDE_DIRS}
|
||||
${BC_DIFF_SOURCE_DIR}
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/api"
|
||||
${SWA_THIRDPARTY_INCLUDES}
|
||||
)
|
||||
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
find_package(X11 REQUIRED)
|
||||
target_include_directories(UnleashedRecomp PRIVATE ${X11_INCLUDE_DIR})
|
||||
target_link_libraries(UnleashedRecomp PRIVATE ${X11_LIBRARIES})
|
||||
endif()
|
||||
|
||||
target_precompile_headers(UnleashedRecomp PUBLIC ${SWA_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)
|
||||
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
|
||||
DEPENDS ${FILE_PATH}
|
||||
)
|
||||
if (SWA_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
|
||||
DEPENDS ${FILE_PATH}
|
||||
)
|
||||
target_sources(UnleashedRecomp PRIVATE ${FILE_PATH}.dxil.h)
|
||||
endif()
|
||||
add_custom_command(
|
||||
OUTPUT ${FILE_PATH}.spirv.h
|
||||
COMMAND ${DIRECTX_DXC_TOOL} -T ${TARGET_NAME} -HV 2021 -all-resources-bound -spirv -fvk-use-dx-layout ${ARGN} -Fh ${FILE_PATH}.spirv.h ${FILE_PATH} -Vn g_${VARIABLE_NAME}_spirv
|
||||
DEPENDS ${FILE_PATH}
|
||||
)
|
||||
target_sources(UnleashedRecomp PRIVATE ${FILE_PATH}.dxil.h ${FILE_PATH}.spirv.h)
|
||||
target_sources(UnleashedRecomp PRIVATE ${FILE_PATH}.spirv.h)
|
||||
endfunction()
|
||||
|
||||
function(compile_vertex_shader FILE_PATH)
|
||||
|
|
|
@ -2,14 +2,14 @@
|
|||
|
||||
#include "SWA.inl"
|
||||
|
||||
inline static void* __HH_ALLOC(const uint32_t in_Size)
|
||||
inline void* __HH_ALLOC(const uint32_t in_Size)
|
||||
{
|
||||
return GuestToHostFunction<void*>(0x82DFA0B0, in_Size, nullptr, 0, 0);
|
||||
return GuestToHostFunction<void*>(sub_822C0988, in_Size);
|
||||
}
|
||||
|
||||
inline static void __HH_FREE(const void* in_pData)
|
||||
inline void __HH_FREE(const void* in_pData)
|
||||
{
|
||||
GuestToHostFunction<void>(0x82DF9E50, in_pData);
|
||||
GuestToHostFunction<void>(sub_822C0270, in_pData);
|
||||
}
|
||||
|
||||
namespace Hedgehog::Base
|
||||
|
|
|
@ -6,38 +6,16 @@ namespace Hedgehog::Base
|
|||
{
|
||||
struct SStringHolder
|
||||
{
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
be<uint16_t> Length;
|
||||
be<uint16_t> RefCount;
|
||||
};
|
||||
|
||||
be<uint32_t> RefCountAndLength;
|
||||
};
|
||||
|
||||
char aStr[1u];
|
||||
be<uint32_t> RefCount;
|
||||
char aStr[];
|
||||
|
||||
static SStringHolder* GetHolder(const char* in_pStr);
|
||||
|
||||
static size_t GetMemorySize(const size_t in_Length);
|
||||
static size_t GetMemorySizeAligned(const size_t in_Length);
|
||||
|
||||
static SStringHolder* Make(const char* in_pStr, const size_t in_Length);
|
||||
|
||||
static SStringHolder* Concat(
|
||||
const char* in_pStrA, const size_t in_LengthA,
|
||||
const char* in_pStrB, const size_t in_LengthB);
|
||||
static SStringHolder* Make(const char* in_pStr);
|
||||
|
||||
void AddRef();
|
||||
void Release();
|
||||
|
||||
bool IsUnique() const;
|
||||
|
||||
bool TryInplaceAssign(const char* in_pStr, const size_t in_Length);
|
||||
bool TryInplaceAppend(const char* in_pStr, const size_t in_Length);
|
||||
bool TryInplacePrepend(const char* in_pStr, const size_t in_Length);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -2,66 +2,41 @@ namespace Hedgehog::Base
|
|||
{
|
||||
inline SStringHolder* SStringHolder::GetHolder(const char* in_pStr)
|
||||
{
|
||||
return (SStringHolder*)((size_t)in_pStr - sizeof(RefCountAndLength));
|
||||
return (SStringHolder*)((size_t)in_pStr - sizeof(RefCount));
|
||||
}
|
||||
|
||||
inline size_t SStringHolder::GetMemorySize(const size_t in_Length)
|
||||
inline SStringHolder* SStringHolder::Make(const char* in_pStr)
|
||||
{
|
||||
return sizeof(RefCountAndLength) + in_Length;
|
||||
}
|
||||
|
||||
inline size_t SStringHolder::GetMemorySizeAligned(const size_t in_Length)
|
||||
{
|
||||
return (GetMemorySize(in_Length) + 0x10) & ~0x0F;
|
||||
}
|
||||
|
||||
inline SStringHolder* SStringHolder::Make(const char* in_pStr, const size_t in_Length)
|
||||
{
|
||||
const size_t memSize = GetMemorySize(in_Length);
|
||||
const size_t memSizeAligned = GetMemorySizeAligned(in_Length);
|
||||
|
||||
auto pHolder = (SStringHolder*)__HH_ALLOC(memSizeAligned);
|
||||
auto pHolder = (SStringHolder*)__HH_ALLOC(sizeof(RefCount) + strlen(in_pStr) + 1);
|
||||
pHolder->RefCount = 1;
|
||||
pHolder->Length = (uint16_t)in_Length;
|
||||
|
||||
if (in_pStr)
|
||||
memcpy(pHolder->aStr, in_pStr, in_Length);
|
||||
|
||||
memset(&pHolder->aStr[in_Length], 0, memSizeAligned - memSize);
|
||||
|
||||
return pHolder;
|
||||
}
|
||||
|
||||
inline SStringHolder* SStringHolder::Concat(const char* in_pStrA, const size_t in_LengthA, const char* in_pStrB, const size_t in_LengthB)
|
||||
{
|
||||
SStringHolder* pHolder = Make(nullptr, in_LengthA + in_LengthB);
|
||||
|
||||
memcpy(pHolder->aStr, in_pStrA, in_LengthA);
|
||||
memcpy(&pHolder->aStr[in_LengthA], in_pStrB, in_LengthB);
|
||||
|
||||
strcpy(pHolder->aStr, in_pStr);
|
||||
return pHolder;
|
||||
}
|
||||
|
||||
inline void SStringHolder::AddRef()
|
||||
{
|
||||
uint32_t originalValue, incrementedValue;
|
||||
std::atomic_ref refCount(RefCount.value);
|
||||
|
||||
be<uint32_t> original, incremented;
|
||||
do
|
||||
{
|
||||
originalValue = RefCountAndLength.value;
|
||||
incrementedValue = ByteSwap(ByteSwap(originalValue) + 1);
|
||||
} while (InterlockedCompareExchange(reinterpret_cast<LONG*>(&RefCountAndLength), incrementedValue, originalValue) != originalValue);
|
||||
original = RefCount;
|
||||
incremented = original + 1;
|
||||
} while (!refCount.compare_exchange_weak(original.value, incremented.value));
|
||||
}
|
||||
|
||||
inline void SStringHolder::Release()
|
||||
{
|
||||
uint32_t originalValue, decrementedValue;
|
||||
std::atomic_ref refCount(RefCount.value);
|
||||
|
||||
be<uint32_t> original, decremented;
|
||||
do
|
||||
{
|
||||
originalValue = RefCountAndLength.value;
|
||||
decrementedValue = ByteSwap(ByteSwap(originalValue) - 1);
|
||||
} while (InterlockedCompareExchange(reinterpret_cast<LONG*>(&RefCountAndLength), decrementedValue, originalValue) != originalValue);
|
||||
original = RefCount;
|
||||
decremented = original - 1;
|
||||
} while (!refCount.compare_exchange_weak(original.value, decremented.value));
|
||||
|
||||
if ((decrementedValue & 0xFFFF0000) == 0)
|
||||
if (decremented == 0)
|
||||
__HH_FREE(this);
|
||||
}
|
||||
|
||||
|
@ -69,66 +44,4 @@ namespace Hedgehog::Base
|
|||
{
|
||||
return RefCount == 1;
|
||||
}
|
||||
|
||||
inline bool SStringHolder::TryInplaceAssign(const char* in_pStr, const size_t in_Length)
|
||||
{
|
||||
if (!IsUnique())
|
||||
return false;
|
||||
|
||||
const size_t memSizeAligned = GetMemorySizeAligned(in_Length);
|
||||
|
||||
if (memSizeAligned > GetMemorySizeAligned(Length))
|
||||
return false;
|
||||
|
||||
if (in_pStr)
|
||||
memcpy(aStr, in_pStr, in_Length);
|
||||
|
||||
memset(&aStr[in_Length], 0, memSizeAligned - GetMemorySize(in_Length));
|
||||
|
||||
Length = (uint16_t)in_Length;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool SStringHolder::TryInplaceAppend(const char* in_pStr, const size_t in_Length)
|
||||
{
|
||||
if (!IsUnique())
|
||||
return false;
|
||||
|
||||
const size_t memSizeAligned = GetMemorySizeAligned(Length + in_Length);
|
||||
|
||||
if (memSizeAligned > GetMemorySizeAligned(Length))
|
||||
return false;
|
||||
|
||||
if (in_pStr)
|
||||
memcpy(&aStr[Length], in_pStr, in_Length);
|
||||
|
||||
memset(&aStr[Length + in_Length], 0, memSizeAligned - GetMemorySize(Length + in_Length));
|
||||
|
||||
Length = (uint16_t)(Length + in_Length);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool SStringHolder::TryInplacePrepend(const char* in_pStr, const size_t in_Length)
|
||||
{
|
||||
if (!IsUnique())
|
||||
return false;
|
||||
|
||||
const size_t memSizeAligned = GetMemorySizeAligned(in_Length + Length);
|
||||
|
||||
if (memSizeAligned > GetMemorySizeAligned(Length))
|
||||
return false;
|
||||
|
||||
memmove(&aStr[in_Length], aStr, Length);
|
||||
|
||||
if (in_pStr)
|
||||
memcpy(aStr, in_pStr, in_Length);
|
||||
|
||||
memset(&aStr[in_Length + Length], 0, memSizeAligned - GetMemorySize(in_Length + Length));
|
||||
|
||||
Length = (uint16_t)(in_Length + Length);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,56 +32,6 @@ namespace Hedgehog::Base
|
|||
|
||||
const char* begin() const;
|
||||
const char* end() const;
|
||||
|
||||
CSharedString substr(size_t pos = 0, size_t len = npos) const;
|
||||
|
||||
size_t find(char c, size_t pos = 0) const;
|
||||
size_t find(const char* s, size_t pos = 0) const;
|
||||
size_t rfind(char c, size_t pos = npos) const;
|
||||
|
||||
size_t find_first_of(const char* s, size_t pos = 0) const;
|
||||
size_t find_last_of(const char* s, size_t pos = npos) const;
|
||||
size_t find_first_not_of(const char* s, size_t pos = 0) const;
|
||||
size_t find_last_not_of(const char* s, size_t pos = npos) const;
|
||||
|
||||
size_t find(const CSharedString& str, size_t pos = 0) const;
|
||||
size_t rfind(const CSharedString& str, size_t pos = npos) const;
|
||||
|
||||
size_t find_first_of(const CSharedString& str, size_t pos = 0) const;
|
||||
size_t find_last_of(const CSharedString& str, size_t pos = npos) const;
|
||||
size_t find_first_not_of(const CSharedString& str, size_t pos = 0) const;
|
||||
size_t find_last_not_of(const CSharedString& str, size_t pos = npos) const;
|
||||
|
||||
void assign(const CSharedString& in_rOther);
|
||||
void assign(const char* in_pStr);
|
||||
void assign(CSharedString&& io_rOther);
|
||||
|
||||
void append(const CSharedString& in_rOther);
|
||||
void append(const char* in_pStr);
|
||||
|
||||
void prepend(const CSharedString& in_rOther);
|
||||
void prepend(const char* in_pStr);
|
||||
|
||||
int compare(const CSharedString& in_rOther) const;
|
||||
|
||||
CSharedString& operator=(const CSharedString& in_rOther);
|
||||
CSharedString& operator=(const char* in_pStr);
|
||||
CSharedString& operator=(CSharedString&& io_rOther);
|
||||
CSharedString& operator+=(const CSharedString& in_rOther);
|
||||
CSharedString& operator+=(const char* in_pStr);
|
||||
|
||||
friend CSharedString operator+(const CSharedString& in_rLeft, const CSharedString& in_rRight);
|
||||
friend CSharedString operator+(const CSharedString& in_rLeft, const char* in_pRight);
|
||||
friend CSharedString operator+(const char* in_pLeft, const CSharedString& in_pRight);
|
||||
|
||||
bool operator>(const CSharedString& in_rOther) const;
|
||||
bool operator>=(const CSharedString& in_rOther) const;
|
||||
bool operator<(const CSharedString& in_rOther) const;
|
||||
bool operator<=(const CSharedString& in_rOther) const;
|
||||
bool operator==(const CSharedString& in_rOther) const;
|
||||
bool operator!=(const CSharedString& in_rOther) const;
|
||||
bool operator==(const char* in_pOther) const;
|
||||
bool operator!=(const char* in_pOther) const;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ namespace Hedgehog::Base
|
|||
size_t length;
|
||||
|
||||
if (in_pStr && (length = strlen(in_pStr)) != 0)
|
||||
m_pStr.ptr = g_memory.MapVirtual(SStringHolder::Make(in_pStr, length)->aStr);
|
||||
m_pStr.ptr = g_memory.MapVirtual(SStringHolder::Make(in_pStr)->aStr);
|
||||
}
|
||||
|
||||
inline CSharedString::CSharedString(const CSharedString& in_rOther) : m_pStr(in_rOther.m_pStr)
|
||||
|
@ -53,7 +53,7 @@ namespace Hedgehog::Base
|
|||
|
||||
inline size_t CSharedString::size() const
|
||||
{
|
||||
return GetHolder()->Length;
|
||||
return strlen(m_pStr);
|
||||
}
|
||||
|
||||
inline size_t CSharedString::length() const
|
||||
|
@ -73,367 +73,6 @@ namespace Hedgehog::Base
|
|||
|
||||
inline const char* CSharedString::end() const
|
||||
{
|
||||
return &m_pStr[GetHolder()->Length];
|
||||
}
|
||||
|
||||
inline CSharedString CSharedString::substr(size_t pos, size_t len) const
|
||||
{
|
||||
if (len == 0)
|
||||
return CSharedString();
|
||||
|
||||
if (len > (GetHolder()->Length - pos))
|
||||
len = GetHolder()->Length - pos;
|
||||
|
||||
if (pos == 0 && len == GetHolder()->Length)
|
||||
return *this;
|
||||
|
||||
return SStringHolder::Make(&m_pStr[pos], len);
|
||||
}
|
||||
|
||||
inline size_t CSharedString::find(char c, size_t pos) const
|
||||
{
|
||||
for (size_t i = pos; i < GetHolder()->Length; i++)
|
||||
{
|
||||
if (m_pStr[i] == c)
|
||||
return i;
|
||||
}
|
||||
|
||||
return npos;
|
||||
}
|
||||
|
||||
inline size_t CSharedString::find(const char* s, size_t pos) const
|
||||
{
|
||||
size_t len = strlen(s);
|
||||
for (size_t i = pos; i < GetHolder()->Length - len + 1; i++)
|
||||
{
|
||||
if (strncmp(m_pStr + i, s, len) == 0)
|
||||
return i;
|
||||
}
|
||||
|
||||
return npos;
|
||||
}
|
||||
|
||||
inline size_t CSharedString::rfind(char c, size_t pos) const
|
||||
{
|
||||
if (pos >= GetHolder()->Length)
|
||||
pos = GetHolder()->Length - 1;
|
||||
|
||||
for (size_t i = pos; i != static_cast<size_t>(-1); i--)
|
||||
{
|
||||
if (m_pStr[i] == c)
|
||||
return i;
|
||||
}
|
||||
|
||||
return npos;
|
||||
}
|
||||
|
||||
inline size_t CSharedString::find_first_of(const char* s, size_t pos) const
|
||||
{
|
||||
size_t len = strlen(s);
|
||||
for (size_t i = pos; i < GetHolder()->Length; i++)
|
||||
{
|
||||
for (size_t j = 0; j < len; j++)
|
||||
{
|
||||
if (m_pStr[i] == s[j])
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return npos;
|
||||
}
|
||||
|
||||
inline size_t CSharedString::find_last_of(const char* s, size_t pos) const
|
||||
{
|
||||
if (pos >= GetHolder()->Length)
|
||||
pos = GetHolder()->Length - 1;
|
||||
|
||||
size_t len = strlen(s);
|
||||
for (size_t i = pos; i != static_cast<size_t>(-1); i--)
|
||||
{
|
||||
for (size_t j = 0; j < len; j++)
|
||||
{
|
||||
if (m_pStr[i] == s[j])
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return npos;
|
||||
}
|
||||
|
||||
inline size_t CSharedString::find_first_not_of(const char* s, size_t pos) const
|
||||
{
|
||||
size_t len = strlen(s);
|
||||
for (size_t i = pos; i < GetHolder()->Length; i++)
|
||||
{
|
||||
bool found = false;
|
||||
for (size_t j = 0; j < len; j++)
|
||||
{
|
||||
if (m_pStr[i] == s[j])
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
return i;
|
||||
}
|
||||
|
||||
return npos;
|
||||
}
|
||||
|
||||
inline size_t CSharedString::find_last_not_of(const char* s, size_t pos) const
|
||||
{
|
||||
if (pos >= GetHolder()->Length)
|
||||
pos = GetHolder()->Length - 1;
|
||||
|
||||
size_t len = strlen(s);
|
||||
for (size_t i = pos; i != static_cast<size_t>(-1); i--)
|
||||
{
|
||||
bool found = false;
|
||||
for (size_t j = 0; j < len; j++)
|
||||
{
|
||||
if (m_pStr[i] == s[j])
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
return i;
|
||||
}
|
||||
|
||||
return npos;
|
||||
}
|
||||
|
||||
inline size_t CSharedString::find(const CSharedString& str, size_t pos) const
|
||||
{
|
||||
return find(str.c_str(), pos);
|
||||
}
|
||||
|
||||
inline size_t CSharedString::find_first_of(const CSharedString& str, size_t pos) const
|
||||
{
|
||||
return find_first_of(str.c_str(), pos);
|
||||
}
|
||||
|
||||
inline size_t CSharedString::find_last_of(const CSharedString& str, size_t pos) const
|
||||
{
|
||||
return find_last_of(str.c_str(), pos);
|
||||
}
|
||||
|
||||
inline size_t CSharedString::find_first_not_of(const CSharedString& str, size_t pos) const
|
||||
{
|
||||
return find_first_not_of(str.c_str(), pos);
|
||||
}
|
||||
|
||||
inline size_t CSharedString::find_last_not_of(const CSharedString& str, size_t pos) const
|
||||
{
|
||||
return find_last_not_of(str.c_str(), pos);
|
||||
}
|
||||
|
||||
inline void CSharedString::assign(const CSharedString& in_rOther)
|
||||
{
|
||||
GetHolder()->Release();
|
||||
m_pStr = in_rOther.m_pStr;
|
||||
GetHolder()->AddRef();
|
||||
}
|
||||
|
||||
inline void CSharedString::assign(const char* in_pStr)
|
||||
{
|
||||
size_t length;
|
||||
|
||||
if (in_pStr && (length = strlen(in_pStr)) != 0)
|
||||
{
|
||||
if (!GetHolder()->TryInplaceAssign(in_pStr, length))
|
||||
{
|
||||
GetHolder()->Release();
|
||||
m_pStr = SStringHolder::Make(in_pStr, length)->aStr;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GetHolder()->Release();
|
||||
m_pStr = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
inline void CSharedString::assign(CSharedString&& io_rOther)
|
||||
{
|
||||
m_pStr = io_rOther.m_pStr;
|
||||
io_rOther.m_pStr = nullptr;
|
||||
}
|
||||
|
||||
inline void CSharedString::append(const CSharedString& in_rOther)
|
||||
{
|
||||
if (!GetHolder()->TryInplaceAppend(in_rOther.GetHolder()->aStr, in_rOther.GetHolder()->Length))
|
||||
{
|
||||
SStringHolder* pHolder = SStringHolder::Concat(
|
||||
GetHolder()->aStr, GetHolder()->Length,
|
||||
in_rOther.GetHolder()->aStr, in_rOther.GetHolder()->Length);
|
||||
|
||||
GetHolder()->Release();
|
||||
m_pStr = pHolder->aStr;
|
||||
}
|
||||
}
|
||||
|
||||
inline void CSharedString::append(const char* in_pStr)
|
||||
{
|
||||
size_t length;
|
||||
|
||||
if (in_pStr && (length = strlen(in_pStr)) != 0)
|
||||
{
|
||||
if (!GetHolder()->TryInplaceAppend(in_pStr, length))
|
||||
{
|
||||
SStringHolder* pHolder = SStringHolder::Concat(
|
||||
GetHolder()->aStr, GetHolder()->Length, in_pStr, length);
|
||||
|
||||
GetHolder()->Release();
|
||||
m_pStr = pHolder->aStr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void CSharedString::prepend(const CSharedString& in_rOther)
|
||||
{
|
||||
if (!GetHolder()->TryInplacePrepend(in_rOther.GetHolder()->aStr, in_rOther.GetHolder()->Length))
|
||||
{
|
||||
SStringHolder* pHolder = SStringHolder::Concat(
|
||||
in_rOther.GetHolder()->aStr, in_rOther.GetHolder()->Length,
|
||||
GetHolder()->aStr, GetHolder()->Length);
|
||||
|
||||
GetHolder()->Release();
|
||||
m_pStr = pHolder->aStr;
|
||||
}
|
||||
}
|
||||
|
||||
inline void CSharedString::prepend(const char* in_pStr)
|
||||
{
|
||||
size_t length;
|
||||
|
||||
if (in_pStr && (length = strlen(in_pStr)) != 0)
|
||||
{
|
||||
if (!GetHolder()->TryInplacePrepend(in_pStr, length))
|
||||
{
|
||||
SStringHolder* pHolder = SStringHolder::Concat(
|
||||
in_pStr, length, GetHolder()->aStr, GetHolder()->Length);
|
||||
|
||||
GetHolder()->Release();
|
||||
m_pStr = pHolder->aStr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline int CSharedString::compare(const CSharedString& in_rOther) const
|
||||
{
|
||||
// TODO: DO NOT PASS BY REFERENCE.
|
||||
return GuestToHostFunction<int>(0x82DFB028, this, &in_rOther);
|
||||
}
|
||||
|
||||
inline CSharedString& CSharedString::operator=(const CSharedString& in_rOther)
|
||||
{
|
||||
assign(in_rOther);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline CSharedString& CSharedString::operator=(const char* in_pStr)
|
||||
{
|
||||
assign(in_pStr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline CSharedString& CSharedString::operator=(CSharedString&& io_rOther)
|
||||
{
|
||||
assign(std::move(io_rOther));
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline CSharedString& CSharedString::operator+=(const CSharedString& in_rOther)
|
||||
{
|
||||
append(in_rOther);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline CSharedString& CSharedString::operator+=(const char* in_pStr)
|
||||
{
|
||||
append(in_pStr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline CSharedString operator+(const CSharedString& in_rLeft, const CSharedString& in_rRight)
|
||||
{
|
||||
return SStringHolder::Concat(
|
||||
in_rLeft.GetHolder()->aStr, in_rLeft.GetHolder()->Length,
|
||||
in_rRight.GetHolder()->aStr, in_rRight.GetHolder()->Length);
|
||||
}
|
||||
|
||||
inline CSharedString operator+(const CSharedString& in_rLeft, const char* in_pRight)
|
||||
{
|
||||
size_t length;
|
||||
|
||||
if (in_pRight && (length = strlen(in_pRight)) != 0)
|
||||
{
|
||||
return SStringHolder::Concat(
|
||||
in_rLeft.GetHolder()->aStr, in_rLeft.GetHolder()->Length, in_pRight, length);
|
||||
}
|
||||
else
|
||||
{
|
||||
return in_rLeft;
|
||||
}
|
||||
}
|
||||
|
||||
inline CSharedString operator+(const char* in_pLeft, const CSharedString& in_pRight)
|
||||
{
|
||||
size_t length;
|
||||
|
||||
if (in_pLeft && (length = strlen(in_pLeft)) != 0)
|
||||
{
|
||||
return SStringHolder::Concat(
|
||||
in_pLeft, length, in_pRight.GetHolder()->aStr, in_pRight.GetHolder()->Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
return in_pRight;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool CSharedString::operator>(const CSharedString& in_rOther) const
|
||||
{
|
||||
return compare(in_rOther) > 0;
|
||||
}
|
||||
|
||||
inline bool CSharedString::operator>=(const CSharedString& in_rOther) const
|
||||
{
|
||||
return compare(in_rOther) >= 0;
|
||||
}
|
||||
|
||||
inline bool CSharedString::operator<(const CSharedString& in_rOther) const
|
||||
{
|
||||
return compare(in_rOther) < 0;
|
||||
}
|
||||
|
||||
inline bool CSharedString::operator<=(const CSharedString& in_rOther) const
|
||||
{
|
||||
return compare(in_rOther) <= 0;
|
||||
}
|
||||
|
||||
inline bool CSharedString::operator==(const CSharedString& in_rOther) const
|
||||
{
|
||||
return compare(in_rOther) == 0;
|
||||
}
|
||||
|
||||
inline bool CSharedString::operator!=(const CSharedString& in_rOther) const
|
||||
{
|
||||
return !(*this == in_rOther);
|
||||
}
|
||||
|
||||
inline bool CSharedString::operator==(const char* in_pOther) const
|
||||
{
|
||||
return strcmp(c_str(), in_pOther) == 0;
|
||||
}
|
||||
|
||||
inline bool CSharedString::operator!=(const char* in_pOther) const
|
||||
{
|
||||
return !(*this == in_pOther);
|
||||
return &m_pStr[size()];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,22 +28,26 @@ namespace boost
|
|||
|
||||
void add_ref()
|
||||
{
|
||||
std::atomic_ref useCount(use_count_.value);
|
||||
|
||||
be<uint32_t> original, incremented;
|
||||
do
|
||||
{
|
||||
original = use_count_;
|
||||
incremented = original + 1;
|
||||
} while (InterlockedCompareExchange((unsigned long*)&use_count_, incremented.value, original.value) != original.value);
|
||||
} while (!useCount.compare_exchange_weak(original.value, incremented.value));
|
||||
}
|
||||
|
||||
void release()
|
||||
{
|
||||
std::atomic_ref useCount(use_count_.value);
|
||||
|
||||
be<uint32_t> original, decremented;
|
||||
do
|
||||
{
|
||||
original = use_count_;
|
||||
decremented = original - 1;
|
||||
} while (InterlockedCompareExchange((unsigned long*)&use_count_, decremented.value, original.value) != original.value);
|
||||
} while (!useCount.compare_exchange_weak(original.value, decremented.value));
|
||||
|
||||
if (decremented == 0)
|
||||
{
|
||||
|
@ -54,12 +58,14 @@ namespace boost
|
|||
|
||||
void weak_release()
|
||||
{
|
||||
std::atomic_ref weakCount(weak_count_.value);
|
||||
|
||||
be<uint32_t> original, decremented;
|
||||
do
|
||||
{
|
||||
original = weak_count_;
|
||||
decremented = original - 1;
|
||||
} while (InterlockedCompareExchange((unsigned long*)&weak_count_, decremented.value, original.value) != original.value);
|
||||
} while (!weakCount.compare_exchange_weak(original.value, decremented.value));
|
||||
|
||||
if (decremented == 0)
|
||||
{
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#include <app.h>
|
||||
#include <install/installer.h>
|
||||
#include <kernel/function.h>
|
||||
#include <ui/window.h>
|
||||
#include <ui/game_window.h>
|
||||
#include <patches/audio_patches.h>
|
||||
#include <user/config.h>
|
||||
#include <os/process.h>
|
||||
|
@ -16,9 +16,11 @@ void App::Exit()
|
|||
{
|
||||
Config::Save();
|
||||
|
||||
#if _WIN32
|
||||
ExitProcess(0);
|
||||
#ifdef _WIN32
|
||||
timeEndPeriod(1);
|
||||
#endif
|
||||
|
||||
std::_Exit(0);
|
||||
}
|
||||
|
||||
// CApplication::Ctor
|
||||
|
@ -41,7 +43,7 @@ PPC_FUNC(sub_822C1130)
|
|||
SDL_PumpEvents();
|
||||
SDL_FlushEvents(SDL_FIRSTEVENT, SDL_LASTEVENT);
|
||||
|
||||
Window::Update();
|
||||
GameWindow::Update();
|
||||
AudioPatches::Update(App::s_deltaTime);
|
||||
|
||||
__imp__sub_822C1130(ctx, base);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <user/config_detail.h>
|
||||
#include <user/config.h>
|
||||
|
||||
class App
|
||||
{
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
std::ofstream g_audioDumpStream;
|
||||
#endif
|
||||
|
||||
uint32_t XAudioRegisterRenderDriverClient(XLPDWORD callback, XLPDWORD driver)
|
||||
uint32_t XAudioRegisterRenderDriverClient(be<uint32_t>* callback, be<uint32_t>* driver)
|
||||
{
|
||||
#ifdef AUDIO_DUMP_SAMPLES_PATH
|
||||
g_audioDumpStream.open(AUDIO_DUMP_SAMPLES_PATH, std::ios::binary);
|
||||
|
@ -25,7 +25,7 @@ uint32_t XAudioRegisterRenderDriverClient(XLPDWORD callback, XLPDWORD driver)
|
|||
return 0;
|
||||
}
|
||||
|
||||
uint32_t XAudioUnregisterRenderDriverClient(DWORD driver)
|
||||
uint32_t XAudioUnregisterRenderDriverClient(uint32_t driver)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -13,6 +13,6 @@ void XAudioRegisterClient(PPCFunc* callback, uint32_t param);
|
|||
void XAudioSubmitFrame(void* samples);
|
||||
#endif
|
||||
|
||||
uint32_t XAudioRegisterRenderDriverClient(XLPDWORD callback, XLPDWORD driver);
|
||||
uint32_t XAudioUnregisterRenderDriverClient(DWORD driver);
|
||||
uint32_t XAudioRegisterRenderDriverClient(be<uint32_t>* callback, be<uint32_t>* driver);
|
||||
uint32_t XAudioUnregisterRenderDriverClient(uint32_t driver);
|
||||
uint32_t XAudioSubmitRenderDriverFrame(uint32_t driver, void* samples);
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
#include "miniaudio_driver.h"
|
||||
#include <cpu/code_cache.h>
|
||||
#include <cpu/guest_thread.h>
|
||||
#include <cpu/guest_code.h>
|
||||
#include <kernel/heap.h>
|
||||
|
||||
static PPCFunc* g_clientCallback{};
|
||||
static DWORD g_clientCallbackParam{}; // pointer in guest memory
|
||||
static ma_device g_audioDevice{};
|
||||
static std::unique_ptr<GuestThreadContext> g_audioCtx;
|
||||
static uint32_t* g_audioOutput;
|
||||
|
||||
static void AudioCallback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
|
||||
{
|
||||
if (g_audioCtx == nullptr)
|
||||
g_audioCtx = std::make_unique<GuestThreadContext>(0);
|
||||
|
||||
g_audioCtx->ppcContext.r3.u64 = g_clientCallbackParam;
|
||||
g_audioOutput = reinterpret_cast<uint32_t*>(pOutput);
|
||||
(*g_clientCallback)(g_audioCtx->ppcContext, reinterpret_cast<uint8_t*>(g_memory.base));
|
||||
}
|
||||
|
||||
void XAudioInitializeSystem()
|
||||
{
|
||||
ma_device_config deviceConfig = ma_device_config_init(ma_device_type_playback);
|
||||
deviceConfig.sampleRate = XAUDIO_SAMPLES_HZ;
|
||||
deviceConfig.periodSizeInFrames = XAUDIO_NUM_SAMPLES;
|
||||
deviceConfig.noPreSilencedOutputBuffer = true;
|
||||
deviceConfig.dataCallback = AudioCallback;
|
||||
deviceConfig.playback.format = ma_format_f32;
|
||||
deviceConfig.playback.channels = XAUDIO_NUM_CHANNELS;
|
||||
ma_device_init(nullptr, &deviceConfig, &g_audioDevice);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
ma_device_start(&g_audioDevice);
|
||||
}
|
||||
|
||||
void XAudioSubmitFrame(void* samples)
|
||||
{
|
||||
for (size_t i = 0; i < XAUDIO_NUM_SAMPLES; i++)
|
||||
{
|
||||
for (size_t j = 0; j < XAUDIO_NUM_CHANNELS; j++)
|
||||
g_audioOutput[i * XAUDIO_NUM_CHANNELS + j] = ByteSwap(((uint32_t*)samples)[j * XAUDIO_NUM_SAMPLES + i]);
|
||||
}
|
||||
}
|
128
UnleashedRecomp/apu/driver/sdl2_driver.cpp
Normal file
128
UnleashedRecomp/apu/driver/sdl2_driver.cpp
Normal file
|
@ -0,0 +1,128 @@
|
|||
#include "sdl2_driver.h"
|
||||
#include <cpu/code_cache.h>
|
||||
#include <cpu/guest_thread.h>
|
||||
#include <cpu/guest_code.h>
|
||||
#include <kernel/heap.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()
|
||||
{
|
||||
SDL_SetHint(SDL_HINT_AUDIO_CATEGORY, "playback");
|
||||
SDL_SetHint(SDL_HINT_AUDIO_DEVICE_APP_NAME, "Unleashed Recompiled");
|
||||
SDL_InitSubSystem(SDL_INIT_AUDIO);
|
||||
|
||||
SDL_AudioSpec desired{}, obtained{};
|
||||
desired.freq = XAUDIO_SAMPLES_HZ;
|
||||
desired.format = AUDIO_F32SYS;
|
||||
desired.channels = XAUDIO_NUM_CHANNELS;
|
||||
desired.samples = XAUDIO_NUM_SAMPLES;
|
||||
g_audioDevice = SDL_OpenAudioDevice(nullptr, 0, &desired, &obtained, SDL_AUDIO_ALLOW_CHANNELS_CHANGE);
|
||||
|
||||
if (obtained.channels != 2 && obtained.channels != XAUDIO_NUM_CHANNELS)
|
||||
{
|
||||
SDL_CloseAudioDevice(g_audioDevice);
|
||||
g_audioDevice = SDL_OpenAudioDevice(nullptr, 0, &desired, &obtained, 0);
|
||||
}
|
||||
|
||||
g_downMixToStereo = (obtained.channels == 2);
|
||||
}
|
||||
|
||||
static std::unique_ptr<std::thread> g_audioThread;
|
||||
|
||||
static void AudioThread()
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
GuestThreadContext ctx(0);
|
||||
|
||||
size_t channels = g_downMixToStereo ? 2 : XAUDIO_NUM_CHANNELS;
|
||||
|
||||
constexpr double INTERVAL = double(XAUDIO_NUM_SAMPLES) / double(XAUDIO_SAMPLES_HZ);
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
size_t iteration = 1;
|
||||
|
||||
while (true)
|
||||
{
|
||||
uint32_t queuedAudioSize = SDL_GetQueuedAudioSize(g_audioDevice);
|
||||
constexpr size_t MAX_LATENCY = 10;
|
||||
const size_t callbackAudioSize = channels * XAUDIO_NUM_SAMPLES * sizeof(float);
|
||||
|
||||
if ((queuedAudioSize / callbackAudioSize) <= MAX_LATENCY)
|
||||
{
|
||||
ctx.ppcContext.r3.u32 = g_clientCallbackParam;
|
||||
g_clientCallback(ctx.ppcContext, reinterpret_cast<uint8_t*>(g_memory.base));
|
||||
}
|
||||
|
||||
auto next = start + std::chrono::duration<double>(iteration * INTERVAL);
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
|
||||
if ((next - now) > 1s)
|
||||
next = now;
|
||||
|
||||
std::this_thread::sleep_until(next);
|
||||
|
||||
iteration = std::chrono::duration<double>(std::chrono::steady_clock::now() - start).count() / INTERVAL + 1;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
SDL_PauseAudioDevice(g_audioDevice, 0);
|
||||
g_audioThread = std::make_unique<std::thread>(AudioThread);
|
||||
}
|
||||
|
||||
void XAudioSubmitFrame(void* samples)
|
||||
{
|
||||
if (g_downMixToStereo)
|
||||
{
|
||||
// 0: left 1.0f, right 0.0f
|
||||
// 1: left 0.0f, right 1.0f
|
||||
// 2: left 0.75f, right 0.75f
|
||||
// 3: left 0.0f, right 0.0f
|
||||
// 4: left 1.0f, right 0.0f
|
||||
// 5: left 0.0f, right 1.0f
|
||||
|
||||
auto floatSamples = reinterpret_cast<be<float>*>(samples);
|
||||
|
||||
std::array<float, 2 * XAUDIO_NUM_SAMPLES> audioFrames;
|
||||
|
||||
for (size_t i = 0; i < XAUDIO_NUM_SAMPLES; i++)
|
||||
{
|
||||
float ch0 = floatSamples[0 * XAUDIO_NUM_SAMPLES + i];
|
||||
float ch1 = floatSamples[1 * XAUDIO_NUM_SAMPLES + i];
|
||||
float ch2 = floatSamples[2 * XAUDIO_NUM_SAMPLES + i];
|
||||
float ch3 = floatSamples[3 * XAUDIO_NUM_SAMPLES + i];
|
||||
float ch4 = floatSamples[4 * XAUDIO_NUM_SAMPLES + i];
|
||||
float ch5 = floatSamples[5 * XAUDIO_NUM_SAMPLES + i];
|
||||
|
||||
audioFrames[i * 2 + 0] = ch0 + ch2 * 0.75f + ch4;
|
||||
audioFrames[i * 2 + 1] = ch1 + ch2 * 0.75f + ch5;
|
||||
}
|
||||
|
||||
SDL_QueueAudio(g_audioDevice, &audioFrames, sizeof(audioFrames));
|
||||
}
|
||||
else
|
||||
{
|
||||
auto rawSamples = reinterpret_cast<be<uint32_t>*>(samples);
|
||||
|
||||
std::array<uint32_t, XAUDIO_NUM_CHANNELS * XAUDIO_NUM_SAMPLES> audioFrames;
|
||||
|
||||
for (size_t i = 0; i < XAUDIO_NUM_SAMPLES; i++)
|
||||
{
|
||||
for (size_t j = 0; j < XAUDIO_NUM_CHANNELS; j++)
|
||||
audioFrames[i * XAUDIO_NUM_CHANNELS + j] = rawSamples[j * XAUDIO_NUM_SAMPLES + i];
|
||||
}
|
||||
|
||||
SDL_QueueAudio(g_audioDevice, &audioFrames, sizeof(audioFrames));
|
||||
}
|
||||
}
|
|
@ -90,7 +90,7 @@ void XAudioInitializeSystem()
|
|||
g_sourceVoice->Start();
|
||||
|
||||
KeInsertHostFunction(XAUDIO_DRIVER_KEY, DriverLoop);
|
||||
GuestThread::Start(XAUDIO_DRIVER_KEY, 0, 0, &g_driverThread);
|
||||
GuestThread::Start({ XAUDIO_DRIVER_KEY, 0, 0 }, nullptr);
|
||||
}
|
||||
|
||||
void XAudioRegisterClient(PPCFunc* callback, uint32_t param)
|
||||
|
|
|
@ -10,82 +10,6 @@
|
|||
#include <res/sounds/sys_actstg_pausewinclose.ogg.h>
|
||||
#include <res/sounds/sys_actstg_pausewinopen.ogg.h>
|
||||
|
||||
#pragma region libvorbis
|
||||
static ma_result ma_decoding_backend_init__libvorbis(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
|
||||
{
|
||||
ma_result result;
|
||||
ma_libvorbis* pVorbis;
|
||||
|
||||
(void)pUserData;
|
||||
|
||||
pVorbis = (ma_libvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks);
|
||||
if (pVorbis == NULL) {
|
||||
return MA_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
result = ma_libvorbis_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pVorbis);
|
||||
if (result != MA_SUCCESS) {
|
||||
ma_free(pVorbis, pAllocationCallbacks);
|
||||
return result;
|
||||
}
|
||||
|
||||
*ppBackend = pVorbis;
|
||||
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
|
||||
static ma_result ma_decoding_backend_init_file__libvorbis(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
|
||||
{
|
||||
ma_result result;
|
||||
ma_libvorbis* pVorbis;
|
||||
|
||||
(void)pUserData;
|
||||
|
||||
pVorbis = (ma_libvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks);
|
||||
if (pVorbis == NULL) {
|
||||
return MA_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
result = ma_libvorbis_init_file(pFilePath, pConfig, pAllocationCallbacks, pVorbis);
|
||||
if (result != MA_SUCCESS) {
|
||||
ma_free(pVorbis, pAllocationCallbacks);
|
||||
return result;
|
||||
}
|
||||
|
||||
*ppBackend = pVorbis;
|
||||
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
|
||||
static void ma_decoding_backend_uninit__libvorbis(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)
|
||||
{
|
||||
ma_libvorbis* pVorbis = (ma_libvorbis*)pBackend;
|
||||
|
||||
(void)pUserData;
|
||||
|
||||
ma_libvorbis_uninit(pVorbis, pAllocationCallbacks);
|
||||
ma_free(pVorbis, pAllocationCallbacks);
|
||||
}
|
||||
|
||||
static ma_result ma_decoding_backend_get_channel_map__libvorbis(void* pUserData, ma_data_source* pBackend, ma_channel* pChannelMap, size_t channelMapCap)
|
||||
{
|
||||
ma_libvorbis* pVorbis = (ma_libvorbis*)pBackend;
|
||||
|
||||
(void)pUserData;
|
||||
|
||||
return ma_libvorbis_get_data_format(pVorbis, NULL, NULL, NULL, pChannelMap, channelMapCap);
|
||||
}
|
||||
|
||||
static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_libvorbis =
|
||||
{
|
||||
ma_decoding_backend_init__libvorbis,
|
||||
ma_decoding_backend_init_file__libvorbis,
|
||||
NULL, /* onInitFileW() */
|
||||
NULL, /* onInitMemory() */
|
||||
ma_decoding_backend_uninit__libvorbis
|
||||
};
|
||||
#pragma endregion
|
||||
|
||||
enum class EmbeddedSound
|
||||
{
|
||||
SysWorldMapCursor,
|
||||
|
@ -100,13 +24,9 @@ enum class EmbeddedSound
|
|||
|
||||
struct EmbeddedSoundData
|
||||
{
|
||||
static const int SimultaneousLimit = 4;
|
||||
std::array<std::unique_ptr<ma_sound>, SimultaneousLimit> sounds;
|
||||
std::array<std::unique_ptr<ma_decoder>, SimultaneousLimit> decoders;
|
||||
int oldestIndex = 0;
|
||||
Mix_Chunk* chunk{};
|
||||
};
|
||||
|
||||
static ma_engine g_audioEngine = {};
|
||||
static std::array<EmbeddedSoundData, size_t(EmbeddedSound::Count)> g_embeddedSoundData = {};
|
||||
static const std::unordered_map<std::string_view, EmbeddedSound> g_embeddedSoundMap =
|
||||
{
|
||||
|
@ -119,114 +39,61 @@ static const std::unordered_map<std::string_view, EmbeddedSound> g_embeddedSound
|
|||
{ "sys_actstg_pausewinopen", EmbeddedSound::SysActStgPauseWinOpen },
|
||||
};
|
||||
|
||||
static size_t g_channelIndex;
|
||||
|
||||
static void PlayEmbeddedSound(EmbeddedSound s)
|
||||
{
|
||||
EmbeddedSoundData &data = g_embeddedSoundData[size_t(s)];
|
||||
int pickedIndex = -1;
|
||||
for (int i = 0; (i < EmbeddedSoundData::SimultaneousLimit) && (pickedIndex < 0); i++)
|
||||
if (data.chunk == nullptr)
|
||||
{
|
||||
if (data.sounds[i] == nullptr)
|
||||
// The sound hasn't been created yet, create it and pick it.
|
||||
const void *soundData = nullptr;
|
||||
size_t soundDataSize = 0;
|
||||
switch (s)
|
||||
{
|
||||
// The sound hasn't been created yet, create it and pick it.
|
||||
const void *soundData = nullptr;
|
||||
size_t soundDataSize = 0;
|
||||
switch (s)
|
||||
{
|
||||
case EmbeddedSound::SysWorldMapCursor:
|
||||
soundData = g_sys_worldmap_cursor;
|
||||
soundDataSize = sizeof(g_sys_worldmap_cursor);
|
||||
break;
|
||||
case EmbeddedSound::SysWorldMapFinalDecide:
|
||||
soundData = g_sys_worldmap_finaldecide;
|
||||
soundDataSize = sizeof(g_sys_worldmap_finaldecide);
|
||||
break;
|
||||
case EmbeddedSound::SysActStgPauseCansel:
|
||||
soundData = g_sys_actstg_pausecansel;
|
||||
soundDataSize = sizeof(g_sys_actstg_pausecansel);
|
||||
break;
|
||||
case EmbeddedSound::SysActStgPauseCursor:
|
||||
soundData = g_sys_actstg_pausecursor;
|
||||
soundDataSize = sizeof(g_sys_actstg_pausecursor);
|
||||
break;
|
||||
case EmbeddedSound::SysActStgPauseDecide:
|
||||
soundData = g_sys_actstg_pausedecide;
|
||||
soundDataSize = sizeof(g_sys_actstg_pausedecide);
|
||||
break;
|
||||
case EmbeddedSound::SysActStgPauseWinClose:
|
||||
soundData = g_sys_actstg_pausewinclose;
|
||||
soundDataSize = sizeof(g_sys_actstg_pausewinclose);
|
||||
break;
|
||||
case EmbeddedSound::SysActStgPauseWinOpen:
|
||||
soundData = g_sys_actstg_pausewinopen;
|
||||
soundDataSize = sizeof(g_sys_actstg_pausewinopen);
|
||||
break;
|
||||
default:
|
||||
assert(false && "Unknown embedded sound.");
|
||||
return;
|
||||
}
|
||||
|
||||
ma_decoding_backend_vtable* pCustomBackendVTables[] =
|
||||
{
|
||||
&g_ma_decoding_backend_vtable_libvorbis
|
||||
};
|
||||
|
||||
ma_decoder_config decoderConfig = ma_decoder_config_init_default();
|
||||
decoderConfig.pCustomBackendUserData = NULL;
|
||||
decoderConfig.ppCustomBackendVTables = pCustomBackendVTables;
|
||||
decoderConfig.customBackendCount = std::size(pCustomBackendVTables);
|
||||
|
||||
ma_result res;
|
||||
data.decoders[i] = std::make_unique<ma_decoder>();
|
||||
res = ma_decoder_init_memory(soundData, soundDataSize, &decoderConfig, data.decoders[i].get());
|
||||
if (res != MA_SUCCESS)
|
||||
{
|
||||
fprintf(stderr, "ma_decoder_init_memory failed with error code %d.\n", res);
|
||||
return;
|
||||
}
|
||||
|
||||
data.sounds[i] = std::make_unique<ma_sound>();
|
||||
res = ma_sound_init_from_data_source(&g_audioEngine, data.decoders[i].get(), MA_SOUND_FLAG_DECODE, nullptr, data.sounds[i].get());
|
||||
if (res != MA_SUCCESS)
|
||||
{
|
||||
fprintf(stderr, "ma_sound_init_from_data_source failed with error code %d.\n", res);
|
||||
return;
|
||||
}
|
||||
|
||||
pickedIndex = i;
|
||||
}
|
||||
else if (ma_sound_at_end(data.sounds[i].get()))
|
||||
{
|
||||
// A sound has reached the end, pick it.
|
||||
pickedIndex = i;
|
||||
case EmbeddedSound::SysWorldMapCursor:
|
||||
soundData = g_sys_worldmap_cursor;
|
||||
soundDataSize = sizeof(g_sys_worldmap_cursor);
|
||||
break;
|
||||
case EmbeddedSound::SysWorldMapFinalDecide:
|
||||
soundData = g_sys_worldmap_finaldecide;
|
||||
soundDataSize = sizeof(g_sys_worldmap_finaldecide);
|
||||
break;
|
||||
case EmbeddedSound::SysActStgPauseCansel:
|
||||
soundData = g_sys_actstg_pausecansel;
|
||||
soundDataSize = sizeof(g_sys_actstg_pausecansel);
|
||||
break;
|
||||
case EmbeddedSound::SysActStgPauseCursor:
|
||||
soundData = g_sys_actstg_pausecursor;
|
||||
soundDataSize = sizeof(g_sys_actstg_pausecursor);
|
||||
break;
|
||||
case EmbeddedSound::SysActStgPauseDecide:
|
||||
soundData = g_sys_actstg_pausedecide;
|
||||
soundDataSize = sizeof(g_sys_actstg_pausedecide);
|
||||
break;
|
||||
case EmbeddedSound::SysActStgPauseWinClose:
|
||||
soundData = g_sys_actstg_pausewinclose;
|
||||
soundDataSize = sizeof(g_sys_actstg_pausewinclose);
|
||||
break;
|
||||
case EmbeddedSound::SysActStgPauseWinOpen:
|
||||
soundData = g_sys_actstg_pausewinopen;
|
||||
soundDataSize = sizeof(g_sys_actstg_pausewinopen);
|
||||
break;
|
||||
default:
|
||||
assert(false && "Unknown embedded sound.");
|
||||
return;
|
||||
}
|
||||
|
||||
data.chunk = Mix_LoadWAV_RW(SDL_RWFromConstMem(soundData, soundDataSize), 1);
|
||||
}
|
||||
|
||||
if (pickedIndex < 0)
|
||||
{
|
||||
// No free slots are available, pick the oldest one.
|
||||
pickedIndex = data.oldestIndex;
|
||||
data.oldestIndex = (data.oldestIndex + 1) % EmbeddedSoundData::SimultaneousLimit;
|
||||
}
|
||||
|
||||
if (data.sounds[pickedIndex] != nullptr)
|
||||
{
|
||||
ma_sound_set_volume(data.sounds[pickedIndex].get(), Config::EffectsVolume);
|
||||
ma_sound_seek_to_pcm_frame(data.sounds[pickedIndex].get(), 0);
|
||||
ma_sound_start(data.sounds[pickedIndex].get());
|
||||
}
|
||||
Mix_PlayChannel(g_channelIndex % MIX_CHANNELS, data.chunk, 0);
|
||||
++g_channelIndex;
|
||||
}
|
||||
|
||||
void EmbeddedPlayer::Init()
|
||||
{
|
||||
ma_engine_config engineConfig = ma_engine_config_init();
|
||||
engineConfig.channels = XAUDIO_NUM_CHANNELS;
|
||||
engineConfig.sampleRate = XAUDIO_SAMPLES_HZ;
|
||||
|
||||
ma_result res = ma_engine_init(&engineConfig, &g_audioEngine);
|
||||
if (res != MA_SUCCESS)
|
||||
{
|
||||
fprintf(stderr, "ma_engine_init failed with error code %d.\n", res);
|
||||
}
|
||||
Mix_OpenAudio(XAUDIO_SAMPLES_HZ, AUDIO_F32SYS, XAUDIO_NUM_CHANNELS, 256);
|
||||
|
||||
s_isActive = true;
|
||||
}
|
||||
|
@ -241,11 +108,6 @@ void EmbeddedPlayer::Play(const char *name)
|
|||
return;
|
||||
}
|
||||
|
||||
if (g_audioEngine.pDevice == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PlayEmbeddedSound(it->second);
|
||||
}
|
||||
|
||||
|
@ -253,37 +115,12 @@ void EmbeddedPlayer::Shutdown()
|
|||
{
|
||||
for (EmbeddedSoundData &data : g_embeddedSoundData)
|
||||
{
|
||||
for (auto &sound : data.sounds)
|
||||
{
|
||||
if (sound != nullptr)
|
||||
{
|
||||
if (sound->pDataSource != nullptr)
|
||||
{
|
||||
ma_sound_uninit(sound.get());
|
||||
}
|
||||
|
||||
sound.reset();
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &decoder : data.decoders)
|
||||
{
|
||||
if (decoder != nullptr)
|
||||
{
|
||||
if (decoder->pBackend != nullptr)
|
||||
{
|
||||
ma_decoder_uninit(decoder.get());
|
||||
}
|
||||
|
||||
decoder.reset();
|
||||
}
|
||||
}
|
||||
if (data.chunk != nullptr)
|
||||
Mix_FreeChunk(data.chunk);
|
||||
}
|
||||
|
||||
if (g_audioEngine.pDevice != nullptr)
|
||||
{
|
||||
ma_engine_uninit(&g_audioEngine);
|
||||
}
|
||||
Mix_CloseAudio();
|
||||
Mix_Quit();
|
||||
|
||||
s_isActive = false;
|
||||
}
|
||||
|
|
|
@ -4,13 +4,22 @@
|
|||
|
||||
CodeCache::CodeCache()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
bucket = (char*)VirtualAlloc(nullptr, 0x200000000, MEM_RESERVE, PAGE_READWRITE);
|
||||
assert(bucket);
|
||||
assert(bucket != nullptr);
|
||||
#else
|
||||
bucket = (char*)mmap(NULL, 0x200000000, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
assert(bucket != (char*)MAP_FAILED);
|
||||
#endif
|
||||
}
|
||||
|
||||
CodeCache::~CodeCache()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
VirtualFree(bucket, 0, MEM_RELEASE);
|
||||
#else
|
||||
munmap(bucket, 0x200000000);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CodeCache::Init()
|
||||
|
@ -19,16 +28,20 @@ void CodeCache::Init()
|
|||
{
|
||||
if (PPCFuncMappings[i].host != nullptr)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
VirtualAlloc(bucket + PPCFuncMappings[i].guest * 2, sizeof(void*), MEM_COMMIT, PAGE_READWRITE);
|
||||
#endif
|
||||
*(void**)(bucket + PPCFuncMappings[i].guest * 2) = (void*)PPCFuncMappings[i].host;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CodeCache::Insert(uint32_t guest, const void* host)
|
||||
void CodeCache::Insert(uint32_t guest, PPCFunc* host)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
VirtualAlloc(bucket + static_cast<uint64_t>(guest) * 2, sizeof(void*), MEM_COMMIT, PAGE_READWRITE);
|
||||
*reinterpret_cast<const void**>(bucket + static_cast<uint64_t>(guest) * 2) = host;
|
||||
#endif
|
||||
*reinterpret_cast<PPCFunc**>(bucket + static_cast<uint64_t>(guest) * 2) = host;
|
||||
}
|
||||
|
||||
void* CodeCache::Find(uint32_t guest) const
|
||||
|
@ -43,5 +56,5 @@ SWA_API PPCFunc* KeFindHostFunction(uint32_t guest)
|
|||
|
||||
SWA_API void KeInsertHostFunction(uint32_t guest, PPCFunc* function)
|
||||
{
|
||||
g_codeCache.Insert(guest, (const void*)function);
|
||||
g_codeCache.Insert(guest, function);
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ struct CodeCache
|
|||
~CodeCache();
|
||||
|
||||
void Init();
|
||||
void Insert(uint32_t guest, const void* host);
|
||||
void Insert(uint32_t guest, PPCFunc* host);
|
||||
|
||||
void* Find(uint32_t guest) const;
|
||||
};
|
||||
|
|
|
@ -27,11 +27,12 @@ GuestThreadContext::GuestThreadContext(uint32_t cpuNumber)
|
|||
*(thread + 0x10C) = cpuNumber;
|
||||
|
||||
*(uint32_t*)(thread + PCR_SIZE + 0x10) = 0xFFFFFFFF; // that one TLS entry that felt quirky
|
||||
*(uint32_t*)(thread + PCR_SIZE + TLS_SIZE + 0x14C) = ByteSwap(GetCurrentThreadId()); // thread id
|
||||
*(uint32_t*)(thread + PCR_SIZE + TLS_SIZE + 0x14C) = ByteSwap(GuestThread::GetCurrentThreadId()); // thread id
|
||||
|
||||
ppcContext.fn = (uint8_t*)g_codeCache.bucket;
|
||||
ppcContext.r1.u64 = g_memory.MapVirtual(thread + PCR_SIZE + TLS_SIZE + TEB_SIZE + STACK_SIZE); // stack pointer
|
||||
ppcContext.r13.u64 = g_memory.MapVirtual(thread);
|
||||
ppcContext.fpscr.loadFromHost();
|
||||
|
||||
assert(GetPPCContext() == nullptr);
|
||||
SetPPCContext(ppcContext);
|
||||
|
@ -42,42 +43,84 @@ GuestThreadContext::~GuestThreadContext()
|
|||
g_userHeap.Free(thread);
|
||||
}
|
||||
|
||||
DWORD GuestThread::Start(uint32_t function)
|
||||
static void GuestThreadFunc(GuestThreadHandle* hThread)
|
||||
{
|
||||
const GuestThreadParameter parameter{ function };
|
||||
return Start(parameter);
|
||||
hThread->suspended.wait(true);
|
||||
GuestThread::Start(hThread->params);
|
||||
}
|
||||
|
||||
DWORD GuestThread::Start(const GuestThreadParameter& parameter)
|
||||
GuestThreadHandle::GuestThreadHandle(const GuestThreadParams& params)
|
||||
: params(params), suspended((params.flags & 0x1) != 0), thread(GuestThreadFunc, this)
|
||||
{
|
||||
const auto procMask = (uint8_t)(parameter.flags >> 24);
|
||||
}
|
||||
|
||||
GuestThreadHandle::~GuestThreadHandle()
|
||||
{
|
||||
if (thread.joinable())
|
||||
thread.join();
|
||||
}
|
||||
|
||||
uint32_t GuestThreadHandle::Wait(uint32_t timeout)
|
||||
{
|
||||
assert(timeout == INFINITE);
|
||||
|
||||
if (thread.joinable())
|
||||
thread.join();
|
||||
|
||||
return STATUS_WAIT_0;
|
||||
}
|
||||
|
||||
uint32_t GuestThread::Start(const GuestThreadParams& params)
|
||||
{
|
||||
const auto procMask = (uint8_t)(params.flags >> 24);
|
||||
const auto cpuNumber = procMask == 0 ? 0 : 7 - std::countl_zero(procMask);
|
||||
|
||||
GuestThreadContext ctx(cpuNumber);
|
||||
ctx.ppcContext.r3.u64 = parameter.value;
|
||||
ctx.ppcContext.r3.u64 = params.value;
|
||||
|
||||
GuestCode::Run(g_codeCache.Find(parameter.function), &ctx.ppcContext, g_memory.Translate(0));
|
||||
reinterpret_cast<PPCFunc*>(g_codeCache.Find(params.function))(ctx.ppcContext, reinterpret_cast<uint8_t*>(g_memory.base));
|
||||
|
||||
return (DWORD)ctx.ppcContext.r3.u64;
|
||||
return ctx.ppcContext.r3.u32;
|
||||
}
|
||||
|
||||
DWORD HostThreadStart(void* pParameter)
|
||||
static uint32_t GetThreadId(const std::thread::id& id)
|
||||
{
|
||||
auto* parameter = static_cast<GuestThreadParameter*>(pParameter);
|
||||
const auto result = GuestThread::Start(*parameter);
|
||||
|
||||
delete parameter;
|
||||
return result;
|
||||
if constexpr (sizeof(id) == 4)
|
||||
return *reinterpret_cast<const uint32_t*>(&id);
|
||||
else
|
||||
return XXH32(&id, sizeof(id), 0);
|
||||
}
|
||||
|
||||
HANDLE GuestThread::Start(uint32_t function, uint32_t parameter, uint32_t flags, LPDWORD threadId)
|
||||
GuestThreadHandle* GuestThread::Start(const GuestThreadParams& params, uint32_t* threadId)
|
||||
{
|
||||
const auto hostCreationFlags = (flags & 1) != 0 ? CREATE_SUSPENDED : 0;
|
||||
//return CreateThread(nullptr, 0, Start, (void*)((uint64_t(parameter) << 32) | function), suspended ? CREATE_SUSPENDED : 0, threadId);
|
||||
return CreateThread(nullptr, 0, HostThreadStart, new GuestThreadParameter{ function, parameter, flags }, hostCreationFlags, threadId);
|
||||
auto hThread = CreateKernelObject<GuestThreadHandle>(params);
|
||||
|
||||
if (threadId != nullptr)
|
||||
*threadId = GetThreadId(hThread->thread.get_id());
|
||||
|
||||
return hThread;
|
||||
}
|
||||
|
||||
void GuestThread::SetThreadName(uint32_t id, const char* name)
|
||||
uint32_t GuestThread::GetCurrentThreadId()
|
||||
{
|
||||
return GetThreadId(std::this_thread::get_id());
|
||||
}
|
||||
|
||||
void GuestThread::SetLastError(uint32_t error)
|
||||
{
|
||||
auto* thread = (char*)g_memory.Translate(GetPPCContext()->r13.u32);
|
||||
if (*(uint32_t*)(thread + 0x150))
|
||||
{
|
||||
// Program doesn't want errors
|
||||
return;
|
||||
}
|
||||
|
||||
// TEB + 0x160 : Win32LastError
|
||||
*(uint32_t*)(thread + TEB_OFFSET + 0x160) = ByteSwap(error);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
void GuestThread::SetThreadName(uint32_t threadId, const char* name)
|
||||
{
|
||||
#pragma pack(push,8)
|
||||
const DWORD MS_VC_EXCEPTION = 0x406D1388;
|
||||
|
@ -94,7 +137,7 @@ void GuestThread::SetThreadName(uint32_t id, const char* name)
|
|||
THREADNAME_INFO info;
|
||||
info.dwType = 0x1000;
|
||||
info.szName = name;
|
||||
info.dwThreadID = id;
|
||||
info.dwThreadID = threadId;
|
||||
info.dwFlags = 0;
|
||||
|
||||
__try
|
||||
|
@ -105,41 +148,31 @@ void GuestThread::SetThreadName(uint32_t id, const char* name)
|
|||
{
|
||||
}
|
||||
}
|
||||
|
||||
void GuestThread::SetLastError(DWORD error)
|
||||
{
|
||||
auto* thread = (char*)g_memory.Translate(GetPPCContext()->r13.u32);
|
||||
if (*(DWORD*)(thread + 0x150))
|
||||
{
|
||||
// Program doesn't want errors
|
||||
return;
|
||||
}
|
||||
|
||||
// TEB + 0x160 : Win32LastError
|
||||
*(DWORD*)(thread + TEB_OFFSET + 0x160) = ByteSwap(error);
|
||||
}
|
||||
|
||||
PPCContext* GuestThread::Invoke(uint32_t address)
|
||||
{
|
||||
auto* ctx = GetPPCContext();
|
||||
GuestCode::Run(g_codeCache.Find(address), ctx);
|
||||
|
||||
return ctx;
|
||||
}
|
||||
#endif
|
||||
|
||||
void SetThreadNameImpl(uint32_t a1, uint32_t threadId, uint32_t* name)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
GuestThread::SetThreadName(threadId, (const char*)g_memory.Translate(ByteSwap(*name)));
|
||||
#endif
|
||||
}
|
||||
|
||||
int GetThreadPriorityImpl(uint32_t hThread)
|
||||
int GetThreadPriorityImpl(GuestThreadHandle* hThread)
|
||||
{
|
||||
return GetThreadPriority((HANDLE)hThread);
|
||||
#ifdef _WIN32
|
||||
return GetThreadPriority(hThread == GetKernelObject(CURRENT_THREAD_HANDLE) ? GetCurrentThread() : hThread->thread.native_handle());
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
DWORD SetThreadIdealProcessorImpl(uint32_t hThread, DWORD dwIdealProcessor)
|
||||
uint32_t SetThreadIdealProcessorImpl(GuestThreadHandle* hThread, uint32_t dwIdealProcessor)
|
||||
{
|
||||
return SetThreadIdealProcessor((HANDLE)hThread, dwIdealProcessor);
|
||||
#ifdef _WIN32
|
||||
return SetThreadIdealProcessor(hThread == GetKernelObject(CURRENT_THREAD_HANDLE) ? GetCurrentThread() : hThread->thread.native_handle(), dwIdealProcessor);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
GUEST_FUNCTION_HOOK(sub_82DFA2E8, SetThreadNameImpl);
|
||||
|
|
|
@ -1,12 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
struct PPCContext;
|
||||
struct GuestThreadParameter
|
||||
{
|
||||
uint32_t function;
|
||||
uint32_t value;
|
||||
uint32_t flags;
|
||||
};
|
||||
#include <kernel/xdm.h>
|
||||
|
||||
#define CURRENT_THREAD_HANDLE uint32_t(-2)
|
||||
|
||||
struct GuestThreadContext
|
||||
{
|
||||
|
@ -17,13 +13,34 @@ struct GuestThreadContext
|
|||
~GuestThreadContext();
|
||||
};
|
||||
|
||||
struct GuestThreadParams
|
||||
{
|
||||
uint32_t function;
|
||||
uint32_t value;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
struct GuestThreadHandle : KernelObject
|
||||
{
|
||||
GuestThreadParams params;
|
||||
std::atomic<bool> suspended;
|
||||
std::thread thread;
|
||||
|
||||
GuestThreadHandle(const GuestThreadParams& params);
|
||||
~GuestThreadHandle() override;
|
||||
|
||||
uint32_t Wait(uint32_t timeout) override;
|
||||
};
|
||||
|
||||
struct GuestThread
|
||||
{
|
||||
static DWORD Start(uint32_t function);
|
||||
static DWORD Start(const GuestThreadParameter& parameter);
|
||||
static HANDLE Start(uint32_t function, uint32_t parameter, uint32_t flags, LPDWORD threadId);
|
||||
static uint32_t Start(const GuestThreadParams& params);
|
||||
static GuestThreadHandle* Start(const GuestThreadParams& params, uint32_t* threadId);
|
||||
|
||||
static void SetThreadName(uint32_t id, const char* name);
|
||||
static void SetLastError(DWORD error);
|
||||
static PPCContext* Invoke(uint32_t address);
|
||||
static uint32_t GetCurrentThreadId();
|
||||
static void SetLastError(uint32_t error);
|
||||
|
||||
#ifdef _WIN32
|
||||
static void SetThreadName(uint32_t threadId, const char* name);
|
||||
#endif
|
||||
};
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
inline thread_local PPCContext* gPPCContext;
|
||||
inline thread_local PPCContext* g_ppcContext;
|
||||
|
||||
inline PPCContext* GetPPCContext()
|
||||
{
|
||||
return gPPCContext;
|
||||
return g_ppcContext;
|
||||
}
|
||||
|
||||
inline void SetPPCContext(PPCContext& ctx)
|
||||
{
|
||||
gPPCContext = &ctx;
|
||||
g_ppcContext = &ctx;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include <kernel/memory.h>
|
||||
#include <cpu/guest_stack_var.h>
|
||||
#include <ui/installer_wizard.h>
|
||||
#include <ui/window.h>
|
||||
#include <ui/game_window.h>
|
||||
#include <api/boost/smart_ptr/shared_ptr.h>
|
||||
|
||||
SWA_API void Game_PlaySound(const char* pName)
|
||||
|
@ -31,10 +31,10 @@ SWA_API void Game_PlaySound(const char* pName)
|
|||
|
||||
SWA_API void Window_SetDisplay(int displayIndex)
|
||||
{
|
||||
Window::SetDisplay(displayIndex);
|
||||
GameWindow::SetDisplay(displayIndex);
|
||||
}
|
||||
|
||||
SWA_API void Window_SetFullscreen(bool isEnabled)
|
||||
{
|
||||
Window::SetFullscreen(isEnabled);
|
||||
GameWindow::SetFullscreen(isEnabled);
|
||||
}
|
||||
|
|
|
@ -4,8 +4,9 @@
|
|||
#define SWA_DLLEXPORT __declspec(dllexport)
|
||||
#define SWA_DLLIMPORT __declspec(dllimport)
|
||||
#else
|
||||
#define SWA_DLLEXPORT __attribute__((dllexport))
|
||||
#define SWA_DLLIMPORT __attribute__((dllimport))
|
||||
// TODO
|
||||
#define SWA_DLLEXPORT
|
||||
#define SWA_DLLIMPORT
|
||||
#endif
|
||||
|
||||
#ifdef SWA_IMPL
|
||||
|
@ -33,19 +34,6 @@ inline T RoundDown(const T& in_rValue, uint32_t in_round)
|
|||
return in_rValue & ~(in_round - 1);
|
||||
}
|
||||
|
||||
inline bool FileExists(const char* path)
|
||||
{
|
||||
const auto attributes = GetFileAttributesA(path);
|
||||
return attributes != INVALID_FILE_ATTRIBUTES && !(attributes & FILE_ATTRIBUTE_DIRECTORY);
|
||||
|
||||
}
|
||||
|
||||
inline bool DirectoryExists(const char* path)
|
||||
{
|
||||
const auto attributes = GetFileAttributesA(path);
|
||||
return attributes != INVALID_FILE_ATTRIBUTES && !!(attributes & FILE_ATTRIBUTE_DIRECTORY);
|
||||
}
|
||||
|
||||
inline size_t StringHash(const std::string_view& str)
|
||||
{
|
||||
return XXH3_64bits(str.data(), str.size());
|
||||
|
|
|
@ -196,7 +196,7 @@ void ImFontAtlasSnapshot::GenerateGlyphRanges()
|
|||
{
|
||||
std::vector<std::string_view> localeStrings;
|
||||
|
||||
for (auto& config : Config::Definitions)
|
||||
for (auto& config : g_configDefinitions)
|
||||
config->GetLocaleStrings(localeStrings);
|
||||
|
||||
std::set<ImWchar> glyphs;
|
||||
|
|
|
@ -3345,7 +3345,8 @@ namespace plume {
|
|||
D3D12MA::ALLOCATOR_DESC allocatorDesc = {};
|
||||
allocatorDesc.pDevice = d3d;
|
||||
allocatorDesc.pAdapter = adapter;
|
||||
allocatorDesc.Flags = D3D12MA::ALLOCATOR_FLAG_DEFAULT_POOLS_NOT_ZEROED | D3D12MA::ALLOCATOR_FLAG_MSAA_TEXTURES_ALWAYS_COMMITTED;
|
||||
allocatorDesc.Flags = D3D12MA::ALLOCATOR_FLAG_DEFAULT_POOLS_NOT_ZEROED |
|
||||
D3D12MA::ALLOCATOR_FLAG_MSAA_TEXTURES_ALWAYS_COMMITTED | D3D12MA::ALLOCATOR_FLAG_DONT_PREFER_SMALL_BUFFERS_COMMITTED;
|
||||
|
||||
res = D3D12MA::CreateAllocator(&allocatorDesc, &allocator);
|
||||
if (FAILED(res)) {
|
||||
|
|
|
@ -29,12 +29,18 @@
|
|||
typedef struct _NSWindow NSWindow;
|
||||
#endif
|
||||
|
||||
#ifdef SDL_VULKAN_ENABLED
|
||||
#include <SDL_vulkan.h>
|
||||
#endif
|
||||
|
||||
namespace plume {
|
||||
#if defined(_WIN64)
|
||||
// Native HWND handle to the target window.
|
||||
typedef HWND RenderWindow;
|
||||
#elif defined(__ANDROID__)
|
||||
typedef ANativeWindow* RenderWindow;
|
||||
#elif defined(SDL_VULKAN_ENABLED)
|
||||
typedef SDL_Window *RenderWindow;
|
||||
#elif defined(__linux__)
|
||||
struct RenderWindow {
|
||||
Display* display;
|
||||
|
|
|
@ -1989,6 +1989,13 @@ namespace plume {
|
|||
fprintf(stderr, "vkCreateWin32SurfaceKHR failed with error code 0x%X.\n", res);
|
||||
return;
|
||||
}
|
||||
# elif defined(SDL_VULKAN_ENABLED)
|
||||
VulkanInterface *renderInterface = commandQueue->device->renderInterface;
|
||||
SDL_bool sdlRes = SDL_Vulkan_CreateSurface(renderWindow, renderInterface->instance, &surface);
|
||||
if (sdlRes == SDL_FALSE) {
|
||||
fprintf(stderr, "SDL_Vulkan_CreateSurface failed with error %s.\n", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
# elif defined(__ANDROID__)
|
||||
assert(renderWindow != nullptr);
|
||||
VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo = {};
|
||||
|
@ -2124,6 +2131,12 @@ namespace plume {
|
|||
}
|
||||
|
||||
bool VulkanSwapChain::present(uint32_t textureIndex, RenderCommandSemaphore **waitSemaphores, uint32_t waitSemaphoreCount) {
|
||||
constexpr uint64_t MaxFrameDelay = 1;
|
||||
if (commandQueue->device->capabilities.presentWait && (currentPresentId > MaxFrameDelay)) {
|
||||
constexpr uint64_t waitTimeout = 100000000;
|
||||
vkWaitForPresentKHR(commandQueue->device->vk, vk, currentPresentId - MaxFrameDelay, waitTimeout);
|
||||
}
|
||||
|
||||
thread_local std::vector<VkSemaphore> waitSemaphoresVector;
|
||||
waitSemaphoresVector.clear();
|
||||
for (uint32_t i = 0; i < waitSemaphoreCount; i++) {
|
||||
|
@ -2139,6 +2152,15 @@ namespace plume {
|
|||
presentInfo.pWaitSemaphores = !waitSemaphoresVector.empty() ? waitSemaphoresVector.data() : nullptr;
|
||||
presentInfo.waitSemaphoreCount = uint32_t(waitSemaphoresVector.size());
|
||||
|
||||
VkPresentIdKHR presentId = {};
|
||||
if (commandQueue->device->capabilities.presentWait) {
|
||||
currentPresentId++;
|
||||
presentId.sType = VK_STRUCTURE_TYPE_PRESENT_ID_KHR;
|
||||
presentId.pPresentIds = ¤tPresentId;
|
||||
presentId.swapchainCount = 1;
|
||||
presentInfo.pNext = &presentId;
|
||||
}
|
||||
|
||||
VkResult res;
|
||||
{
|
||||
const std::scoped_lock queueLock(*commandQueue->queue->mutex);
|
||||
|
@ -2297,6 +2319,8 @@ namespace plume {
|
|||
GetClientRect(renderWindow, &rect);
|
||||
dstWidth = rect.right - rect.left;
|
||||
dstHeight = rect.bottom - rect.top;
|
||||
# elif defined(SDL_VULKAN_ENABLED)
|
||||
SDL_GetWindowSize(renderWindow, (int *)(&dstWidth), (int *)(&dstHeight));
|
||||
# elif defined(__ANDROID__)
|
||||
dstWidth = ANativeWindow_getWidth(renderWindow);
|
||||
dstHeight = ANativeWindow_getHeight(renderWindow);
|
||||
|
@ -4058,7 +4082,11 @@ namespace plume {
|
|||
|
||||
// VulkanInterface
|
||||
|
||||
#if SDL_VULKAN_ENABLED
|
||||
VulkanInterface::VulkanInterface(RenderWindow sdlWindow) {
|
||||
#else
|
||||
VulkanInterface::VulkanInterface() {
|
||||
#endif
|
||||
VkResult res = volkInitialize();
|
||||
if (res != VK_SUCCESS) {
|
||||
fprintf(stderr, "volkInitialize failed with error code 0x%X.\n", res);
|
||||
|
@ -4085,11 +4113,31 @@ namespace plume {
|
|||
std::vector<VkExtensionProperties> availableExtensions(extensionCount);
|
||||
vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, availableExtensions.data());
|
||||
|
||||
std::unordered_set<std::string> missingRequiredExtensions = RequiredInstanceExtensions;
|
||||
std::unordered_set<std::string> requiredExtensions = RequiredInstanceExtensions;
|
||||
std::unordered_set<std::string> supportedOptionalExtensions;
|
||||
# if DLSS_ENABLED
|
||||
const std::unordered_set<std::string> dlssExtensions = DLSS::getRequiredInstanceExtensionsVulkan();
|
||||
# endif
|
||||
|
||||
# if SDL_VULKAN_ENABLED
|
||||
// Push the extensions specified by SDL as required.
|
||||
// SDL2 has this awkward requirement for the window to pull the extensions from.
|
||||
// This can be removed when upgrading to SDL3.
|
||||
if (sdlWindow != nullptr) {
|
||||
uint32_t sdlVulkanExtensionCount = 0;
|
||||
if (SDL_Vulkan_GetInstanceExtensions(sdlWindow, &sdlVulkanExtensionCount, nullptr)) {
|
||||
std::vector<char *> sdlVulkanExtensions;
|
||||
sdlVulkanExtensions.resize(sdlVulkanExtensionCount);
|
||||
if (SDL_Vulkan_GetInstanceExtensions(sdlWindow, &sdlVulkanExtensionCount, (const char **)(sdlVulkanExtensions.data()))) {
|
||||
for (char *sdlVulkanExtension : sdlVulkanExtensions) {
|
||||
requiredExtensions.insert(sdlVulkanExtension);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
# endif
|
||||
|
||||
std::unordered_set<std::string> missingRequiredExtensions = requiredExtensions;
|
||||
for (uint32_t i = 0; i < extensionCount; i++) {
|
||||
const std::string extensionName(availableExtensions[i].extensionName);
|
||||
missingRequiredExtensions.erase(extensionName);
|
||||
|
@ -4114,7 +4162,7 @@ namespace plume {
|
|||
}
|
||||
|
||||
std::vector<const char *> enabledExtensions;
|
||||
for (const std::string &extension : RequiredInstanceExtensions) {
|
||||
for (const std::string &extension : requiredExtensions) {
|
||||
enabledExtensions.push_back(extension.c_str());
|
||||
}
|
||||
|
||||
|
@ -4177,8 +4225,15 @@ namespace plume {
|
|||
|
||||
// Global creation function.
|
||||
|
||||
#if SDL_VULKAN_ENABLED
|
||||
std::unique_ptr<RenderInterface> CreateVulkanInterface(RenderWindow sdlWindow) {
|
||||
std::unique_ptr<VulkanInterface> createdInterface = std::make_unique<VulkanInterface>(sdlWindow);
|
||||
return createdInterface->isValid() ? std::move(createdInterface) : nullptr;
|
||||
}
|
||||
#else
|
||||
std::unique_ptr<RenderInterface> CreateVulkanInterface() {
|
||||
std::unique_ptr<VulkanInterface> createdInterface = std::make_unique<VulkanInterface>();
|
||||
return createdInterface->isValid() ? std::move(createdInterface) : nullptr;
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
|
|
@ -22,9 +22,18 @@
|
|||
#define VK_USE_PLATFORM_XLIB_KHR
|
||||
#endif
|
||||
|
||||
#include "volk.h"
|
||||
#include <volk.h>
|
||||
|
||||
#include "vk_mem_alloc.h"
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wnullability-completeness"
|
||||
#endif
|
||||
|
||||
#include <vk_mem_alloc.h>
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
namespace plume {
|
||||
struct VulkanCommandQueue;
|
||||
|
@ -220,6 +229,7 @@ namespace plume {
|
|||
VkPresentModeKHR requiredPresentMode = VK_PRESENT_MODE_FIFO_KHR;
|
||||
VkCompositeAlphaFlagBitsKHR pickedAlphaFlag = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
||||
std::vector<VulkanTexture> textures;
|
||||
uint64_t currentPresentId = 0;
|
||||
bool immediatePresentModeSupported = false;
|
||||
|
||||
VulkanSwapChain(VulkanCommandQueue *commandQueue, RenderWindow renderWindow, uint32_t textureCount, RenderFormat format);
|
||||
|
@ -412,7 +422,12 @@ namespace plume {
|
|||
VkApplicationInfo appInfo = {};
|
||||
RenderInterfaceCapabilities capabilities;
|
||||
|
||||
# if SDL_VULKAN_ENABLED
|
||||
VulkanInterface(RenderWindow sdlWindow);
|
||||
# else
|
||||
VulkanInterface();
|
||||
# endif
|
||||
|
||||
~VulkanInterface() override;
|
||||
std::unique_ptr<RenderDevice> createDevice() override;
|
||||
const RenderInterfaceCapabilities &getCapabilities() const override;
|
||||
|
|
|
@ -26,56 +26,68 @@
|
|||
#include <ui/message_window.h>
|
||||
#include <ui/options_menu.h>
|
||||
#include <ui/sdl_listener.h>
|
||||
#include <ui/window.h>
|
||||
#include <ui/game_window.h>
|
||||
#include <user/config.h>
|
||||
#include <xxHashMap.h>
|
||||
|
||||
#if defined(ASYNC_PSO_DEBUG) || defined(PSO_CACHING)
|
||||
#include <magic_enum.hpp>
|
||||
#include <magic_enum/magic_enum.hpp>
|
||||
#endif
|
||||
|
||||
#include "../../tools/ShaderRecomp/ShaderRecomp/shader_common.h"
|
||||
|
||||
#ifdef SWA_D3D12
|
||||
#include "shader/copy_vs.hlsl.dxil.h"
|
||||
#include "shader/copy_vs.hlsl.spirv.h"
|
||||
#include "shader/csd_filter_ps.hlsl.dxil.h"
|
||||
#include "shader/csd_filter_ps.hlsl.spirv.h"
|
||||
#include "shader/enhanced_motion_blur_ps.hlsl.dxil.h"
|
||||
#include "shader/enhanced_motion_blur_ps.hlsl.spirv.h"
|
||||
#include "shader/gamma_correction_ps.hlsl.dxil.h"
|
||||
#include "shader/gamma_correction_ps.hlsl.spirv.h"
|
||||
#include "shader/gaussian_blur_3x3.hlsl.dxil.h"
|
||||
#include "shader/gaussian_blur_3x3.hlsl.spirv.h"
|
||||
#include "shader/gaussian_blur_5x5.hlsl.dxil.h"
|
||||
#include "shader/gaussian_blur_5x5.hlsl.spirv.h"
|
||||
#include "shader/gaussian_blur_7x7.hlsl.dxil.h"
|
||||
#include "shader/gaussian_blur_7x7.hlsl.spirv.h"
|
||||
#include "shader/gaussian_blur_9x9.hlsl.dxil.h"
|
||||
#include "shader/gaussian_blur_9x9.hlsl.spirv.h"
|
||||
#include "shader/imgui_ps.hlsl.dxil.h"
|
||||
#include "shader/imgui_ps.hlsl.spirv.h"
|
||||
#include "shader/imgui_vs.hlsl.dxil.h"
|
||||
#include "shader/imgui_vs.hlsl.spirv.h"
|
||||
#include "shader/movie_ps.hlsl.dxil.h"
|
||||
#include "shader/movie_ps.hlsl.spirv.h"
|
||||
#include "shader/movie_vs.hlsl.dxil.h"
|
||||
#include "shader/movie_vs.hlsl.spirv.h"
|
||||
#include "shader/resolve_msaa_depth_2x.hlsl.dxil.h"
|
||||
#include "shader/resolve_msaa_depth_2x.hlsl.spirv.h"
|
||||
#include "shader/resolve_msaa_depth_4x.hlsl.dxil.h"
|
||||
#include "shader/resolve_msaa_depth_4x.hlsl.spirv.h"
|
||||
#include "shader/resolve_msaa_depth_8x.hlsl.dxil.h"
|
||||
#endif
|
||||
|
||||
#include "shader/copy_vs.hlsl.spirv.h"
|
||||
#include "shader/csd_filter_ps.hlsl.spirv.h"
|
||||
#include "shader/enhanced_motion_blur_ps.hlsl.spirv.h"
|
||||
#include "shader/gamma_correction_ps.hlsl.spirv.h"
|
||||
#include "shader/gaussian_blur_3x3.hlsl.spirv.h"
|
||||
#include "shader/gaussian_blur_5x5.hlsl.spirv.h"
|
||||
#include "shader/gaussian_blur_7x7.hlsl.spirv.h"
|
||||
#include "shader/gaussian_blur_9x9.hlsl.spirv.h"
|
||||
#include "shader/imgui_ps.hlsl.spirv.h"
|
||||
#include "shader/imgui_vs.hlsl.spirv.h"
|
||||
#include "shader/movie_ps.hlsl.spirv.h"
|
||||
#include "shader/movie_vs.hlsl.spirv.h"
|
||||
#include "shader/resolve_msaa_depth_2x.hlsl.spirv.h"
|
||||
#include "shader/resolve_msaa_depth_4x.hlsl.spirv.h"
|
||||
#include "shader/resolve_msaa_depth_8x.hlsl.spirv.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
extern "C"
|
||||
{
|
||||
__declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001;
|
||||
__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace plume
|
||||
{
|
||||
#ifdef SWA_D3D12
|
||||
extern std::unique_ptr<RenderInterface> CreateD3D12Interface();
|
||||
#endif
|
||||
#ifdef SDL_VULKAN_ENABLED
|
||||
extern std::unique_ptr<RenderInterface> CreateVulkanInterface(RenderWindow sdlWindow);
|
||||
#else
|
||||
extern std::unique_ptr<RenderInterface> CreateVulkanInterface();
|
||||
#endif
|
||||
}
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
@ -165,7 +177,7 @@ struct DirtyStates
|
|||
static DirtyStates g_dirtyStates(true);
|
||||
|
||||
template<typename T>
|
||||
static FORCEINLINE void SetDirtyValue(bool& dirtyState, T& dest, const T& src)
|
||||
static void SetDirtyValue(bool& dirtyState, T& dest, const T& src)
|
||||
{
|
||||
if (dest != src)
|
||||
{
|
||||
|
@ -174,7 +186,12 @@ static FORCEINLINE void SetDirtyValue(bool& dirtyState, T& dest, const T& src)
|
|||
}
|
||||
}
|
||||
|
||||
static bool g_vulkan;
|
||||
#ifdef SWA_D3D12
|
||||
static bool g_vulkan = false;
|
||||
#else
|
||||
static constexpr bool g_vulkan = true;
|
||||
#endif
|
||||
|
||||
static std::unique_ptr<RenderInterface> g_interface;
|
||||
static std::unique_ptr<RenderDevice> g_device;
|
||||
|
||||
|
@ -197,7 +214,6 @@ static std::unique_ptr<RenderCommandFence> g_copyCommandFence;
|
|||
|
||||
static std::unique_ptr<RenderSwapChain> g_swapChain;
|
||||
static bool g_swapChainValid;
|
||||
static bool g_needsResize;
|
||||
|
||||
static constexpr RenderFormat BACKBUFFER_FORMAT = RenderFormat::B8G8R8A8_UNORM;
|
||||
|
||||
|
@ -545,7 +561,7 @@ static void DestructTempResources()
|
|||
g_tempBuffers[g_frame].clear();
|
||||
}
|
||||
|
||||
static uint32_t g_mainThreadId;
|
||||
static std::thread::id g_mainThreadId;
|
||||
|
||||
static ankerl::unordered_dense::map<RenderTexture*, RenderTextureLayout> g_barrierMap;
|
||||
|
||||
|
@ -579,13 +595,18 @@ static std::unique_ptr<uint8_t[]> g_buttonBcDiff;
|
|||
|
||||
static void LoadEmbeddedResources()
|
||||
{
|
||||
const size_t decompressedSize = g_vulkan ? g_spirvCacheDecompressedSize : g_dxilCacheDecompressedSize;
|
||||
g_shaderCache = std::make_unique<uint8_t[]>(decompressedSize);
|
||||
|
||||
ZSTD_decompress(g_shaderCache.get(),
|
||||
decompressedSize,
|
||||
g_vulkan ? g_compressedSpirvCache : g_compressedDxilCache,
|
||||
g_vulkan ? g_spirvCacheCompressedSize : g_dxilCacheCompressedSize);
|
||||
if (g_vulkan)
|
||||
{
|
||||
g_shaderCache = std::make_unique<uint8_t[]>(g_spirvCacheDecompressedSize);
|
||||
ZSTD_decompress(g_shaderCache.get(), g_spirvCacheDecompressedSize, g_compressedSpirvCache, g_spirvCacheCompressedSize);
|
||||
}
|
||||
#ifdef SWA_D3D12
|
||||
else
|
||||
{
|
||||
g_shaderCache = std::make_unique<uint8_t[]>(g_dxilCacheDecompressedSize);
|
||||
ZSTD_decompress(g_shaderCache.get(), g_dxilCacheDecompressedSize, g_compressedDxilCache, g_dxilCacheCompressedSize);
|
||||
}
|
||||
#endif
|
||||
|
||||
g_buttonBcDiff = decompressZstd(g_button_bc_diff, g_button_bc_diff_uncompressed_size);
|
||||
}
|
||||
|
@ -1023,7 +1044,7 @@ static void ProcSetRenderState(const RenderCommand& cmd)
|
|||
}
|
||||
}
|
||||
|
||||
static const std::pair<GuestRenderState, void*> g_setRenderStateFunctions[] =
|
||||
static const std::pair<GuestRenderState, PPCFunc*> g_setRenderStateFunctions[] =
|
||||
{
|
||||
{ D3DRS_ZENABLE, HostToGuestFunction<SetRenderState<D3DRS_ZENABLE>> },
|
||||
{ D3DRS_ZWRITEENABLE, HostToGuestFunction<SetRenderState<D3DRS_ZWRITEENABLE>> },
|
||||
|
@ -1062,6 +1083,8 @@ static GuestShader* g_csdShader;
|
|||
|
||||
static std::unique_ptr<GuestShader> g_enhancedMotionBlurShader;
|
||||
|
||||
#ifdef SWA_D3D12
|
||||
|
||||
#define CREATE_SHADER(NAME) \
|
||||
g_device->createShader( \
|
||||
g_vulkan ? g_##NAME##_spirv : g_##NAME##_dxil, \
|
||||
|
@ -1069,11 +1092,20 @@ static std::unique_ptr<GuestShader> g_enhancedMotionBlurShader;
|
|||
"main", \
|
||||
g_vulkan ? RenderShaderFormat::SPIRV : RenderShaderFormat::DXIL)
|
||||
|
||||
#else
|
||||
|
||||
#define CREATE_SHADER(NAME) \
|
||||
g_device->createShader(g_##NAME##_spirv, sizeof(g_##NAME##_spirv), "main", RenderShaderFormat::SPIRV);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
static bool DetectWine()
|
||||
{
|
||||
HMODULE dllHandle = GetModuleHandle("ntdll.dll");
|
||||
return dllHandle != nullptr && GetProcAddress(dllHandle, "wine_get_version") != nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
static constexpr size_t TEXTURE_DESCRIPTOR_SIZE = 65536;
|
||||
static constexpr size_t SAMPLER_DESCRIPTOR_SIZE = 1024;
|
||||
|
@ -1136,7 +1168,7 @@ static void CreateImGuiBackend()
|
|||
OptionsMenu::Init();
|
||||
InstallerWizard::Init();
|
||||
|
||||
ImGui_ImplSDL2_InitForOther(Window::s_pWindow);
|
||||
ImGui_ImplSDL2_InitForOther(GameWindow::s_pWindow);
|
||||
|
||||
#ifdef ENABLE_IM_FONT_ATLAS_SNAPSHOT
|
||||
g_imFontTexture = LoadTexture(
|
||||
|
@ -1278,7 +1310,7 @@ static void CreateImGuiBackend()
|
|||
|
||||
static void BeginCommandList();
|
||||
|
||||
void Video::CreateHostDevice()
|
||||
void Video::CreateHostDevice(bool sdlVideoDefault)
|
||||
{
|
||||
for (uint32_t i = 0; i < 16; i++)
|
||||
g_inputSlots[i].index = i;
|
||||
|
@ -1286,13 +1318,25 @@ void Video::CreateHostDevice()
|
|||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
|
||||
Window::Init();
|
||||
GameWindow::Init(sdlVideoDefault);
|
||||
|
||||
#ifdef SWA_D3D12
|
||||
g_vulkan = DetectWine() || Config::GraphicsAPI == EGraphicsAPI::Vulkan;
|
||||
#endif
|
||||
|
||||
LoadEmbeddedResources();
|
||||
|
||||
g_interface = g_vulkan ? CreateVulkanInterface() : CreateD3D12Interface();
|
||||
if (g_vulkan)
|
||||
#ifdef SDL_VULKAN_ENABLED
|
||||
g_interface = CreateVulkanInterface(GameWindow::s_renderWindow);
|
||||
#else
|
||||
g_interface = CreateVulkanInterface();
|
||||
#endif
|
||||
#ifdef SWA_D3D12
|
||||
else
|
||||
g_interface = CreateD3D12Interface();
|
||||
#endif
|
||||
|
||||
g_device = g_interface->createDevice();
|
||||
|
||||
g_triangleFanSupported = g_device->getCapabilities().triangleFan;
|
||||
|
@ -1314,7 +1358,17 @@ void Video::CreateHostDevice()
|
|||
switch (Config::TripleBuffering)
|
||||
{
|
||||
case ETripleBuffering::Auto:
|
||||
bufferCount = g_vulkan ? 2 : 3; // Defaulting to 3 is fine on D3D12 thanks to flip discard model.
|
||||
if (g_vulkan)
|
||||
{
|
||||
// Defaulting to 3 is fine if presentWait as supported, as the maximum frame latency allowed is only 1.
|
||||
bufferCount = g_device->getCapabilities().presentWait ? 3 : 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Defaulting to 3 is fine on D3D12 thanks to flip discard model.
|
||||
bufferCount = 3;
|
||||
}
|
||||
|
||||
break;
|
||||
case ETripleBuffering::On:
|
||||
bufferCount = 3;
|
||||
|
@ -1324,7 +1378,7 @@ void Video::CreateHostDevice()
|
|||
break;
|
||||
}
|
||||
|
||||
g_swapChain = g_queue->createSwapChain(Window::s_handle, bufferCount, BACKBUFFER_FORMAT);
|
||||
g_swapChain = g_queue->createSwapChain(GameWindow::s_renderWindow, bufferCount, BACKBUFFER_FORMAT);
|
||||
g_swapChain->setVsyncEnabled(Config::VSync);
|
||||
g_swapChainValid = !g_swapChain->needsResize();
|
||||
|
||||
|
@ -1334,7 +1388,7 @@ void Video::CreateHostDevice()
|
|||
for (auto& renderSemaphore : g_renderSemaphores)
|
||||
renderSemaphore = g_device->createCommandSemaphore();
|
||||
|
||||
g_mainThreadId = GetCurrentThreadId();
|
||||
g_mainThreadId = std::this_thread::get_id();
|
||||
|
||||
RenderPipelineLayoutBuilder pipelineLayoutBuilder;
|
||||
pipelineLayoutBuilder.begin(false, true);
|
||||
|
@ -1626,9 +1680,9 @@ static uint32_t CreateDevice(uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4,
|
|||
memset(device, 0, sizeof(*device));
|
||||
|
||||
uint32_t functionOffset = 0x443344; // D3D
|
||||
g_codeCache.Insert(functionOffset, reinterpret_cast<void*>(HostToGuestFunction<SetRenderStateUnimplemented>));
|
||||
g_codeCache.Insert(functionOffset, HostToGuestFunction<SetRenderStateUnimplemented>);
|
||||
|
||||
for (size_t i = 0; i < _countof(device->setRenderStateFunctions); i++)
|
||||
for (size_t i = 0; i < std::size(device->setRenderStateFunctions); i++)
|
||||
device->setRenderStateFunctions[i] = functionOffset;
|
||||
|
||||
for (auto& [state, function] : g_setRenderStateFunctions)
|
||||
|
@ -1638,7 +1692,7 @@ static uint32_t CreateDevice(uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4,
|
|||
device->setRenderStateFunctions[state / 4] = functionOffset;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < _countof(device->setSamplerStateFunctions); i++)
|
||||
for (size_t i = 0; i < std::size(device->setSamplerStateFunctions); i++)
|
||||
device->setSamplerStateFunctions[i] = *reinterpret_cast<uint32_t*>(g_memory.Translate(0x8330F3DC + i * 0xC));
|
||||
|
||||
device->viewport.width = 1280.0f;
|
||||
|
@ -1763,7 +1817,7 @@ static void UnlockBuffer(GuestBuffer* buffer)
|
|||
{
|
||||
if (!buffer->lockedReadOnly)
|
||||
{
|
||||
if (GetCurrentThreadId() == g_mainThreadId)
|
||||
if (std::this_thread::get_id() == g_mainThreadId)
|
||||
{
|
||||
RenderCommand cmd;
|
||||
cmd.type = (sizeof(T) == 2) ? RenderCommandType::UnlockBuffer16 : RenderCommandType::UnlockBuffer32;
|
||||
|
@ -2765,9 +2819,6 @@ static void ProcSetScissorRect(const RenderCommand& cmd)
|
|||
SetDirtyValue<int32_t>(g_dirtyStates.scissorRect, g_scissorRect.right, args.right);
|
||||
}
|
||||
|
||||
static Mutex g_compiledSpecConstantLibraryBlobMutex;
|
||||
static ankerl::unordered_dense::map<uint32_t, ComPtr<IDxcBlob>> g_compiledSpecConstantLibraryBlobs;
|
||||
|
||||
static RenderShader* GetOrLinkShader(GuestShader* guestShader, uint32_t specConstants)
|
||||
{
|
||||
if (g_vulkan ||
|
||||
|
@ -2808,8 +2859,12 @@ static RenderShader* GetOrLinkShader(GuestShader* guestShader, uint32_t specCons
|
|||
shader = guestShader->linkedShaders[specConstants].get();
|
||||
}
|
||||
|
||||
#ifdef SWA_D3D12
|
||||
if (shader == nullptr)
|
||||
{
|
||||
static Mutex g_compiledSpecConstantLibraryBlobMutex;
|
||||
static ankerl::unordered_dense::map<uint32_t, ComPtr<IDxcBlob>> g_compiledSpecConstantLibraryBlobs;
|
||||
|
||||
thread_local ComPtr<IDxcCompiler3> s_dxcCompiler;
|
||||
thread_local ComPtr<IDxcLinker> s_dxcLinker;
|
||||
thread_local ComPtr<IDxcUtils> s_dxcUtils;
|
||||
|
@ -2916,6 +2971,7 @@ static RenderShader* GetOrLinkShader(GuestShader* guestShader, uint32_t specCons
|
|||
shader = linkedShader.get();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
@ -4013,8 +4069,9 @@ static void ProcSetPixelShader(const RenderCommand& cmd)
|
|||
|
||||
static std::thread g_renderThread([]
|
||||
{
|
||||
#ifdef _WIN32
|
||||
GuestThread::SetThreadName(GetCurrentThreadId(), "Render Thread");
|
||||
|
||||
#endif
|
||||
RenderCommand commands[32];
|
||||
|
||||
while (true)
|
||||
|
@ -4821,40 +4878,20 @@ struct PipelineStateQueueItem
|
|||
|
||||
static moodycamel::BlockingConcurrentQueue<PipelineStateQueueItem> g_pipelineStateQueue;
|
||||
|
||||
struct MinimalGuestThreadContext
|
||||
{
|
||||
uint8_t* stack = nullptr;
|
||||
PPCContext ppcContext{};
|
||||
|
||||
~MinimalGuestThreadContext()
|
||||
{
|
||||
if (stack != nullptr)
|
||||
g_userHeap.Free(stack);
|
||||
}
|
||||
|
||||
void ensureValid()
|
||||
{
|
||||
if (stack == nullptr)
|
||||
{
|
||||
stack = reinterpret_cast<uint8_t*>(g_userHeap.Alloc(0x4000));
|
||||
ppcContext.fn = (uint8_t*)g_codeCache.bucket;
|
||||
ppcContext.r1.u64 = g_memory.MapVirtual(stack + 0x4000);
|
||||
SetPPCContext(ppcContext);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static void PipelineCompilerThread()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
GuestThread::SetThreadName(GetCurrentThreadId(), "Pipeline Compiler Thread");
|
||||
MinimalGuestThreadContext ctx;
|
||||
#endif
|
||||
std::unique_ptr<GuestThreadContext> ctx;
|
||||
|
||||
while (true)
|
||||
{
|
||||
PipelineStateQueueItem queueItem;
|
||||
g_pipelineStateQueue.wait_dequeue(queueItem);
|
||||
|
||||
ctx.ensureValid();
|
||||
if (ctx == nullptr)
|
||||
ctx = std::make_unique<GuestThreadContext>(0);
|
||||
|
||||
auto pipeline = CreateGraphicsPipeline(queueItem.pipelineState);
|
||||
#ifdef ASYNC_PSO_DEBUG
|
||||
|
@ -4867,6 +4904,8 @@ static void PipelineCompilerThread()
|
|||
cmd.addPipeline.hash = queueItem.pipelineHash;
|
||||
cmd.addPipeline.pipeline = pipeline.release();
|
||||
g_renderQueue.enqueue(cmd);
|
||||
|
||||
std::this_thread::yield();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5496,10 +5535,11 @@ static bool CheckMadeAll(const T& modelData)
|
|||
|
||||
static void ModelConsumerThread()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
GuestThread::SetThreadName(GetCurrentThreadId(), "Model Consumer Thread");
|
||||
|
||||
#endif
|
||||
std::vector<boost::shared_ptr<Hedgehog::Database::CDatabaseData>> localPendingDataQueue;
|
||||
MinimalGuestThreadContext ctx;
|
||||
std::unique_ptr<GuestThreadContext> ctx;
|
||||
|
||||
while (true)
|
||||
{
|
||||
|
@ -5508,7 +5548,8 @@ static void ModelConsumerThread()
|
|||
while ((pendingDataCount = g_pendingDataCount.load()) == 0)
|
||||
g_pendingDataCount.wait(pendingDataCount);
|
||||
|
||||
ctx.ensureValid();
|
||||
if (ctx == nullptr)
|
||||
ctx = std::make_unique<GuestThreadContext>(0);
|
||||
|
||||
if (g_pendingPipelineStateCache)
|
||||
{
|
||||
|
@ -5673,6 +5714,8 @@ static void ModelConsumerThread()
|
|||
|
||||
if (allHandled)
|
||||
localPendingDataQueue.clear();
|
||||
|
||||
std::this_thread::yield();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ using namespace plume;
|
|||
|
||||
struct Video
|
||||
{
|
||||
static void CreateHostDevice();
|
||||
static void CreateHostDevice(bool sdlVideoDefault);
|
||||
static void HostPresent();
|
||||
static void StartPipelinePrecompilation();
|
||||
static void WaitForGPU();
|
||||
|
@ -84,22 +84,26 @@ struct GuestResource
|
|||
|
||||
void AddRef()
|
||||
{
|
||||
std::atomic_ref atomicRef(refCount.value);
|
||||
|
||||
uint32_t originalValue, incrementedValue;
|
||||
do
|
||||
{
|
||||
originalValue = refCount.value;
|
||||
incrementedValue = ByteSwap(ByteSwap(originalValue) + 1);
|
||||
} while (InterlockedCompareExchange(reinterpret_cast<LONG*>(&refCount), incrementedValue, originalValue) != originalValue);
|
||||
} while (!atomicRef.compare_exchange_weak(originalValue, incrementedValue));
|
||||
}
|
||||
|
||||
void Release()
|
||||
{
|
||||
std::atomic_ref atomicRef(refCount.value);
|
||||
|
||||
uint32_t originalValue, decrementedValue;
|
||||
do
|
||||
{
|
||||
originalValue = refCount.value;
|
||||
decrementedValue = ByteSwap(ByteSwap(originalValue) - 1);
|
||||
} while (InterlockedCompareExchange(reinterpret_cast<LONG*>(&refCount), decrementedValue, originalValue) != originalValue);
|
||||
} while (!atomicRef.compare_exchange_weak(originalValue, decrementedValue));
|
||||
|
||||
// Normally we are supposed to release here, so only use this
|
||||
// function when you know you won't be the one destructing it.
|
||||
|
@ -274,8 +278,10 @@ 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
|
||||
std::vector<ComPtr<IDxcBlob>> shaderBlobs;
|
||||
ComPtr<IDxcBlobEncoding> libraryBlob;
|
||||
#endif
|
||||
#ifdef ASYNC_PSO_DEBUG
|
||||
const char* name = "<unknown>";
|
||||
#endif
|
||||
|
@ -390,7 +396,7 @@ enum GuestTextureAddress
|
|||
D3DTADDRESS_BORDER = 6
|
||||
};
|
||||
|
||||
extern bool g_needsResize;
|
||||
inline bool g_needsResize;
|
||||
|
||||
extern std::unique_ptr<GuestTexture> LoadTexture(const uint8_t* data, size_t dataSize, RenderComponentMapping componentMapping = RenderComponentMapping());
|
||||
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
#include <SDL.h>
|
||||
#include <user/config.h>
|
||||
#include <hid/hid_detail.h>
|
||||
#include <ui/window.h>
|
||||
#include <ui/game_window.h>
|
||||
#include <kernel/xdm.h>
|
||||
|
||||
#define TRANSLATE_INPUT(S, X) SDL_GameControllerGetButton(controller, S) << FirstBitLow(X)
|
||||
#define VIBRATION_TIMEOUT_MS 5000
|
||||
|
@ -65,7 +66,7 @@ public:
|
|||
|
||||
bool CanPoll()
|
||||
{
|
||||
return controller && (Window::s_isFocused || Config::AllowBackgroundInput);
|
||||
return controller && (GameWindow::s_isFocused || Config::AllowBackgroundInput);
|
||||
}
|
||||
|
||||
void PollAxis()
|
||||
|
@ -128,7 +129,7 @@ public:
|
|||
std::array<Controller, 4> g_controllers;
|
||||
Controller* g_activeController;
|
||||
|
||||
inline Controller* EnsureController(DWORD dwUserIndex)
|
||||
inline Controller* EnsureController(uint32_t dwUserIndex)
|
||||
{
|
||||
if (!g_controllers[dwUserIndex].controller)
|
||||
return nullptr;
|
||||
|
@ -260,7 +261,7 @@ void hid::detail::Init()
|
|||
|
||||
uint32_t hid::detail::GetState(uint32_t dwUserIndex, XAMINPUT_STATE* pState)
|
||||
{
|
||||
static DWORD packet;
|
||||
static uint32_t packet;
|
||||
|
||||
if (!pState)
|
||||
return ERROR_BAD_ARGUMENTS;
|
||||
|
|
|
@ -52,7 +52,7 @@ static std::unique_ptr<VirtualFileSystem> createFileSystemFromPath(const std::fi
|
|||
{
|
||||
return XContentFileSystem::create(path);
|
||||
}
|
||||
else if (toLower(path.extension().string()) == ISOExtension)
|
||||
else if (toLower(fromPath(path.extension())) == ISOExtension)
|
||||
{
|
||||
return ISOFileSystem::create(path);
|
||||
}
|
||||
|
@ -122,7 +122,7 @@ static bool copyFile(const FilePair &pair, const uint64_t *fileHashes, VirtualFi
|
|||
if (!outStream.is_open())
|
||||
{
|
||||
journal.lastResult = Journal::Result::FileCreationFailed;
|
||||
journal.lastErrorMessage = fmt::format("Failed to create file at {}.", targetPath.string());
|
||||
journal.lastErrorMessage = fmt::format("Failed to create file at {}.", fromPath(targetPath));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -132,7 +132,7 @@ static bool copyFile(const FilePair &pair, const uint64_t *fileHashes, VirtualFi
|
|||
if (outStream.bad())
|
||||
{
|
||||
journal.lastResult = Journal::Result::FileWriteFailed;
|
||||
journal.lastErrorMessage = fmt::format("Failed to create file at {}.", targetPath.string());
|
||||
journal.lastErrorMessage = fmt::format("Failed to create file at {}.", fromPath(targetPath));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -162,7 +162,7 @@ static DLC detectDLC(const std::filesystem::path &sourcePath, VirtualFileSystem
|
|||
if (typeStartLocation == nullptr || typeEndLocation == nullptr)
|
||||
{
|
||||
journal.lastResult = Journal::Result::DLCParsingFailed;
|
||||
journal.lastErrorMessage = "Failed to find DLC type for " + sourcePath.string() + ".";
|
||||
journal.lastErrorMessage = "Failed to find DLC type for " + fromPath(sourcePath) + ".";
|
||||
return DLC::Unknown;
|
||||
}
|
||||
|
||||
|
@ -171,7 +171,7 @@ static DLC detectDLC(const std::filesystem::path &sourcePath, VirtualFileSystem
|
|||
if (typeNumberCount != 1)
|
||||
{
|
||||
journal.lastResult = Journal::Result::UnknownDLCType;
|
||||
journal.lastErrorMessage = "DLC type for " + sourcePath.string() + " is unknown.";
|
||||
journal.lastErrorMessage = "DLC type for " + fromPath(sourcePath) + " is unknown.";
|
||||
return DLC::Unknown;
|
||||
}
|
||||
|
||||
|
@ -191,7 +191,7 @@ static DLC detectDLC(const std::filesystem::path &sourcePath, VirtualFileSystem
|
|||
return DLC::EmpireCityAdabat;
|
||||
default:
|
||||
journal.lastResult = Journal::Result::UnknownDLCType;
|
||||
journal.lastErrorMessage = "DLC type for " + sourcePath.string() + " is unknown.";
|
||||
journal.lastErrorMessage = "DLC type for " + fromPath(sourcePath) + " is unknown.";
|
||||
return DLC::Unknown;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -97,7 +97,7 @@ bool MemoryMappedFile::open(const std::filesystem::path &path)
|
|||
if (fileSize == (off_t)(-1))
|
||||
{
|
||||
fprintf(stderr, "lseek failed with error %s.\n", strerror(errno));
|
||||
close(fileHandle);
|
||||
::close(fileHandle);
|
||||
fileHandle = -1;
|
||||
return false;
|
||||
}
|
||||
|
@ -106,7 +106,7 @@ bool MemoryMappedFile::open(const std::filesystem::path &path)
|
|||
if (fileView == MAP_FAILED)
|
||||
{
|
||||
fprintf(stderr, "mmap failed with error %s.\n", strerror(errno));
|
||||
close(fileHandle);
|
||||
::close(fileHandle);
|
||||
fileHandle = -1;
|
||||
return false;
|
||||
}
|
||||
|
@ -140,7 +140,7 @@ void MemoryMappedFile::close()
|
|||
|
||||
if (fileHandle != -1)
|
||||
{
|
||||
close(fileHandle);
|
||||
::close(fileHandle);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -308,14 +308,14 @@ inline bool bitScanForward(uint64_t v, uint32_t *outFirstSetIndex)
|
|||
inline bool bitScanForward(uint32_t v, uint32_t *outFirstSetIndex)
|
||||
{
|
||||
int i = ffs(v);
|
||||
*out_first_set_index = i - 1;
|
||||
*outFirstSetIndex = i - 1;
|
||||
return i != 0;
|
||||
}
|
||||
|
||||
inline bool bitScanForward(uint64_t v, uint32_t *outFirstSetIndex)
|
||||
{
|
||||
int i = __builtin_ffsll(v);
|
||||
*out_first_set_index = i - 1;
|
||||
*outFirstSetIndex = i - 1;
|
||||
return i != 0;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -39,7 +39,7 @@ std::enable_if_t<(I < sizeof...(TArgs)), void> _tuple_for(std::tuple<TArgs...>&
|
|||
|
||||
struct ArgTranslator
|
||||
{
|
||||
FORCEINLINE constexpr static uint64_t GetIntegerArgumentValue(const PPCContext& ctx, uint8_t* base, size_t arg) noexcept
|
||||
constexpr static uint64_t GetIntegerArgumentValue(const PPCContext& ctx, uint8_t* base, size_t arg) noexcept
|
||||
{
|
||||
if (arg <= 7)
|
||||
{
|
||||
|
@ -57,10 +57,10 @@ struct ArgTranslator
|
|||
}
|
||||
}
|
||||
|
||||
return *reinterpret_cast<XLPDWORD>(base + ctx.r1.u32 + 0x54 + ((arg - 8) * 8));
|
||||
return *reinterpret_cast<be<uint32_t>*>(base + ctx.r1.u32 + 0x54 + ((arg - 8) * 8));
|
||||
}
|
||||
|
||||
FORCEINLINE static double GetPrecisionArgumentValue(const PPCContext& ctx, uint8_t* base, size_t arg) noexcept
|
||||
static double GetPrecisionArgumentValue(const PPCContext& ctx, uint8_t* base, size_t arg) noexcept
|
||||
{
|
||||
switch (arg)
|
||||
{
|
||||
|
@ -84,7 +84,7 @@ struct ArgTranslator
|
|||
return 0;
|
||||
}
|
||||
|
||||
FORCEINLINE constexpr static void SetIntegerArgumentValue(PPCContext& ctx, uint8_t* base, size_t arg, uint64_t value) noexcept
|
||||
constexpr static void SetIntegerArgumentValue(PPCContext& ctx, uint8_t* base, size_t arg, uint64_t value) noexcept
|
||||
{
|
||||
if (arg <= 7)
|
||||
{
|
||||
|
@ -105,7 +105,7 @@ struct ArgTranslator
|
|||
assert(arg < 7 && "Pushing to stack memory is not yet supported.");
|
||||
}
|
||||
|
||||
FORCEINLINE static void SetPrecisionArgumentValue(PPCContext& ctx, uint8_t* base, size_t arg, double value) noexcept
|
||||
static void SetPrecisionArgumentValue(PPCContext& ctx, uint8_t* base, size_t arg, double value) noexcept
|
||||
{
|
||||
switch (arg)
|
||||
{
|
||||
|
@ -129,7 +129,7 @@ struct ArgTranslator
|
|||
}
|
||||
|
||||
template<typename T>
|
||||
FORCEINLINE constexpr static std::enable_if_t<!std::is_pointer_v<T>, T> GetValue(PPCContext& ctx, uint8_t* base, size_t idx) noexcept
|
||||
constexpr static std::enable_if_t<!std::is_pointer_v<T>, T> GetValue(PPCContext& ctx, uint8_t* base, size_t idx) noexcept
|
||||
{
|
||||
if constexpr (is_precise_v<T>)
|
||||
{
|
||||
|
@ -142,7 +142,7 @@ struct ArgTranslator
|
|||
}
|
||||
|
||||
template<typename T>
|
||||
FORCEINLINE constexpr static std::enable_if_t<std::is_pointer_v<T>, T> GetValue(PPCContext& ctx, uint8_t* base, size_t idx) noexcept
|
||||
constexpr static std::enable_if_t<std::is_pointer_v<T>, T> GetValue(PPCContext& ctx, uint8_t* base, size_t idx) noexcept
|
||||
{
|
||||
const auto v = GetIntegerArgumentValue(ctx, base, idx);
|
||||
if (!v)
|
||||
|
@ -154,7 +154,7 @@ struct ArgTranslator
|
|||
}
|
||||
|
||||
template<typename T>
|
||||
FORCEINLINE constexpr static std::enable_if_t<!std::is_pointer_v<T>, void> SetValue(PPCContext& ctx, uint8_t* base, size_t idx, T value) noexcept
|
||||
constexpr static std::enable_if_t<!std::is_pointer_v<T>, void> SetValue(PPCContext& ctx, uint8_t* base, size_t idx, T value) noexcept
|
||||
{
|
||||
if constexpr (is_precise_v<T>)
|
||||
{
|
||||
|
@ -175,7 +175,7 @@ struct ArgTranslator
|
|||
}
|
||||
|
||||
template<typename T>
|
||||
FORCEINLINE constexpr static std::enable_if_t<std::is_pointer_v<T>, void> SetValue(PPCContext& ctx, uint8_t* base, size_t idx, T value) noexcept
|
||||
constexpr static std::enable_if_t<std::is_pointer_v<T>, void> SetValue(PPCContext& ctx, uint8_t* base, size_t idx, T value) noexcept
|
||||
{
|
||||
const auto v = g_memory.MapVirtual((void*)value);
|
||||
if (!v)
|
||||
|
@ -240,13 +240,13 @@ struct arg_ordinal_t
|
|||
};
|
||||
|
||||
template<auto Func, int I = 0, typename ...TArgs>
|
||||
FORCEINLINE void _translate_args_to_host(PPCContext& ctx, uint8_t* base, std::tuple<TArgs...>&) noexcept
|
||||
void _translate_args_to_host(PPCContext& ctx, uint8_t* base, std::tuple<TArgs...>&) noexcept
|
||||
requires (I >= sizeof...(TArgs))
|
||||
{
|
||||
}
|
||||
|
||||
template <auto Func, int I = 0, typename ...TArgs>
|
||||
FORCEINLINE std::enable_if_t<(I < sizeof...(TArgs)), void> _translate_args_to_host(PPCContext& ctx, uint8_t* base, std::tuple<TArgs...>& tpl) noexcept
|
||||
std::enable_if_t<(I < sizeof...(TArgs)), void> _translate_args_to_host(PPCContext& ctx, uint8_t* base, std::tuple<TArgs...>& tpl) noexcept
|
||||
{
|
||||
using T = std::tuple_element_t<I, std::remove_reference_t<decltype(tpl)>>;
|
||||
std::get<I>(tpl) = ArgTranslator::GetValue<T>(ctx, base, arg_ordinal_t<Func, I>::value);
|
||||
|
@ -255,13 +255,13 @@ FORCEINLINE std::enable_if_t<(I < sizeof...(TArgs)), void> _translate_args_to_ho
|
|||
}
|
||||
|
||||
template<int I = 0, typename ...TArgs>
|
||||
FORCEINLINE void _translate_args_to_guest(PPCContext& ctx, uint8_t* base, std::tuple<TArgs...>&) noexcept
|
||||
void _translate_args_to_guest(PPCContext& ctx, uint8_t* base, std::tuple<TArgs...>&) noexcept
|
||||
requires (I >= sizeof...(TArgs))
|
||||
{
|
||||
}
|
||||
|
||||
template <int I = 0, typename ...TArgs>
|
||||
FORCEINLINE std::enable_if_t<(I < sizeof...(TArgs)), void> _translate_args_to_guest(PPCContext& ctx, uint8_t* base, std::tuple<TArgs...>& tpl) noexcept
|
||||
std::enable_if_t<(I < sizeof...(TArgs)), void> _translate_args_to_guest(PPCContext& ctx, uint8_t* base, std::tuple<TArgs...>& tpl) noexcept
|
||||
{
|
||||
using T = std::tuple_element_t<I, std::remove_reference_t<decltype(tpl)>>;
|
||||
ArgTranslator::SetValue<T>(ctx, base, GatherFunctionArguments(std::tuple<TArgs...>{})[I].ordinal, std::get<I>(tpl));
|
||||
|
@ -270,7 +270,7 @@ FORCEINLINE std::enable_if_t<(I < sizeof...(TArgs)), void> _translate_args_to_gu
|
|||
}
|
||||
|
||||
template<auto Func>
|
||||
FORCEINLINE PPC_FUNC(HostToGuestFunction)
|
||||
PPC_FUNC(HostToGuestFunction)
|
||||
{
|
||||
using ret_t = decltype(std::apply(Func, function_args(Func)));
|
||||
|
||||
|
@ -293,7 +293,7 @@ FORCEINLINE PPC_FUNC(HostToGuestFunction)
|
|||
}
|
||||
else
|
||||
{
|
||||
ctx.r3.u64 = NULL;
|
||||
ctx.r3.u64 = 0;
|
||||
}
|
||||
}
|
||||
else if constexpr (is_precise_v<ret_t>)
|
||||
|
@ -308,7 +308,7 @@ FORCEINLINE PPC_FUNC(HostToGuestFunction)
|
|||
}
|
||||
|
||||
template<typename T, typename TFunction, typename... TArgs>
|
||||
FORCEINLINE T GuestToHostFunction(const TFunction& func, TArgs&&... argv)
|
||||
T GuestToHostFunction(const TFunction& func, TArgs&&... argv)
|
||||
{
|
||||
auto args = std::make_tuple(std::forward<TArgs>(argv)...);
|
||||
auto& currentCtx = *GetPPCContext();
|
||||
|
@ -331,11 +331,7 @@ FORCEINLINE T GuestToHostFunction(const TFunction& func, TArgs&&... argv)
|
|||
currentCtx.fpscr = newCtx.fpscr;
|
||||
SetPPCContext(currentCtx);
|
||||
|
||||
if constexpr (std::is_void_v<T>)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else if constexpr (std::is_pointer_v<T>)
|
||||
if constexpr (std::is_pointer_v<T>)
|
||||
{
|
||||
return reinterpret_cast<T>((uint64_t)g_memory.Translate(newCtx.r3.u32));
|
||||
}
|
||||
|
@ -349,7 +345,7 @@ FORCEINLINE T GuestToHostFunction(const TFunction& func, TArgs&&... argv)
|
|||
}
|
||||
else
|
||||
{
|
||||
static_assert(false, "Unsupported return type.");
|
||||
static_assert(std::is_void_v<T>, "Unsupported return type.");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -103,7 +103,7 @@ uint32_t RtlSizeHeap(uint32_t heapHandle, uint32_t flags, uint32_t memoryPointer
|
|||
return 0;
|
||||
}
|
||||
|
||||
SWA_API uint32_t XAlloc(uint32_t size, uint32_t flags)
|
||||
SWA_API uint32_t XAllocMem(uint32_t size, uint32_t flags)
|
||||
{
|
||||
void* ptr = (flags & 0x80000000) != 0 ?
|
||||
g_userHeap.AllocPhysical(size, (1ull << ((flags >> 24) & 0xF))) :
|
||||
|
@ -116,7 +116,7 @@ SWA_API uint32_t XAlloc(uint32_t size, uint32_t flags)
|
|||
return g_memory.MapVirtual(ptr);
|
||||
}
|
||||
|
||||
SWA_API void XFree(uint32_t baseAddress, uint32_t flags)
|
||||
SWA_API void XFreeMem(uint32_t baseAddress, uint32_t flags)
|
||||
{
|
||||
if (baseAddress != NULL)
|
||||
g_userHeap.Free(g_memory.Translate(baseAddress));
|
||||
|
@ -130,6 +130,5 @@ GUEST_FUNCTION_HOOK(sub_82BD8600, RtlFreeHeap);
|
|||
GUEST_FUNCTION_HOOK(sub_82BD88F0, RtlReAllocateHeap);
|
||||
GUEST_FUNCTION_HOOK(sub_82BD6FD0, RtlSizeHeap);
|
||||
|
||||
// Seems like these handle allocation of virtual and physical pages
|
||||
GUEST_FUNCTION_HOOK(sub_831CC9C8, XAlloc);
|
||||
GUEST_FUNCTION_HOOK(sub_831CCA60, XFree);
|
||||
GUEST_FUNCTION_HOOK(sub_831CC9C8, XAllocMem);
|
||||
GUEST_FUNCTION_HOOK(sub_831CCA60, XFreeMem);
|
||||
|
|
|
@ -19,7 +19,7 @@ struct Heap
|
|||
size_t Size(void* ptr);
|
||||
|
||||
template<typename T, typename... Args>
|
||||
T* Alloc(Args... args)
|
||||
T* Alloc(Args&&... args)
|
||||
{
|
||||
T* obj = (T*)Alloc(sizeof(T));
|
||||
new (obj) T(std::forward<Args>(args)...);
|
||||
|
@ -27,7 +27,7 @@ struct Heap
|
|||
}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
T* AllocPhysical(Args... args)
|
||||
T* AllocPhysical(Args&&... args)
|
||||
{
|
||||
T* obj = (T*)AllocPhysical(sizeof(T), alignof(T));
|
||||
new (obj) T(std::forward<Args>(args)...);
|
||||
|
|
|
@ -10,11 +10,129 @@
|
|||
#include <memory>
|
||||
#include "xam.h"
|
||||
#include "xdm.h"
|
||||
#include <timeapi.h>
|
||||
#include <user/config.h>
|
||||
#include <os/logger.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <ntstatus.h>
|
||||
#endif
|
||||
|
||||
struct Event final : KernelObject, HostObject<XKEVENT>
|
||||
{
|
||||
bool manualReset;
|
||||
std::atomic<bool> signaled;
|
||||
|
||||
Event(XKEVENT* header)
|
||||
: manualReset(!header->Type), signaled(!!header->SignalState)
|
||||
{
|
||||
}
|
||||
|
||||
Event(bool manualReset, bool initialState)
|
||||
: manualReset(manualReset), signaled(initialState)
|
||||
{
|
||||
}
|
||||
|
||||
uint32_t Wait(uint32_t timeout) override
|
||||
{
|
||||
if (timeout == 0)
|
||||
{
|
||||
if (manualReset)
|
||||
{
|
||||
if (!signaled)
|
||||
return STATUS_TIMEOUT;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool expected = true;
|
||||
if (!signaled.compare_exchange_strong(expected, false))
|
||||
return STATUS_TIMEOUT;
|
||||
}
|
||||
}
|
||||
else if (timeout == INFINITE)
|
||||
{
|
||||
if (manualReset)
|
||||
{
|
||||
signaled.wait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
bool expected = true;
|
||||
if (signaled.compare_exchange_weak(expected, false))
|
||||
break;
|
||||
|
||||
signaled.wait(expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(false && "Unhandled timeout value.");
|
||||
}
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
bool Set()
|
||||
{
|
||||
signaled = true;
|
||||
|
||||
if (manualReset)
|
||||
signaled.notify_all();
|
||||
else
|
||||
signaled.notify_one();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
bool Reset()
|
||||
{
|
||||
signaled = false;
|
||||
return TRUE;
|
||||
}
|
||||
};
|
||||
|
||||
static std::atomic<uint32_t> g_keSetEventGeneration;
|
||||
|
||||
struct Semaphore final : KernelObject, HostObject<XKSEMAPHORE>
|
||||
{
|
||||
std::counting_semaphore<> semaphore;
|
||||
uint32_t maximumCount;
|
||||
|
||||
Semaphore(XKSEMAPHORE* semaphore)
|
||||
: semaphore(semaphore->Header.SignalState), maximumCount(semaphore->Limit)
|
||||
{
|
||||
}
|
||||
|
||||
Semaphore(uint32_t count, uint32_t maximumCount)
|
||||
: semaphore(count), maximumCount(maximumCount)
|
||||
{
|
||||
}
|
||||
|
||||
uint32_t Wait(uint32_t timeout) override
|
||||
{
|
||||
if (timeout == 0)
|
||||
{
|
||||
return semaphore.try_acquire() ? STATUS_SUCCESS : STATUS_TIMEOUT;
|
||||
}
|
||||
else if (timeout == INFINITE)
|
||||
{
|
||||
semaphore.acquire();
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(false && "Unhandled timeout value.");
|
||||
return STATUS_TIMEOUT;
|
||||
}
|
||||
}
|
||||
|
||||
void Release(uint32_t releaseCount, uint32_t* previousCount)
|
||||
{
|
||||
semaphore.release(releaseCount);
|
||||
}
|
||||
};
|
||||
|
||||
inline void CloseKernelObject(XDISPATCHER_HEADER& header)
|
||||
{
|
||||
|
@ -23,10 +141,10 @@ inline void CloseKernelObject(XDISPATCHER_HEADER& header)
|
|||
return;
|
||||
}
|
||||
|
||||
ObCloseHandle(header.WaitListHead.Blink);
|
||||
DestroyKernelObject(header.WaitListHead.Blink);
|
||||
}
|
||||
|
||||
DWORD GuestTimeoutToMilliseconds(XLPQWORD timeout)
|
||||
uint32_t GuestTimeoutToMilliseconds(be<int64_t>* timeout)
|
||||
{
|
||||
return timeout ? (*timeout * -1) / 10000 : INFINITE;
|
||||
}
|
||||
|
@ -84,7 +202,7 @@ uint32_t XGetGameRegion()
|
|||
return 0x03FF;
|
||||
}
|
||||
|
||||
uint32_t XMsgStartIORequest(DWORD App, DWORD Message, XXOVERLAPPED* lpOverlapped, void* Buffer, DWORD szBuffer)
|
||||
uint32_t XMsgStartIORequest(uint32_t App, uint32_t Message, XXOVERLAPPED* lpOverlapped, void* Buffer, uint32_t szBuffer)
|
||||
{
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
@ -104,8 +222,7 @@ void XamContentDelete()
|
|||
LOG_UTILITY("!!! STUB !!!");
|
||||
}
|
||||
|
||||
|
||||
uint32_t XamContentGetCreator(DWORD userIndex, const XCONTENT_DATA* contentData, PBOOL isCreator, XLPQWORD xuid, XXOVERLAPPED* overlapped)
|
||||
uint32_t XamContentGetCreator(uint32_t userIndex, const XCONTENT_DATA* contentData, be<uint32_t>* isCreator, be<uint64_t>* xuid, XXOVERLAPPED* overlapped)
|
||||
{
|
||||
if (isCreator)
|
||||
*isCreator = true;
|
||||
|
@ -146,7 +263,7 @@ uint32_t XamShowDeviceSelectorUI
|
|||
uint32_t contentType,
|
||||
uint32_t contentFlags,
|
||||
uint64_t totalRequested,
|
||||
XDWORD* deviceId,
|
||||
be<uint32_t>* deviceId,
|
||||
XXOVERLAPPED* overlapped
|
||||
)
|
||||
{
|
||||
|
@ -214,10 +331,10 @@ void RtlInitAnsiString(XANSI_STRING* destination, char* source)
|
|||
destination->Buffer = source;
|
||||
}
|
||||
|
||||
DWORD NtCreateFile
|
||||
uint32_t NtCreateFile
|
||||
(
|
||||
XLPDWORD FileHandle,
|
||||
DWORD DesiredAccess,
|
||||
be<uint32_t>* FileHandle,
|
||||
uint32_t DesiredAccess,
|
||||
XOBJECT_ATTRIBUTES* Attributes,
|
||||
XIO_STATUS_BLOCK* IoStatusBlock,
|
||||
uint64_t* AllocationSize,
|
||||
|
@ -232,16 +349,19 @@ DWORD NtCreateFile
|
|||
|
||||
uint32_t NtClose(uint32_t handle)
|
||||
{
|
||||
if (handle == (uint32_t)INVALID_HANDLE_VALUE)
|
||||
if (handle == GUEST_INVALID_HANDLE_VALUE)
|
||||
return 0xFFFFFFFF;
|
||||
|
||||
if (CHECK_GUEST_HANDLE(handle))
|
||||
if (IsKernelObject(handle))
|
||||
{
|
||||
ObCloseHandle(HOST_HANDLE(handle));
|
||||
DestroyKernelObject(handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return CloseHandle((HANDLE)handle) ? 0 : 0xFFFFFFFF;
|
||||
else
|
||||
{
|
||||
assert(false && "Unrecognized kernel object.");
|
||||
return 0xFFFFFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
void NtSetInformationFile()
|
||||
|
@ -254,20 +374,21 @@ uint32_t FscSetCacheElementCount()
|
|||
return 0;
|
||||
}
|
||||
|
||||
DWORD NtWaitForSingleObjectEx(DWORD Handle, DWORD WaitMode, DWORD Alertable, XLPQWORD Timeout)
|
||||
uint32_t NtWaitForSingleObjectEx(uint32_t Handle, uint32_t WaitMode, uint32_t Alertable, be<int64_t>* Timeout)
|
||||
{
|
||||
const auto status = WaitForSingleObjectEx((HANDLE)Handle, GuestTimeoutToMilliseconds(Timeout), Alertable);
|
||||
uint32_t timeout = GuestTimeoutToMilliseconds(Timeout);
|
||||
assert(timeout == 0 || timeout == INFINITE);
|
||||
|
||||
if (status == WAIT_IO_COMPLETION)
|
||||
if (IsKernelObject(Handle))
|
||||
{
|
||||
return STATUS_USER_APC;
|
||||
return GetKernelObject(Handle)->Wait(timeout);
|
||||
}
|
||||
else if (status)
|
||||
else
|
||||
{
|
||||
return STATUS_ALERTED;
|
||||
assert(false && "Unrecognized handle value.");
|
||||
}
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
return STATUS_TIMEOUT;
|
||||
}
|
||||
|
||||
void NtWriteFile()
|
||||
|
@ -280,7 +401,7 @@ void vsprintf_x()
|
|||
LOG_UTILITY("!!! STUB !!!");
|
||||
}
|
||||
|
||||
uint32_t ExGetXConfigSetting(uint16_t Category, uint16_t Setting, void* Buffer, uint16_t SizeOfBuffer, XLPDWORD RequiredSize)
|
||||
uint32_t ExGetXConfigSetting(uint16_t Category, uint16_t Setting, void* Buffer, uint16_t SizeOfBuffer, be<uint32_t>* RequiredSize)
|
||||
{
|
||||
uint32_t data[4]{};
|
||||
|
||||
|
@ -357,9 +478,9 @@ void MmQueryStatistics()
|
|||
LOG_UTILITY("!!! STUB !!!");
|
||||
}
|
||||
|
||||
uint32_t NtCreateEvent(uint32_t* handle, void* objAttributes, uint32_t eventType, uint32_t initialState)
|
||||
uint32_t NtCreateEvent(be<uint32_t>* handle, void* objAttributes, uint32_t eventType, uint32_t initialState)
|
||||
{
|
||||
*handle = ByteSwap((uint32_t)CreateEventA(nullptr, !eventType, !!initialState, nullptr));
|
||||
*handle = GetKernelHandle(CreateKernelObject<Event>(!eventType, !!initialState));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -393,9 +514,9 @@ void XexGetModuleSection()
|
|||
LOG_UTILITY("!!! STUB !!!");
|
||||
}
|
||||
|
||||
NTSTATUS RtlUnicodeToMultiByteN(PCHAR MultiByteString, DWORD MaxBytesInMultiByteString, XLPDWORD BytesInMultiByteString, PCWCH UnicodeString, ULONG BytesInUnicodeString)
|
||||
uint32_t RtlUnicodeToMultiByteN(char* MultiByteString, uint32_t MaxBytesInMultiByteString, be<uint32_t>* BytesInMultiByteString, const be<uint16_t>* UnicodeString, uint32_t BytesInUnicodeString)
|
||||
{
|
||||
const auto reqSize = BytesInUnicodeString / sizeof(wchar_t);
|
||||
const auto reqSize = BytesInUnicodeString / sizeof(uint16_t);
|
||||
|
||||
if (BytesInMultiByteString)
|
||||
*BytesInMultiByteString = reqSize;
|
||||
|
@ -405,7 +526,7 @@ NTSTATUS RtlUnicodeToMultiByteN(PCHAR MultiByteString, DWORD MaxBytesInMultiByte
|
|||
|
||||
for (size_t i = 0; i < reqSize; i++)
|
||||
{
|
||||
const auto c = ByteSwap(UnicodeString[i]);
|
||||
const auto c = UnicodeString[i].get();
|
||||
|
||||
MultiByteString[i] = c < 256 ? c : '?';
|
||||
}
|
||||
|
@ -413,24 +534,22 @@ NTSTATUS RtlUnicodeToMultiByteN(PCHAR MultiByteString, DWORD MaxBytesInMultiByte
|
|||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
DWORD KeDelayExecutionThread(DWORD WaitMode, bool Alertable, XLPQWORD Timeout)
|
||||
uint32_t KeDelayExecutionThread(uint32_t WaitMode, bool Alertable, be<int64_t>* Timeout)
|
||||
{
|
||||
// We don't do async file reads.
|
||||
if (Alertable)
|
||||
return STATUS_USER_APC;
|
||||
|
||||
timeBeginPeriod(1);
|
||||
const auto status = SleepEx(GuestTimeoutToMilliseconds(Timeout), Alertable);
|
||||
timeEndPeriod(1);
|
||||
uint32_t timeout = GuestTimeoutToMilliseconds(Timeout);
|
||||
|
||||
if (status == WAIT_IO_COMPLETION)
|
||||
{
|
||||
return STATUS_USER_APC;
|
||||
}
|
||||
else if (status)
|
||||
{
|
||||
return STATUS_ALERTED;
|
||||
}
|
||||
#ifdef _WIN32
|
||||
Sleep(timeout);
|
||||
#else
|
||||
if (timeout == 0)
|
||||
std::this_thread::yield();
|
||||
else
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(timeout));
|
||||
#endif
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
@ -485,8 +604,9 @@ void ObDereferenceObject()
|
|||
LOG_UTILITY("!!! STUB !!!");
|
||||
}
|
||||
|
||||
void KeSetBasePriorityThread(uint32_t thread, int priority)
|
||||
void KeSetBasePriorityThread(GuestThreadHandle* hThread, int priority)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (priority == 16)
|
||||
{
|
||||
priority = 15;
|
||||
|
@ -496,10 +616,11 @@ void KeSetBasePriorityThread(uint32_t thread, int priority)
|
|||
priority = -15;
|
||||
}
|
||||
|
||||
SetThreadPriority((HANDLE)thread, priority);
|
||||
SetThreadPriority(hThread == GetKernelObject(CURRENT_THREAD_HANDLE) ? GetCurrentThread() : hThread->thread.native_handle(), priority);
|
||||
#endif
|
||||
}
|
||||
|
||||
uint32_t ObReferenceObjectByHandle(uint32_t handle, uint32_t objectType, XLPDWORD object)
|
||||
uint32_t ObReferenceObjectByHandle(uint32_t handle, uint32_t objectType, be<uint32_t>* object)
|
||||
{
|
||||
*object = handle;
|
||||
return 0;
|
||||
|
@ -510,20 +631,17 @@ void KeQueryBasePriorityThread()
|
|||
LOG_UTILITY("!!! STUB !!!");
|
||||
}
|
||||
|
||||
uint32_t NtSuspendThread(uint32_t hThread, uint32_t* suspendCount)
|
||||
uint32_t NtSuspendThread(GuestThreadHandle* hThread, uint32_t* suspendCount)
|
||||
{
|
||||
DWORD count = SuspendThread((HANDLE)hThread);
|
||||
assert(hThread != GetKernelObject(CURRENT_THREAD_HANDLE) && hThread->thread.get_id() == std::this_thread::get_id());
|
||||
|
||||
if (count == (DWORD)-1)
|
||||
return E_FAIL;
|
||||
|
||||
if (suspendCount != nullptr)
|
||||
*suspendCount = ByteSwap(count);
|
||||
hThread->suspended = true;
|
||||
hThread->suspended.wait(true);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
uint32_t KeSetAffinityThread(DWORD Thread, DWORD Affinity, XLPDWORD lpPreviousAffinity)
|
||||
uint32_t KeSetAffinityThread(uint32_t Thread, uint32_t Affinity, be<uint32_t>* lpPreviousAffinity)
|
||||
{
|
||||
if (lpPreviousAffinity)
|
||||
*lpPreviousAffinity = 2;
|
||||
|
@ -531,38 +649,6 @@ uint32_t KeSetAffinityThread(DWORD Thread, DWORD Affinity, XLPDWORD lpPreviousAf
|
|||
return 0;
|
||||
}
|
||||
|
||||
struct Event : HostObject<XKEVENT>
|
||||
{
|
||||
HANDLE handle;
|
||||
|
||||
Event(XKEVENT* header)
|
||||
{
|
||||
handle = CreateEventA(nullptr, !header->Type, !!header->SignalState, nullptr);
|
||||
}
|
||||
|
||||
bool Set()
|
||||
{
|
||||
return SetEvent(handle);
|
||||
}
|
||||
|
||||
bool Reset()
|
||||
{
|
||||
return ResetEvent(handle);
|
||||
}
|
||||
};
|
||||
|
||||
struct Semaphore : HostObject<XKSEMAPHORE>
|
||||
{
|
||||
HANDLE handle;
|
||||
|
||||
Semaphore(XKSEMAPHORE* semaphore)
|
||||
{
|
||||
handle = CreateSemaphoreA(nullptr, semaphore->Header.SignalState, semaphore->Limit, nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
// https://devblogs.microsoft.com/oldnewthing/20160825-00/?p=94165
|
||||
|
||||
void RtlLeaveCriticalSection(XRTL_CRITICAL_SECTION* cs)
|
||||
{
|
||||
cs->RecursionCount--;
|
||||
|
@ -570,25 +656,29 @@ void RtlLeaveCriticalSection(XRTL_CRITICAL_SECTION* cs)
|
|||
if (cs->RecursionCount != 0)
|
||||
return;
|
||||
|
||||
InterlockedExchange(&cs->OwningThread, 0);
|
||||
WakeByAddressSingle(&cs->OwningThread);
|
||||
std::atomic_ref owningThread(cs->OwningThread);
|
||||
owningThread.store(0);
|
||||
owningThread.notify_one();
|
||||
}
|
||||
|
||||
void RtlEnterCriticalSection(XRTL_CRITICAL_SECTION* cs)
|
||||
{
|
||||
DWORD thisThread = GetCurrentThreadId();
|
||||
uint32_t thisThread = g_ppcContext->r13.u32;
|
||||
assert(thisThread != NULL);
|
||||
|
||||
std::atomic_ref owningThread(cs->OwningThread);
|
||||
|
||||
while (true)
|
||||
{
|
||||
DWORD previousOwner = InterlockedCompareExchangeAcquire(&cs->OwningThread, thisThread, 0);
|
||||
uint32_t previousOwner = 0;
|
||||
|
||||
if (previousOwner == 0 || previousOwner == thisThread)
|
||||
if (owningThread.compare_exchange_weak(previousOwner, thisThread) || previousOwner == thisThread)
|
||||
{
|
||||
cs->RecursionCount++;
|
||||
return;
|
||||
}
|
||||
|
||||
WaitOnAddress(&cs->OwningThread, &previousOwner, sizeof(previousOwner), INFINITE);
|
||||
owningThread.wait(previousOwner);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -609,7 +699,7 @@ void RtlFillMemoryUlong()
|
|||
|
||||
void KeBugCheckEx()
|
||||
{
|
||||
__debugbreak();
|
||||
__builtin_debugtrap();
|
||||
}
|
||||
|
||||
uint32_t KeGetCurrentProcessType()
|
||||
|
@ -639,15 +729,22 @@ void RtlRaiseException_x()
|
|||
|
||||
void KfReleaseSpinLock(uint32_t* spinLock)
|
||||
{
|
||||
InterlockedExchange((volatile long*)spinLock, 0);
|
||||
std::atomic_ref spinLockRef(*spinLock);
|
||||
spinLockRef = 0;
|
||||
}
|
||||
|
||||
void KfAcquireSpinLock(uint32_t* spinLock)
|
||||
{
|
||||
const auto ctx = GetPPCContext();
|
||||
std::atomic_ref spinLockRef(*spinLock);
|
||||
|
||||
while (InterlockedCompareExchange((volatile long*)spinLock, ByteSwap(*(uint32_t*)(g_memory.Translate(ctx->r13.u32 + 0x110))), 0) != 0)
|
||||
Sleep(0);
|
||||
while (true)
|
||||
{
|
||||
uint32_t expected = 0;
|
||||
if (spinLockRef.compare_exchange_weak(expected, g_ppcContext->r13.u32))
|
||||
break;
|
||||
|
||||
std::this_thread::yield();
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t KeQueryPerformanceFrequency()
|
||||
|
@ -679,15 +776,22 @@ void VdGetSystemCommandBuffer()
|
|||
|
||||
void KeReleaseSpinLockFromRaisedIrql(uint32_t* spinLock)
|
||||
{
|
||||
InterlockedExchange((volatile long*)spinLock, 0);
|
||||
std::atomic_ref spinLockRef(*spinLock);
|
||||
spinLockRef = 0;
|
||||
}
|
||||
|
||||
void KeAcquireSpinLockAtRaisedIrql(uint32_t* spinLock)
|
||||
{
|
||||
const auto ctx = GetPPCContext();
|
||||
std::atomic_ref spinLockRef(*spinLock);
|
||||
|
||||
while (InterlockedCompareExchange((volatile long*)spinLock, ByteSwap(*(uint32_t*)(g_memory.Translate(ctx->r13.u32 + 0x110))), 0) != 0)
|
||||
Sleep(0);
|
||||
while (true)
|
||||
{
|
||||
uint32_t expected = 0;
|
||||
if (spinLockRef.compare_exchange_weak(expected, g_ppcContext->r13.u32))
|
||||
break;
|
||||
|
||||
std::this_thread::yield();
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t KiApcNormalRoutineNop()
|
||||
|
@ -851,7 +955,7 @@ void VdEnableDisableClockGating()
|
|||
|
||||
void KeBugCheck()
|
||||
{
|
||||
__debugbreak();
|
||||
__builtin_debugtrap();
|
||||
}
|
||||
|
||||
void KeLockL2()
|
||||
|
@ -864,62 +968,95 @@ void KeUnlockL2()
|
|||
LOG_UTILITY("!!! STUB !!!");
|
||||
}
|
||||
|
||||
bool KeSetEvent(XKEVENT* pEvent, DWORD Increment, bool Wait)
|
||||
bool KeSetEvent(XKEVENT* pEvent, uint32_t Increment, bool Wait)
|
||||
{
|
||||
return ObQueryObject<Event>(*pEvent)->Set();
|
||||
bool result = QueryKernelObject<Event>(*pEvent)->Set();
|
||||
|
||||
++g_keSetEventGeneration;
|
||||
g_keSetEventGeneration.notify_all();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool KeResetEvent(XKEVENT* pEvent)
|
||||
{
|
||||
return ObQueryObject<Event>(*pEvent)->Reset();
|
||||
return QueryKernelObject<Event>(*pEvent)->Reset();
|
||||
}
|
||||
|
||||
DWORD KeWaitForSingleObject(XDISPATCHER_HEADER* Object, DWORD WaitReason, DWORD WaitMode, bool Alertable, XLPQWORD Timeout)
|
||||
uint32_t KeWaitForSingleObject(XDISPATCHER_HEADER* Object, uint32_t WaitReason, uint32_t WaitMode, bool Alertable, be<int64_t>* Timeout)
|
||||
{
|
||||
const uint64_t timeout = GuestTimeoutToMilliseconds(Timeout);
|
||||
|
||||
HANDLE handle = nullptr;
|
||||
const uint32_t timeout = GuestTimeoutToMilliseconds(Timeout);
|
||||
assert(timeout == INFINITE);
|
||||
|
||||
switch (Object->Type)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
handle = ObQueryObject<Event>(*Object)->handle;
|
||||
QueryKernelObject<Event>(*Object)->Wait(timeout);
|
||||
break;
|
||||
|
||||
case 5:
|
||||
handle = ObQueryObject<Semaphore>(*Object)->handle;
|
||||
QueryKernelObject<Semaphore>(*Object)->Wait(timeout);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
assert(false && "Unrecognized kernel object type.");
|
||||
return STATUS_TIMEOUT;
|
||||
}
|
||||
|
||||
return WaitForSingleObjectEx(handle, timeout, Alertable);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
uint32_t KeTlsGetValue(DWORD dwTlsIndex)
|
||||
static std::vector<size_t> g_tlsFreeIndices;
|
||||
static size_t g_tlsNextIndex = 0;
|
||||
static Mutex g_tlsAllocationMutex;
|
||||
|
||||
static uint32_t& KeTlsGetValueRef(size_t index)
|
||||
{
|
||||
return (uint32_t)TlsGetValue(dwTlsIndex);
|
||||
// Having this a global thread_local variable
|
||||
// for some reason crashes on boot in debug builds.
|
||||
thread_local std::vector<uint32_t> s_tlsValues;
|
||||
|
||||
if (s_tlsValues.size() <= index)
|
||||
{
|
||||
s_tlsValues.resize(index + 1, 0);
|
||||
}
|
||||
|
||||
return s_tlsValues[index];
|
||||
}
|
||||
|
||||
BOOL KeTlsSetValue(DWORD dwTlsIndex, DWORD lpTlsValue)
|
||||
uint32_t KeTlsGetValue(uint32_t dwTlsIndex)
|
||||
{
|
||||
return TlsSetValue(dwTlsIndex, (LPVOID)lpTlsValue);
|
||||
return KeTlsGetValueRef(dwTlsIndex);
|
||||
}
|
||||
|
||||
DWORD KeTlsAlloc()
|
||||
uint32_t KeTlsSetValue(uint32_t dwTlsIndex, uint32_t lpTlsValue)
|
||||
{
|
||||
return TlsAlloc();
|
||||
KeTlsGetValueRef(dwTlsIndex) = lpTlsValue;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL KeTlsFree(DWORD dwTlsIndex)
|
||||
uint32_t KeTlsAlloc()
|
||||
{
|
||||
return TlsFree(dwTlsIndex);
|
||||
std::lock_guard<Mutex> lock(g_tlsAllocationMutex);
|
||||
if (!g_tlsFreeIndices.empty())
|
||||
{
|
||||
size_t index = g_tlsFreeIndices.back();
|
||||
g_tlsFreeIndices.pop_back();
|
||||
return index;
|
||||
}
|
||||
|
||||
return g_tlsNextIndex++;
|
||||
}
|
||||
|
||||
DWORD XMsgInProcessCall(uint32_t app, uint32_t message, XDWORD* param1, XDWORD* param2)
|
||||
uint32_t KeTlsFree(uint32_t dwTlsIndex)
|
||||
{
|
||||
std::lock_guard<Mutex> lock(g_tlsAllocationMutex);
|
||||
g_tlsFreeIndices.push_back(dwTlsIndex);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
uint32_t XMsgInProcessCall(uint32_t app, uint32_t message, be<uint32_t>* param1, be<uint32_t>* param2)
|
||||
{
|
||||
if (message == 0x7001B)
|
||||
{
|
||||
|
@ -939,7 +1076,7 @@ void XamUserReadProfileSettings
|
|||
uint64_t* xuids,
|
||||
uint32_t settingCount,
|
||||
uint32_t* settingIds,
|
||||
XDWORD* bufferSize,
|
||||
be<uint32_t>* bufferSize,
|
||||
void* buffer,
|
||||
void* overlapped
|
||||
)
|
||||
|
@ -1036,10 +1173,14 @@ void XexGetModuleHandle()
|
|||
|
||||
bool RtlTryEnterCriticalSection(XRTL_CRITICAL_SECTION* cs)
|
||||
{
|
||||
DWORD thisThread = GetCurrentThreadId();
|
||||
DWORD previousOwner = InterlockedCompareExchangeAcquire(&cs->OwningThread, thisThread, 0);
|
||||
uint32_t thisThread = g_ppcContext->r13.u32;
|
||||
assert(thisThread != NULL);
|
||||
|
||||
if (previousOwner == 0 || previousOwner == thisThread)
|
||||
std::atomic_ref owningThread(cs->OwningThread);
|
||||
|
||||
uint32_t previousOwner = 0;
|
||||
|
||||
if (owningThread.compare_exchange_weak(previousOwner, thisThread) || previousOwner == thisThread)
|
||||
{
|
||||
cs->RecursionCount++;
|
||||
return true;
|
||||
|
@ -1116,19 +1257,15 @@ void NtQueryFullAttributesFile()
|
|||
LOG_UTILITY("!!! STUB !!!");
|
||||
}
|
||||
|
||||
NTSTATUS RtlMultiByteToUnicodeN(PWCH UnicodeString, ULONG MaxBytesInUnicodeString, XLPDWORD BytesInUnicodeString, const CHAR* MultiByteString, ULONG BytesInMultiByteString)
|
||||
uint32_t RtlMultiByteToUnicodeN(be<uint16_t>* UnicodeString, uint32_t MaxBytesInUnicodeString, be<uint32_t>* BytesInUnicodeString, const char* MultiByteString, uint32_t BytesInMultiByteString)
|
||||
{
|
||||
// i am lazy
|
||||
const auto n = MultiByteToWideChar(CP_UTF8, 0, MultiByteString, BytesInMultiByteString, UnicodeString, MaxBytesInUnicodeString);
|
||||
uint32_t length = std::min(MaxBytesInUnicodeString / 2, BytesInMultiByteString);
|
||||
|
||||
if (BytesInUnicodeString)
|
||||
*BytesInUnicodeString = n * sizeof(wchar_t);
|
||||
for (size_t i = 0; i < length; i++)
|
||||
UnicodeString[i] = MultiByteString[i];
|
||||
|
||||
if (n)
|
||||
{
|
||||
for (size_t i = 0; i < n; i++)
|
||||
UnicodeString[i] = ByteSwap(UnicodeString[i]);
|
||||
}
|
||||
if (BytesInUnicodeString != nullptr)
|
||||
*BytesInUnicodeString = length * 2;
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
@ -1143,41 +1280,41 @@ void MmQueryAllocationSize()
|
|||
LOG_UTILITY("!!! STUB !!!");
|
||||
}
|
||||
|
||||
uint32_t NtClearEvent(uint32_t handle, uint32_t* previousState)
|
||||
uint32_t NtClearEvent(Event* handle, uint32_t* previousState)
|
||||
{
|
||||
return ResetEvent((HANDLE)handle) ? 0 : 0xFFFFFFFF;
|
||||
handle->Reset();
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t NtResumeThread(uint32_t hThread, uint32_t* suspendCount)
|
||||
uint32_t NtResumeThread(GuestThreadHandle* hThread, uint32_t* suspendCount)
|
||||
{
|
||||
DWORD count = ResumeThread((HANDLE)hThread);
|
||||
assert(hThread != GetKernelObject(CURRENT_THREAD_HANDLE));
|
||||
|
||||
if (count == (DWORD)-1)
|
||||
return E_FAIL;
|
||||
|
||||
if (suspendCount != nullptr)
|
||||
*suspendCount = ByteSwap(count);
|
||||
hThread->suspended = false;
|
||||
hThread->suspended.notify_all();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
uint32_t NtSetEvent(uint32_t handle, uint32_t* previousState)
|
||||
uint32_t NtSetEvent(Event* handle, uint32_t* previousState)
|
||||
{
|
||||
return SetEvent((HANDLE)handle) ? 0 : 0xFFFFFFFF;
|
||||
handle->Set();
|
||||
return 0;
|
||||
}
|
||||
|
||||
NTSTATUS NtCreateSemaphore(XLPDWORD Handle, XOBJECT_ATTRIBUTES* ObjectAttributes, DWORD InitialCount, DWORD MaximumCount)
|
||||
uint32_t NtCreateSemaphore(be<uint32_t>* Handle, XOBJECT_ATTRIBUTES* ObjectAttributes, uint32_t InitialCount, uint32_t MaximumCount)
|
||||
{
|
||||
*Handle = (uint32_t)CreateSemaphoreA(nullptr, InitialCount, MaximumCount, nullptr);
|
||||
*Handle = GetKernelHandle(CreateKernelObject<Semaphore>(InitialCount, MaximumCount));
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NTSTATUS NtReleaseSemaphore(uint32_t Handle, DWORD ReleaseCount, LONG* PreviousCount)
|
||||
uint32_t NtReleaseSemaphore(Semaphore* Handle, uint32_t ReleaseCount, int32_t* PreviousCount)
|
||||
{
|
||||
ReleaseSemaphore((HANDLE)Handle, ReleaseCount, PreviousCount);
|
||||
uint32_t previousCount;
|
||||
Handle->Release(ReleaseCount, &previousCount);
|
||||
|
||||
if (PreviousCount)
|
||||
*PreviousCount = ByteSwap(*PreviousCount);
|
||||
if (PreviousCount != nullptr)
|
||||
*PreviousCount = ByteSwap(previousCount);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
@ -1212,11 +1349,17 @@ void NtFlushBuffersFile()
|
|||
LOG_UTILITY("!!! STUB !!!");
|
||||
}
|
||||
|
||||
void KeQuerySystemTime(uint64_t* time)
|
||||
void KeQuerySystemTime(be<uint64_t>* time)
|
||||
{
|
||||
FILETIME t;
|
||||
GetSystemTimeAsFileTime(&t);
|
||||
*time = ByteSwap((uint64_t(t.dwHighDateTime) << 32) | t.dwLowDateTime);
|
||||
constexpr int64_t FILETIME_EPOCH_DIFFERENCE = 116444736000000000LL;
|
||||
|
||||
auto now = std::chrono::system_clock::now();
|
||||
auto timeSinceEpoch = now.time_since_epoch();
|
||||
|
||||
int64_t currentTime100ns = std::chrono::duration_cast<std::chrono::duration<int64_t, std::ratio<1, 10000000>>>(timeSinceEpoch).count();
|
||||
currentTime100ns += FILETIME_EPOCH_DIFFERENCE;
|
||||
|
||||
*time = currentTime100ns;
|
||||
}
|
||||
|
||||
void RtlTimeToTimeFields()
|
||||
|
@ -1244,14 +1387,14 @@ void ExTerminateThread()
|
|||
LOG_UTILITY("!!! STUB !!!");
|
||||
}
|
||||
|
||||
uint32_t ExCreateThread(XLPDWORD handle, uint32_t stackSize, XLPDWORD threadId, uint32_t xApiThreadStartup, uint32_t startAddress, uint32_t startContext, uint32_t creationFlags)
|
||||
uint32_t ExCreateThread(be<uint32_t>* handle, uint32_t stackSize, be<uint32_t>* threadId, uint32_t xApiThreadStartup, uint32_t startAddress, uint32_t startContext, uint32_t creationFlags)
|
||||
{
|
||||
LOGF_UTILITY("0x{:X}, 0x{:X}, 0x{:X}, 0x{:X}, 0x{:X}, 0x{:X}, 0x{:X}",
|
||||
(intptr_t)handle, stackSize, (intptr_t)threadId, xApiThreadStartup, startAddress, startContext, creationFlags);
|
||||
|
||||
DWORD hostThreadId;
|
||||
uint32_t hostThreadId;
|
||||
|
||||
*handle = (uint32_t)GuestThread::Start(startAddress, startContext, creationFlags, &hostThreadId);
|
||||
*handle = GetKernelHandle(GuestThread::Start({ startAddress, startContext, creationFlags }, &hostThreadId));
|
||||
|
||||
if (threadId != nullptr)
|
||||
*threadId = hostThreadId;
|
||||
|
@ -1329,21 +1472,43 @@ void NetDll_XNetGetTitleXnAddr()
|
|||
LOG_UTILITY("!!! STUB !!!");
|
||||
}
|
||||
|
||||
DWORD KeWaitForMultipleObjects(DWORD Count, xpointer<XDISPATCHER_HEADER>* Objects, DWORD WaitType, DWORD WaitReason, DWORD WaitMode, DWORD Alertable, XLPQWORD Timeout)
|
||||
uint32_t KeWaitForMultipleObjects(uint32_t Count, xpointer<XDISPATCHER_HEADER>* Objects, uint32_t WaitType, uint32_t WaitReason, uint32_t WaitMode, uint32_t Alertable, be<int64_t>* Timeout)
|
||||
{
|
||||
// TODO: create actual objects by type.
|
||||
// FIXME: This function is only accounting for events.
|
||||
|
||||
const uint64_t timeout = GuestTimeoutToMilliseconds(Timeout);
|
||||
assert(timeout == INFINITE);
|
||||
|
||||
thread_local std::vector<HANDLE> events;
|
||||
events.resize(Count);
|
||||
|
||||
for (size_t i = 0; i < Count; i++)
|
||||
if (WaitType == 0) // Wait all
|
||||
{
|
||||
assert(Objects[i]->Type <= 1);
|
||||
events[i] = ObQueryObject<Event>(*Objects[i].get())->handle;
|
||||
for (size_t i = 0; i < Count; i++)
|
||||
QueryKernelObject<Event>(*Objects[i])->Wait(timeout);
|
||||
}
|
||||
else
|
||||
{
|
||||
thread_local std::vector<Event*> s_events;
|
||||
s_events.resize(Count);
|
||||
|
||||
for (size_t i = 0; i < Count; i++)
|
||||
s_events[i] = QueryKernelObject<Event>(*Objects[i]);
|
||||
|
||||
while (true)
|
||||
{
|
||||
uint32_t generation = g_keSetEventGeneration.load();
|
||||
|
||||
for (size_t i = 0; i < Count; i++)
|
||||
{
|
||||
if (s_events[i]->Wait(0) == STATUS_SUCCESS)
|
||||
{
|
||||
return STATUS_WAIT_0 + i;
|
||||
}
|
||||
}
|
||||
|
||||
g_keSetEventGeneration.wait(generation);
|
||||
}
|
||||
}
|
||||
|
||||
return WaitForMultipleObjectsEx(Count, events.data(), WaitType == 0, timeout, Alertable);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
uint32_t KeRaiseIrqlToDpcLevel()
|
||||
|
@ -1355,8 +1520,9 @@ void KfLowerIrql() { }
|
|||
|
||||
uint32_t KeReleaseSemaphore(XKSEMAPHORE* semaphore, uint32_t increment, uint32_t adjustment, uint32_t wait)
|
||||
{
|
||||
auto* object = ObQueryObject<Semaphore>(semaphore->Header);
|
||||
return ReleaseSemaphore(object->handle, adjustment, nullptr) ? 0 : 0xFFFFFFFF;
|
||||
auto* object = QueryKernelObject<Semaphore>(semaphore->Header);
|
||||
object->Release(adjustment, nullptr);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
void XAudioGetVoiceCategoryVolume()
|
||||
|
@ -1364,16 +1530,19 @@ void XAudioGetVoiceCategoryVolume()
|
|||
LOG_UTILITY("!!! STUB !!!");
|
||||
}
|
||||
|
||||
DWORD XAudioGetVoiceCategoryVolumeChangeMask(DWORD Driver, XLPDWORD Mask)
|
||||
uint32_t XAudioGetVoiceCategoryVolumeChangeMask(uint32_t Driver, be<uint32_t>* Mask)
|
||||
{
|
||||
*Mask = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t KeResumeThread(uint32_t object)
|
||||
uint32_t KeResumeThread(GuestThreadHandle* object)
|
||||
{
|
||||
LOGF_UTILITY("0x{:x}", object);
|
||||
return ResumeThread((HANDLE)object);
|
||||
assert(object != GetKernelObject(CURRENT_THREAD_HANDLE));
|
||||
|
||||
object->suspended = false;
|
||||
object->suspended.notify_all();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void KeInitializeSemaphore(XKSEMAPHORE* semaphore, uint32_t count, uint32_t limit)
|
||||
|
@ -1382,7 +1551,7 @@ void KeInitializeSemaphore(XKSEMAPHORE* semaphore, uint32_t count, uint32_t limi
|
|||
semaphore->Header.SignalState = count;
|
||||
semaphore->Limit = limit;
|
||||
|
||||
auto* object = ObQueryObject<Semaphore>(semaphore->Header);
|
||||
auto* object = QueryKernelObject<Semaphore>(semaphore->Header);
|
||||
}
|
||||
|
||||
void XMAReleaseContext()
|
||||
|
@ -1395,7 +1564,7 @@ void XMACreateContext()
|
|||
LOG_UTILITY("!!! STUB !!!");
|
||||
}
|
||||
|
||||
// uint32_t XAudioRegisterRenderDriverClient(XLPDWORD callback, XLPDWORD driver)
|
||||
// uint32_t XAudioRegisterRenderDriverClient(be<uint32_t>* callback, be<uint32_t>* driver)
|
||||
// {
|
||||
// //printf("XAudioRegisterRenderDriverClient(): %x %x\n");
|
||||
//
|
||||
|
|
|
@ -6,78 +6,140 @@
|
|||
#include <cpu/guest_thread.h>
|
||||
#include <os/logger.h>
|
||||
|
||||
bool FindHandleCloser(void* handle)
|
||||
struct FileHandle : KernelObject
|
||||
{
|
||||
FindClose(handle);
|
||||
return false;
|
||||
}
|
||||
std::fstream stream;
|
||||
std::filesystem::path path;
|
||||
};
|
||||
|
||||
SWA_API uint32_t XCreateFileA
|
||||
struct FindHandle : KernelObject
|
||||
{
|
||||
std::error_code ec;
|
||||
std::filesystem::path searchPath;
|
||||
std::filesystem::directory_iterator iterator;
|
||||
|
||||
void fillFindData(WIN32_FIND_DATAA* lpFindFileData)
|
||||
{
|
||||
if (iterator->is_directory())
|
||||
lpFindFileData->dwFileAttributes = ByteSwap(FILE_ATTRIBUTE_DIRECTORY);
|
||||
else if (iterator->is_regular_file())
|
||||
lpFindFileData->dwFileAttributes = ByteSwap(FILE_ATTRIBUTE_NORMAL);
|
||||
|
||||
std::u8string pathU8Str = iterator->path().lexically_relative(searchPath).u8string();
|
||||
uint64_t fileSize = iterator->file_size(ec);
|
||||
strncpy(lpFindFileData->cFileName, (const char *)(pathU8Str.c_str()), sizeof(lpFindFileData->cFileName));
|
||||
lpFindFileData->nFileSizeLow = ByteSwap(uint32_t(fileSize >> 32U));
|
||||
lpFindFileData->nFileSizeHigh = ByteSwap(uint32_t(fileSize));
|
||||
lpFindFileData->ftCreationTime = {};
|
||||
lpFindFileData->ftLastAccessTime = {};
|
||||
lpFindFileData->ftLastWriteTime = {};
|
||||
}
|
||||
};
|
||||
|
||||
SWA_API FileHandle* XCreateFileA
|
||||
(
|
||||
LPCSTR lpFileName,
|
||||
DWORD dwDesiredAccess,
|
||||
DWORD dwShareMode,
|
||||
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
|
||||
DWORD dwCreationDisposition,
|
||||
DWORD dwFlagsAndAttributes
|
||||
const char* lpFileName,
|
||||
uint32_t dwDesiredAccess,
|
||||
uint32_t dwShareMode,
|
||||
void* lpSecurityAttributes,
|
||||
uint32_t dwCreationDisposition,
|
||||
uint32_t dwFlagsAndAttributes
|
||||
)
|
||||
{
|
||||
const auto handle = (uint32_t)CreateFileA(
|
||||
FileSystem::TransformPath(lpFileName),
|
||||
dwDesiredAccess,
|
||||
dwShareMode,
|
||||
nullptr,
|
||||
dwCreationDisposition,
|
||||
dwFlagsAndAttributes & ~(FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED),
|
||||
nullptr);
|
||||
assert(((dwDesiredAccess & ~(GENERIC_READ | GENERIC_WRITE | FILE_READ_DATA)) == 0) && "Unknown desired access bits.");
|
||||
assert(((dwShareMode & ~(FILE_SHARE_READ | FILE_SHARE_WRITE)) == 0) && "Unknown share mode bits.");
|
||||
assert(((dwCreationDisposition & ~(CREATE_NEW | CREATE_ALWAYS)) == 0) && "Unknown creation disposition bits.");
|
||||
|
||||
GuestThread::SetLastError(GetLastError());
|
||||
std::filesystem::path filePath = std::u8string_view((const char8_t*)(FileSystem::TransformPath(lpFileName)));
|
||||
std::fstream fileStream;
|
||||
std::ios::openmode fileOpenMode = std::ios::binary;
|
||||
if (dwDesiredAccess & (GENERIC_READ | FILE_READ_DATA))
|
||||
{
|
||||
fileOpenMode |= std::ios::in;
|
||||
}
|
||||
|
||||
LOGF_UTILITY("\"{}\", 0x{:X}, 0x{:X}, 0x{:X}, 0x{:X}, 0x{:X} -> 0x{:X}",
|
||||
lpFileName, dwDesiredAccess, dwShareMode, (intptr_t)lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, handle);
|
||||
if (dwDesiredAccess & GENERIC_WRITE)
|
||||
{
|
||||
fileOpenMode |= std::ios::out;
|
||||
}
|
||||
|
||||
return handle;
|
||||
fileStream.open(filePath, fileOpenMode);
|
||||
if (!fileStream.is_open())
|
||||
{
|
||||
#ifdef _WIN32
|
||||
GuestThread::SetLastError(GetLastError());
|
||||
#endif
|
||||
return GetInvalidKernelObject<FileHandle>();
|
||||
}
|
||||
|
||||
FileHandle *fileHandle = CreateKernelObject<FileHandle>();
|
||||
fileHandle->stream = std::move(fileStream);
|
||||
fileHandle->path = std::move(filePath);
|
||||
return fileHandle;
|
||||
}
|
||||
|
||||
static DWORD XGetFileSizeA(uint32_t hFile, LPDWORD lpFileSizeHigh)
|
||||
static uint32_t XGetFileSizeA(FileHandle* hFile, be<uint32_t>* lpFileSizeHigh)
|
||||
{
|
||||
DWORD fileSize = GetFileSize((HANDLE)hFile, lpFileSizeHigh);
|
||||
std::error_code ec;
|
||||
auto fileSize = std::filesystem::file_size(hFile->path, ec);
|
||||
if (!ec)
|
||||
{
|
||||
if (lpFileSizeHigh != nullptr)
|
||||
{
|
||||
*lpFileSizeHigh = uint32_t(fileSize >> 32U);
|
||||
}
|
||||
|
||||
if (lpFileSizeHigh != nullptr)
|
||||
*lpFileSizeHigh = ByteSwap(*lpFileSizeHigh);
|
||||
return (uint32_t)(fileSize);
|
||||
}
|
||||
|
||||
return fileSize;
|
||||
return INVALID_FILE_SIZE;
|
||||
}
|
||||
|
||||
BOOL XGetFileSizeExA(uint32_t hFile, PLARGE_INTEGER lpFileSize)
|
||||
uint32_t XGetFileSizeExA(FileHandle* hFile, LARGE_INTEGER* lpFileSize)
|
||||
{
|
||||
BOOL result = GetFileSizeEx((HANDLE)hFile, lpFileSize);
|
||||
std::error_code ec;
|
||||
auto fileSize = std::filesystem::file_size(hFile->path, ec);
|
||||
if (!ec)
|
||||
{
|
||||
if (lpFileSize != nullptr)
|
||||
{
|
||||
lpFileSize->QuadPart = ByteSwap(fileSize);
|
||||
}
|
||||
|
||||
if (result)
|
||||
lpFileSize->QuadPart = ByteSwap(lpFileSize->QuadPart);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return result;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL XReadFile
|
||||
uint32_t XReadFile
|
||||
(
|
||||
uint32_t hFile,
|
||||
LPVOID lpBuffer,
|
||||
DWORD nNumberOfBytesToRead,
|
||||
XLPDWORD lpNumberOfBytesRead,
|
||||
FileHandle* hFile,
|
||||
void* lpBuffer,
|
||||
uint32_t nNumberOfBytesToRead,
|
||||
be<uint32_t>* lpNumberOfBytesRead,
|
||||
XOVERLAPPED* lpOverlapped
|
||||
)
|
||||
{
|
||||
uint32_t result = FALSE;
|
||||
if (lpOverlapped != nullptr)
|
||||
{
|
||||
LONG distanceToMoveHigh = lpOverlapped->OffsetHigh;
|
||||
|
||||
if (SetFilePointer((HANDLE)hFile, lpOverlapped->Offset, &distanceToMoveHigh, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
|
||||
std::streamoff streamOffset = lpOverlapped->Offset + (std::streamoff(lpOverlapped->OffsetHigh.get()) << 32U);
|
||||
hFile->stream.clear();
|
||||
hFile->stream.seekg(streamOffset, std::ios::beg);
|
||||
if (hFile->stream.bad())
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
DWORD numberOfBytesRead;
|
||||
BOOL result = ReadFile((HANDLE)hFile, lpBuffer, nNumberOfBytesToRead, &numberOfBytesRead, nullptr);
|
||||
uint32_t numberOfBytesRead;
|
||||
hFile->stream.read((char *)(lpBuffer), nNumberOfBytesToRead);
|
||||
if (!hFile->stream.bad())
|
||||
{
|
||||
numberOfBytesRead = uint32_t(hFile->stream.gcount());
|
||||
result = TRUE;
|
||||
}
|
||||
|
||||
if (result)
|
||||
{
|
||||
|
@ -85,9 +147,6 @@ BOOL XReadFile
|
|||
{
|
||||
lpOverlapped->Internal = 0;
|
||||
lpOverlapped->InternalHigh = numberOfBytesRead;
|
||||
|
||||
if (lpOverlapped->hEvent != NULL)
|
||||
SetEvent((HANDLE)lpOverlapped->hEvent.get());
|
||||
}
|
||||
else if (lpNumberOfBytesRead != nullptr)
|
||||
{
|
||||
|
@ -95,115 +154,197 @@ BOOL XReadFile
|
|||
}
|
||||
}
|
||||
|
||||
// printf("ReadFile(): %x %x %x %x %x %x\n", hFile, lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead, lpOverlapped, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DWORD XSetFilePointer(uint32_t hFile, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh, DWORD dwMoveMethod)
|
||||
uint32_t XSetFilePointer(FileHandle* hFile, int32_t lDistanceToMove, be<int32_t>* lpDistanceToMoveHigh, uint32_t dwMoveMethod)
|
||||
{
|
||||
LONG distanceToMoveHigh = lpDistanceToMoveHigh ? ByteSwap(*lpDistanceToMoveHigh) : 0;
|
||||
DWORD result = SetFilePointer((HANDLE)hFile, lDistanceToMove, lpDistanceToMoveHigh ? &distanceToMoveHigh : nullptr, dwMoveMethod);
|
||||
int32_t distanceToMoveHigh = lpDistanceToMoveHigh ? lpDistanceToMoveHigh->get() : 0;
|
||||
std::streamoff streamOffset = lDistanceToMove + (std::streamoff(distanceToMoveHigh) << 32U);
|
||||
std::fstream::seekdir streamSeekDir = {};
|
||||
switch (dwMoveMethod)
|
||||
{
|
||||
case FILE_BEGIN:
|
||||
streamSeekDir = std::ios::beg;
|
||||
break;
|
||||
case FILE_CURRENT:
|
||||
streamSeekDir = std::ios::cur;
|
||||
break;
|
||||
case FILE_END:
|
||||
streamSeekDir = std::ios::end;
|
||||
break;
|
||||
default:
|
||||
assert(false && "Unknown move method.");
|
||||
break;
|
||||
}
|
||||
|
||||
hFile->stream.clear();
|
||||
hFile->stream.seekg(streamOffset, streamSeekDir);
|
||||
if (hFile->stream.bad())
|
||||
{
|
||||
return INVALID_SET_FILE_POINTER;
|
||||
}
|
||||
|
||||
std::streampos streamPos = hFile->stream.tellg();
|
||||
if (lpDistanceToMoveHigh != nullptr)
|
||||
*lpDistanceToMoveHigh = ByteSwap(distanceToMoveHigh);
|
||||
*lpDistanceToMoveHigh = int32_t(streamPos >> 32U);
|
||||
|
||||
return result;
|
||||
return uint32_t(streamPos);
|
||||
}
|
||||
|
||||
BOOL XSetFilePointerEx(uint32_t hFile, LONG lDistanceToMove, PLARGE_INTEGER lpNewFilePointer, DWORD dwMoveMethod)
|
||||
uint32_t XSetFilePointerEx(FileHandle* hFile, int32_t lDistanceToMove, LARGE_INTEGER* lpNewFilePointer, uint32_t dwMoveMethod)
|
||||
{
|
||||
LARGE_INTEGER distanceToMove;
|
||||
distanceToMove.QuadPart = lDistanceToMove;
|
||||
std::fstream::seekdir streamSeekDir = {};
|
||||
switch (dwMoveMethod)
|
||||
{
|
||||
case FILE_BEGIN:
|
||||
streamSeekDir = std::ios::beg;
|
||||
break;
|
||||
case FILE_CURRENT:
|
||||
streamSeekDir = std::ios::cur;
|
||||
break;
|
||||
case FILE_END:
|
||||
streamSeekDir = std::ios::end;
|
||||
break;
|
||||
default:
|
||||
assert(false && "Unknown move method.");
|
||||
break;
|
||||
}
|
||||
|
||||
DWORD result = SetFilePointerEx((HANDLE)hFile, distanceToMove, lpNewFilePointer, dwMoveMethod);
|
||||
hFile->stream.clear();
|
||||
hFile->stream.seekg(lDistanceToMove, streamSeekDir);
|
||||
if (hFile->stream.bad())
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (lpNewFilePointer != nullptr)
|
||||
lpNewFilePointer->QuadPart = ByteSwap(lpNewFilePointer->QuadPart);
|
||||
{
|
||||
lpNewFilePointer->QuadPart = ByteSwap(int64_t(hFile->stream.tellg()));
|
||||
}
|
||||
|
||||
return result;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
uint32_t XFindFirstFileA(LPCSTR lpFileName, LPWIN32_FIND_DATAA lpFindFileData)
|
||||
FindHandle* XFindFirstFileA(const char* lpFileName, WIN32_FIND_DATAA* lpFindFileData)
|
||||
{
|
||||
auto& data = *lpFindFileData;
|
||||
const auto handle = FindFirstFileA(FileSystem::TransformPath(lpFileName), &data);
|
||||
const char *transformedPath = FileSystem::TransformPath(lpFileName);
|
||||
size_t transformedPathLength = strlen(transformedPath);
|
||||
if (transformedPathLength == 0)
|
||||
return (FindHandle*)GUEST_INVALID_HANDLE_VALUE;
|
||||
|
||||
GuestThread::SetLastError(GetLastError());
|
||||
std::filesystem::path dirPath;
|
||||
if (strstr(transformedPath, "\\*") == (&transformedPath[transformedPathLength - 2]) || strstr(transformedPath, "/*") == (&transformedPath[transformedPathLength - 2]))
|
||||
{
|
||||
dirPath = std::u8string_view((const char8_t*)(transformedPath), transformedPathLength - 2);
|
||||
}
|
||||
else if (strstr(transformedPath, "\\*.*") == (&transformedPath[transformedPathLength - 4]) || strstr(transformedPath, "/*.*") == (&transformedPath[transformedPathLength - 4]))
|
||||
{
|
||||
dirPath = std::u8string_view((const char8_t *)(transformedPath), transformedPathLength - 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
dirPath = std::u8string_view((const char8_t *)(transformedPath), transformedPathLength);
|
||||
assert(!dirPath.has_extension() && "Unknown search pattern.");
|
||||
}
|
||||
|
||||
if (handle == INVALID_HANDLE_VALUE)
|
||||
return 0xFFFFFFFF;
|
||||
if (!std::filesystem::is_directory(dirPath))
|
||||
return GetInvalidKernelObject<FindHandle>();
|
||||
|
||||
ByteSwapInplace(data.dwFileAttributes);
|
||||
ByteSwapInplace(*(uint64_t*)&data.ftCreationTime);
|
||||
ByteSwapInplace(*(uint64_t*)&data.ftLastAccessTime);
|
||||
ByteSwapInplace(*(uint64_t*)&data.ftLastWriteTime);
|
||||
ByteSwapInplace(*(uint64_t*)&data.nFileSizeHigh);
|
||||
std::filesystem::directory_iterator dirIterator(dirPath);
|
||||
if (dirIterator == std::filesystem::directory_iterator())
|
||||
return GetInvalidKernelObject<FindHandle>();
|
||||
|
||||
return GUEST_HANDLE(ObInsertObject(handle, FindHandleCloser));
|
||||
FindHandle *findHandle = CreateKernelObject<FindHandle>();
|
||||
findHandle->searchPath = std::move(dirPath);
|
||||
findHandle->iterator = std::move(dirIterator);
|
||||
findHandle->fillFindData(lpFindFileData);
|
||||
return findHandle;
|
||||
}
|
||||
|
||||
uint32_t XFindNextFileA(uint32_t Handle, LPWIN32_FIND_DATAA lpFindFileData)
|
||||
uint32_t XFindNextFileA(FindHandle* Handle, WIN32_FIND_DATAA* lpFindFileData)
|
||||
{
|
||||
auto* handle = ObQueryObject(HOST_HANDLE(Handle));
|
||||
auto& data = *lpFindFileData;
|
||||
const auto result = FindNextFileA(handle, &data);
|
||||
Handle->iterator++;
|
||||
|
||||
ByteSwapInplace(data.dwFileAttributes);
|
||||
ByteSwapInplace(*(uint64_t*)&data.ftCreationTime);
|
||||
ByteSwapInplace(*(uint64_t*)&data.ftLastAccessTime);
|
||||
ByteSwapInplace(*(uint64_t*)&data.ftLastWriteTime);
|
||||
ByteSwapInplace(*(uint64_t*)&data.nFileSizeHigh);
|
||||
|
||||
return result;
|
||||
if (Handle->iterator == std::filesystem::directory_iterator())
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
Handle->fillFindData(lpFindFileData);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
BOOL XReadFileEx(uint32_t hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, XOVERLAPPED* lpOverlapped, uint32_t lpCompletionRoutine)
|
||||
uint32_t XReadFileEx(FileHandle* hFile, void* lpBuffer, uint32_t nNumberOfBytesToRead, XOVERLAPPED* lpOverlapped, uint32_t lpCompletionRoutine)
|
||||
{
|
||||
LONG distanceToMoveHigh = lpOverlapped->OffsetHigh;
|
||||
|
||||
if (SetFilePointer((HANDLE)hFile, lpOverlapped->Offset, &distanceToMoveHigh, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
|
||||
uint32_t result = FALSE;
|
||||
uint32_t numberOfBytesRead;
|
||||
std::streamoff streamOffset = lpOverlapped->Offset + (std::streamoff(lpOverlapped->OffsetHigh.get()) << 32U);
|
||||
hFile->stream.clear();
|
||||
hFile->stream.seekg(streamOffset, std::ios::beg);
|
||||
if (hFile->stream.bad())
|
||||
return FALSE;
|
||||
|
||||
DWORD numberOfBytesRead;
|
||||
BOOL result = ReadFile((HANDLE)hFile, lpBuffer, nNumberOfBytesToRead, &numberOfBytesRead, nullptr);
|
||||
hFile->stream.read((char *)(lpBuffer), nNumberOfBytesToRead);
|
||||
if (!hFile->stream.bad())
|
||||
{
|
||||
numberOfBytesRead = uint32_t(hFile->stream.gcount());
|
||||
result = TRUE;
|
||||
}
|
||||
|
||||
if (result)
|
||||
{
|
||||
lpOverlapped->Internal = 0;
|
||||
lpOverlapped->InternalHigh = numberOfBytesRead;
|
||||
|
||||
if (lpOverlapped->hEvent != NULL)
|
||||
SetEvent((HANDLE)lpOverlapped->hEvent.get());
|
||||
}
|
||||
|
||||
// printf("ReadFileEx(): %x %x %x %x %x %x\n", hFile, lpBuffer, nNumberOfBytesToRead, lpOverlapped, lpCompletionRoutine, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DWORD XGetFileAttributesA(LPCSTR lpFileName)
|
||||
uint32_t XGetFileAttributesA(const char* lpFileName)
|
||||
{
|
||||
return GetFileAttributesA(FileSystem::TransformPath(lpFileName));
|
||||
std::filesystem::path filePath(std::u8string_view((const char8_t*)(FileSystem::TransformPath(lpFileName))));
|
||||
if (std::filesystem::is_directory(filePath))
|
||||
return FILE_ATTRIBUTE_DIRECTORY;
|
||||
else if (std::filesystem::is_regular_file(filePath))
|
||||
return FILE_ATTRIBUTE_NORMAL;
|
||||
else
|
||||
return INVALID_FILE_ATTRIBUTES;
|
||||
}
|
||||
|
||||
BOOL XWriteFile(uint32_t hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped)
|
||||
uint32_t XWriteFile(FileHandle* hFile, const void* lpBuffer, uint32_t nNumberOfBytesToWrite, be<uint32_t>* lpNumberOfBytesWritten, void* lpOverlapped)
|
||||
{
|
||||
assert(lpOverlapped == nullptr);
|
||||
assert(lpOverlapped == nullptr && "Overlapped not implemented.");
|
||||
|
||||
BOOL result = WriteFile((HANDLE)hFile, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, nullptr);
|
||||
hFile->stream.write((const char *)(lpBuffer), nNumberOfBytesToWrite);
|
||||
if (hFile->stream.bad())
|
||||
return FALSE;
|
||||
|
||||
if (result && lpNumberOfBytesWritten != nullptr)
|
||||
ByteSwapInplace(*lpNumberOfBytesWritten);
|
||||
if (lpNumberOfBytesWritten != nullptr)
|
||||
*lpNumberOfBytesWritten = uint32_t(hFile->stream.gcount());
|
||||
|
||||
return result;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void fixSlashes(char *path)
|
||||
{
|
||||
while (*path != 0)
|
||||
{
|
||||
if (*path == '\\')
|
||||
{
|
||||
*path = '/';
|
||||
}
|
||||
|
||||
path++;
|
||||
}
|
||||
}
|
||||
|
||||
const char* FileSystem::TransformPath(const char* path)
|
||||
{
|
||||
thread_local char builtPath[2048]{};
|
||||
const char* relativePath = strstr(path, ":\\");
|
||||
|
||||
if (relativePath != nullptr)
|
||||
{
|
||||
// rooted folder, handle direction
|
||||
|
@ -215,12 +356,19 @@ const char* FileSystem::TransformPath(const char* path)
|
|||
strncpy(builtPath, newRoot.data(), newRoot.size());
|
||||
builtPath[newRoot.size()] = '\\';
|
||||
strcpy(builtPath + newRoot.size() + 1, relativePath + 2);
|
||||
|
||||
return builtPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy(builtPath, relativePath + 2, sizeof(builtPath));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy(builtPath, path, sizeof(builtPath));
|
||||
}
|
||||
|
||||
return relativePath != nullptr ? relativePath + 2 : path;
|
||||
fixSlashes(builtPath);
|
||||
return builtPath;
|
||||
}
|
||||
|
||||
SWA_API const char* XExpandFilePathA(const char* path)
|
||||
|
|
|
@ -3,25 +3,46 @@
|
|||
|
||||
Memory::Memory(void* address, size_t size) : size(size)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
base = (char*)VirtualAlloc(address, size, MEM_RESERVE, PAGE_READWRITE);
|
||||
|
||||
if (base == nullptr)
|
||||
base = (char*)VirtualAlloc(nullptr, size, MEM_RESERVE, PAGE_READWRITE);
|
||||
#else
|
||||
base = (char*)mmap(address, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
|
||||
if (base == (char*)MAP_FAILED)
|
||||
base = (char*)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
|
||||
mprotect(base, 4096, PROT_NONE);
|
||||
#endif
|
||||
}
|
||||
|
||||
void* Memory::Alloc(size_t offset, size_t size, uint32_t type)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return VirtualAlloc(base + offset, size, type, PAGE_READWRITE);
|
||||
#else
|
||||
return base + offset;
|
||||
#endif
|
||||
}
|
||||
|
||||
void* Memory::Commit(size_t offset, size_t size)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return Alloc(offset, size, MEM_COMMIT);
|
||||
#else
|
||||
return base + offset;
|
||||
#endif
|
||||
}
|
||||
|
||||
void* Memory::Reserve(size_t offset, size_t size)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return Alloc(offset, size, MEM_RESERVE);
|
||||
#else
|
||||
return base + offset;
|
||||
#endif
|
||||
}
|
||||
|
||||
void* MmGetHostAddress(uint32_t ptr)
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#ifndef _WIN32
|
||||
#define MEM_COMMIT 0x00001000
|
||||
#define MEM_RESERVE 0x00002000
|
||||
#endif
|
||||
|
||||
class Memory
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -2,16 +2,92 @@
|
|||
#include "xam.h"
|
||||
#include "xdm.h"
|
||||
#include <hid/hid.h>
|
||||
#include <ui/window.h>
|
||||
#include <ui/game_window.h>
|
||||
#include <cpu/guest_thread.h>
|
||||
#include <ranges>
|
||||
#include <unordered_set>
|
||||
#include <CommCtrl.h>
|
||||
#include "xxHashMap.h"
|
||||
#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{};
|
||||
uint64_t areas{};
|
||||
std::vector<std::tuple<uint32_t, uint32_t>> notifications;
|
||||
|
||||
XamListener(const XamListener&) = delete;
|
||||
XamListener& operator=(const XamListener&) = delete;
|
||||
|
||||
XamListener();
|
||||
~XamListener();
|
||||
};
|
||||
|
||||
struct XamEnumeratorBase : KernelObject
|
||||
{
|
||||
virtual uint32_t Next(void* buffer)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename TIterator = std::vector<XHOSTCONTENT_DATA>::iterator>
|
||||
struct XamEnumerator : XamEnumeratorBase
|
||||
{
|
||||
uint32_t fetch;
|
||||
size_t size;
|
||||
TIterator position;
|
||||
TIterator begin;
|
||||
TIterator end;
|
||||
|
||||
XamEnumerator() = default;
|
||||
XamEnumerator(uint32_t fetch, size_t size, TIterator begin, TIterator end) : fetch(fetch), size(size), position(begin), begin(begin), end(end)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
uint32_t Next(void* buffer) override
|
||||
{
|
||||
if (position == end)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (buffer == nullptr)
|
||||
{
|
||||
for (size_t i = 0; i < fetch; i++)
|
||||
{
|
||||
if (position == end)
|
||||
{
|
||||
return i == 0 ? -1 : i;
|
||||
}
|
||||
|
||||
++position;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < fetch; i++)
|
||||
{
|
||||
if (position == end)
|
||||
{
|
||||
return i == 0 ? -1 : i;
|
||||
}
|
||||
|
||||
memcpy(buffer, &*position, size);
|
||||
|
||||
++position;
|
||||
buffer = (void*)((size_t)buffer + size);
|
||||
}
|
||||
|
||||
return fetch;
|
||||
}
|
||||
};
|
||||
|
||||
std::array<xxHashMap<XHOSTCONTENT_DATA>, 3> gContentRegistry{};
|
||||
std::unordered_set<XamListener*> gListeners{};
|
||||
|
@ -42,7 +118,7 @@ XamListener::~XamListener()
|
|||
gListeners.erase(this);
|
||||
}
|
||||
|
||||
XCONTENT_DATA XamMakeContent(DWORD type, const std::string_view& name)
|
||||
XCONTENT_DATA XamMakeContent(uint32_t type, const std::string_view& name)
|
||||
{
|
||||
XCONTENT_DATA data{ 1, type };
|
||||
|
||||
|
@ -58,7 +134,7 @@ void XamRegisterContent(const XCONTENT_DATA& data, const std::string_view& root)
|
|||
gContentRegistry[idx].emplace(StringHash(data.szFileName), XHOSTCONTENT_DATA{ data }).first->second.szRoot = root;
|
||||
}
|
||||
|
||||
void XamRegisterContent(DWORD type, const std::string_view name, const std::string_view& root)
|
||||
void XamRegisterContent(uint32_t type, const std::string_view name, const std::string_view& root)
|
||||
{
|
||||
XCONTENT_DATA data{ 1, type, {}, "" };
|
||||
|
||||
|
@ -67,17 +143,16 @@ void XamRegisterContent(DWORD type, const std::string_view name, const std::stri
|
|||
XamRegisterContent(data, root);
|
||||
}
|
||||
|
||||
SWA_API DWORD XamNotifyCreateListener(uint64_t qwAreas)
|
||||
SWA_API uint32_t XamNotifyCreateListener(uint64_t qwAreas)
|
||||
{
|
||||
int handle;
|
||||
auto* listener = ObCreateObject<XamListener>(handle);
|
||||
auto* listener = CreateKernelObject<XamListener>();
|
||||
|
||||
listener->areas = qwAreas;
|
||||
|
||||
return GUEST_HANDLE(handle);
|
||||
return GetKernelHandle(listener);
|
||||
}
|
||||
|
||||
SWA_API void XamNotifyEnqueueEvent(DWORD dwId, DWORD dwParam)
|
||||
SWA_API void XamNotifyEnqueueEvent(uint32_t dwId, uint32_t dwParam)
|
||||
{
|
||||
for (const auto& listener : gListeners)
|
||||
{
|
||||
|
@ -88,9 +163,9 @@ SWA_API void XamNotifyEnqueueEvent(DWORD dwId, DWORD dwParam)
|
|||
}
|
||||
}
|
||||
|
||||
SWA_API bool XNotifyGetNext(DWORD hNotification, DWORD dwMsgFilter, XDWORD* pdwId, XDWORD* pParam)
|
||||
SWA_API bool XNotifyGetNext(uint32_t hNotification, uint32_t dwMsgFilter, be<uint32_t>* pdwId, be<uint32_t>* pParam)
|
||||
{
|
||||
auto& listener = *ObTryQueryObject<XamListener>(HOST_HANDLE(hNotification));
|
||||
auto& listener = *GetKernelObject<XamListener>(hNotification);
|
||||
|
||||
if (dwMsgFilter)
|
||||
{
|
||||
|
@ -129,9 +204,12 @@ SWA_API bool XNotifyGetNext(DWORD hNotification, DWORD dwMsgFilter, XDWORD* pdwI
|
|||
return false;
|
||||
}
|
||||
|
||||
SWA_API uint32_t XamShowMessageBoxUI(DWORD dwUserIndex, XWORD* wszTitle, XWORD* wszText, DWORD cButtons,
|
||||
xpointer<XWORD>* pwszButtons, DWORD dwFocusButton, DWORD dwFlags, XLPDWORD pResult, XXOVERLAPPED* pOverlapped)
|
||||
SWA_API 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{};
|
||||
|
||||
#ifdef _WIN32
|
||||
std::vector<std::wstring> texts{};
|
||||
std::vector<TASKDIALOG_BUTTON> buttons{};
|
||||
|
||||
|
@ -154,20 +232,19 @@ SWA_API uint32_t XamShowMessageBoxUI(DWORD dwUserIndex, XWORD* wszTitle, XWORD*
|
|||
|
||||
TASKDIALOGCONFIG config{};
|
||||
config.cbSize = sizeof(config);
|
||||
// config.hwndParent = Window::s_hWnd;
|
||||
config.pszWindowTitle = texts[0].c_str();
|
||||
config.pszContent = texts[1].c_str();
|
||||
config.cButtons = cButtons;
|
||||
config.pButtons = buttons.data();
|
||||
|
||||
int button{};
|
||||
TaskDialogIndirect(&config, &button, nullptr, nullptr);
|
||||
#endif
|
||||
|
||||
*pResult = button;
|
||||
|
||||
if (pOverlapped)
|
||||
{
|
||||
pOverlapped->dwCompletionContext = GetCurrentThreadId();
|
||||
pOverlapped->dwCompletionContext = GuestThread::GetCurrentThreadId();
|
||||
pOverlapped->Error = 0;
|
||||
pOverlapped->Length = -1;
|
||||
}
|
||||
|
@ -177,8 +254,8 @@ SWA_API uint32_t XamShowMessageBoxUI(DWORD dwUserIndex, XWORD* wszTitle, XWORD*
|
|||
return 0;
|
||||
}
|
||||
|
||||
SWA_API uint32_t XamContentCreateEnumerator(DWORD dwUserIndex, DWORD DeviceID, DWORD dwContentType,
|
||||
DWORD dwContentFlags, DWORD cItem, XLPDWORD pcbBuffer, XLPDWORD phEnum)
|
||||
SWA_API uint32_t XamContentCreateEnumerator(uint32_t dwUserIndex, uint32_t DeviceID, uint32_t dwContentType,
|
||||
uint32_t dwContentFlags, uint32_t cItem, be<uint32_t>* pcbBuffer, be<uint32_t>* phEnum)
|
||||
{
|
||||
if (dwUserIndex != 0)
|
||||
{
|
||||
|
@ -188,19 +265,19 @@ SWA_API uint32_t XamContentCreateEnumerator(DWORD dwUserIndex, DWORD DeviceID, D
|
|||
|
||||
const auto& registry = gContentRegistry[dwContentType - 1];
|
||||
const auto& values = registry | std::views::values;
|
||||
const int handle = ObInsertObject(new XamEnumerator(cItem, sizeof(_XCONTENT_DATA), values.begin(), values.end()));
|
||||
auto* enumerator = CreateKernelObject<XamEnumerator<decltype(values.begin())>>(cItem, sizeof(_XCONTENT_DATA), values.begin(), values.end());
|
||||
|
||||
if (pcbBuffer)
|
||||
*pcbBuffer = sizeof(_XCONTENT_DATA) * cItem;
|
||||
|
||||
*phEnum = GUEST_HANDLE(handle);
|
||||
*phEnum = GetKernelHandle(enumerator);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SWA_API uint32_t XamEnumerate(uint32_t hEnum, DWORD dwFlags, PVOID pvBuffer, DWORD cbBuffer, XLPDWORD pcItemsReturned, XXOVERLAPPED* pOverlapped)
|
||||
SWA_API uint32_t XamEnumerate(uint32_t hEnum, uint32_t dwFlags, void* pvBuffer, uint32_t cbBuffer, be<uint32_t>* pcItemsReturned, XXOVERLAPPED* pOverlapped)
|
||||
{
|
||||
auto* enumerator = ObTryQueryObject<XamEnumeratorBase>(HOST_HANDLE(hEnum));
|
||||
auto* enumerator = GetKernelObject<XamEnumeratorBase>(hEnum);
|
||||
const auto count = enumerator->Next(pvBuffer);
|
||||
|
||||
if (count == -1)
|
||||
|
@ -212,9 +289,9 @@ SWA_API uint32_t XamEnumerate(uint32_t hEnum, DWORD dwFlags, PVOID pvBuffer, DWO
|
|||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
SWA_API uint32_t XamContentCreateEx(DWORD dwUserIndex, LPCSTR szRootName, const XCONTENT_DATA* pContentData,
|
||||
DWORD dwContentFlags, XLPDWORD pdwDisposition, XLPDWORD pdwLicenseMask,
|
||||
DWORD dwFileCacheSize, uint64_t uliContentSize, PXXOVERLAPPED pOverlapped)
|
||||
SWA_API uint32_t XamContentCreateEx(uint32_t dwUserIndex, const char* szRootName, const XCONTENT_DATA* pContentData,
|
||||
uint32_t dwContentFlags, be<uint32_t>* pdwDisposition, be<uint32_t>* pdwLicenseMask,
|
||||
uint32_t dwFileCacheSize, uint64_t uliContentSize, PXXOVERLAPPED pOverlapped)
|
||||
{
|
||||
const auto& registry = gContentRegistry[pContentData->dwContentType - 1];
|
||||
const auto exists = registry.contains(StringHash(pContentData->szFileName));
|
||||
|
@ -231,19 +308,23 @@ SWA_API uint32_t XamContentCreateEx(DWORD dwUserIndex, LPCSTR szRootName, const
|
|||
|
||||
if (pContentData->dwContentType == XCONTENTTYPE_SAVEDATA)
|
||||
{
|
||||
root = GetSavePath().string();
|
||||
std::u8string savePathU8 = GetSavePath().u8string();
|
||||
root = (const char *)(savePathU8.c_str());
|
||||
}
|
||||
else if (pContentData->dwContentType == XCONTENTTYPE_DLC)
|
||||
{
|
||||
root = ".\\dlc";
|
||||
root = GAME_INSTALL_DIRECTORY "/dlc";
|
||||
}
|
||||
else
|
||||
{
|
||||
root = ".";
|
||||
root = GAME_INSTALL_DIRECTORY;
|
||||
}
|
||||
|
||||
XamRegisterContent(*pContentData, root);
|
||||
CreateDirectoryA(root.c_str(), nullptr);
|
||||
|
||||
std::error_code ec;
|
||||
std::filesystem::create_directory(std::u8string_view((const char8_t*)(root.c_str())), ec);
|
||||
|
||||
XamRootCreate(szRootName, root);
|
||||
}
|
||||
else
|
||||
|
@ -277,13 +358,13 @@ SWA_API uint32_t XamContentCreateEx(DWORD dwUserIndex, LPCSTR szRootName, const
|
|||
return ERROR_PATH_NOT_FOUND;
|
||||
}
|
||||
|
||||
SWA_API uint32_t XamContentClose(LPCSTR szRootName, XXOVERLAPPED* pOverlapped)
|
||||
SWA_API uint32_t XamContentClose(const char* szRootName, XXOVERLAPPED* pOverlapped)
|
||||
{
|
||||
gRootMap.erase(StringHash(szRootName));
|
||||
return 0;
|
||||
}
|
||||
|
||||
SWA_API uint32_t XamContentGetDeviceData(DWORD DeviceID, XDEVICE_DATA* pDeviceData)
|
||||
SWA_API uint32_t XamContentGetDeviceData(uint32_t DeviceID, XDEVICE_DATA* pDeviceData)
|
||||
{
|
||||
pDeviceData->DeviceID = DeviceID;
|
||||
pDeviceData->DeviceType = XCONTENTDEVICETYPE_HDD;
|
||||
|
@ -320,73 +401,63 @@ SWA_API uint32_t XamInputGetCapabilities(uint32_t unk, uint32_t userIndex, uint3
|
|||
|
||||
SWA_API uint32_t XamInputGetState(uint32_t userIndex, uint32_t flags, XAMINPUT_STATE* state)
|
||||
{
|
||||
memset(state, 0, sizeof(*state));
|
||||
|
||||
uint32_t result = hid::GetState(userIndex, state);
|
||||
|
||||
if (result == ERROR_SUCCESS)
|
||||
if (GameWindow::s_isFocused)
|
||||
{
|
||||
ByteSwapInplace(state->dwPacketNumber);
|
||||
ByteSwapInplace(state->Gamepad.wButtons);
|
||||
ByteSwapInplace(state->Gamepad.sThumbLX);
|
||||
ByteSwapInplace(state->Gamepad.sThumbLY);
|
||||
ByteSwapInplace(state->Gamepad.sThumbRX);
|
||||
ByteSwapInplace(state->Gamepad.sThumbRY);
|
||||
}
|
||||
else if (userIndex == 0)
|
||||
{
|
||||
if (!Window::s_isFocused)
|
||||
return ERROR_SUCCESS;
|
||||
auto keyboardState = SDL_GetKeyboardState(NULL);
|
||||
|
||||
memset(state, 0, sizeof(*state));
|
||||
if (GetAsyncKeyState('W') & 0x8000)
|
||||
if (keyboardState[SDL_SCANCODE_W])
|
||||
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_Y;
|
||||
if (GetAsyncKeyState('A') & 0x8000)
|
||||
if (keyboardState[SDL_SCANCODE_A])
|
||||
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_X;
|
||||
if (GetAsyncKeyState('S') & 0x8000)
|
||||
if (keyboardState[SDL_SCANCODE_S])
|
||||
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_A;
|
||||
if (GetAsyncKeyState('D') & 0x8000)
|
||||
if (keyboardState[SDL_SCANCODE_D])
|
||||
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_B;
|
||||
if (GetAsyncKeyState('Q') & 0x8000)
|
||||
|
||||
if (keyboardState[SDL_SCANCODE_Q])
|
||||
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_LEFT_SHOULDER;
|
||||
if (GetAsyncKeyState('E') & 0x8000)
|
||||
if (keyboardState[SDL_SCANCODE_E])
|
||||
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_RIGHT_SHOULDER;
|
||||
if (GetAsyncKeyState('1') & 0x8000)
|
||||
if (keyboardState[SDL_SCANCODE_1])
|
||||
state->Gamepad.bLeftTrigger = 0xFF;
|
||||
if (GetAsyncKeyState('3') & 0x8000)
|
||||
if (keyboardState[SDL_SCANCODE_3])
|
||||
state->Gamepad.bRightTrigger = 0xFF;
|
||||
|
||||
if (GetAsyncKeyState('I') & 0x8000)
|
||||
if (keyboardState[SDL_SCANCODE_I])
|
||||
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_DPAD_UP;
|
||||
if (GetAsyncKeyState('J') & 0x8000)
|
||||
if (keyboardState[SDL_SCANCODE_J])
|
||||
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_DPAD_LEFT;
|
||||
if (GetAsyncKeyState('K') & 0x8000)
|
||||
if (keyboardState[SDL_SCANCODE_K])
|
||||
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_DPAD_DOWN;
|
||||
if (GetAsyncKeyState('L') & 0x8000)
|
||||
if (keyboardState[SDL_SCANCODE_L])
|
||||
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_DPAD_RIGHT;
|
||||
|
||||
if (GetAsyncKeyState(VK_UP) & 0x8000)
|
||||
if (keyboardState[SDL_SCANCODE_UP])
|
||||
state->Gamepad.sThumbLY = 32767;
|
||||
if (GetAsyncKeyState(VK_LEFT) & 0x8000)
|
||||
if (keyboardState[SDL_SCANCODE_LEFT])
|
||||
state->Gamepad.sThumbLX = -32768;
|
||||
if (GetAsyncKeyState(VK_DOWN) & 0x8000)
|
||||
if (keyboardState[SDL_SCANCODE_DOWN])
|
||||
state->Gamepad.sThumbLY = -32768;
|
||||
if (GetAsyncKeyState(VK_RIGHT) & 0x8000)
|
||||
if (keyboardState[SDL_SCANCODE_RIGHT])
|
||||
state->Gamepad.sThumbLX = 32767;
|
||||
|
||||
if (GetAsyncKeyState(VK_RETURN) & 0x8000)
|
||||
if (keyboardState[SDL_SCANCODE_RETURN])
|
||||
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_START;
|
||||
if (GetAsyncKeyState(VK_BACK) & 0x8000)
|
||||
if (keyboardState[SDL_SCANCODE_BACKSPACE])
|
||||
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_BACK;
|
||||
|
||||
ByteSwapInplace(state->Gamepad.wButtons);
|
||||
ByteSwapInplace(state->Gamepad.sThumbLX);
|
||||
ByteSwapInplace(state->Gamepad.sThumbLY);
|
||||
ByteSwapInplace(state->Gamepad.sThumbRX);
|
||||
ByteSwapInplace(state->Gamepad.sThumbRY);
|
||||
|
||||
result = ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
return result;
|
||||
ByteSwapInplace(state->Gamepad.wButtons);
|
||||
ByteSwapInplace(state->Gamepad.sThumbLX);
|
||||
ByteSwapInplace(state->Gamepad.sThumbLY);
|
||||
ByteSwapInplace(state->Gamepad.sThumbRX);
|
||||
ByteSwapInplace(state->Gamepad.sThumbRY);
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
SWA_API uint32_t XamInputSetState(uint32_t userIndex, uint32_t flags, XAMINPUT_VIBRATION* vibration)
|
||||
|
|
|
@ -1,108 +1,32 @@
|
|||
#pragma once
|
||||
#include <xbox.h>
|
||||
|
||||
#define MSGID(Area, Number) (DWORD)((WORD)(Area) << 16 | (WORD)(Number))
|
||||
#define MSGID(Area, Number) (uint32_t)((uint16_t)(Area) << 16 | (uint16_t)(Number))
|
||||
#define MSG_AREA(msgid) (((msgid) >> 16) & 0xFFFF)
|
||||
#define MSG_NUMBER(msgid) ((msgid) & 0xFFFF)
|
||||
|
||||
struct XamListener
|
||||
{
|
||||
uint32_t id{};
|
||||
uint64_t areas{};
|
||||
std::vector<std::tuple<DWORD, DWORD>> notifications;
|
||||
|
||||
XamListener(const XamListener&) = delete;
|
||||
XamListener& operator=(const XamListener&) = delete;
|
||||
|
||||
XamListener();
|
||||
~XamListener();
|
||||
};
|
||||
|
||||
class XamEnumeratorBase
|
||||
{
|
||||
public:
|
||||
virtual ~XamEnumeratorBase() = default;
|
||||
virtual uint32_t Next(void* buffer)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename TIterator = std::vector<XHOSTCONTENT_DATA>::iterator>
|
||||
class XamEnumerator : public XamEnumeratorBase
|
||||
{
|
||||
public:
|
||||
uint32_t fetch;
|
||||
size_t size;
|
||||
TIterator position;
|
||||
TIterator begin;
|
||||
TIterator end;
|
||||
|
||||
XamEnumerator() = default;
|
||||
XamEnumerator(uint32_t fetch, size_t size, TIterator begin, TIterator end) : fetch(fetch), size(size), position(begin), begin(begin), end(end)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
uint32_t Next(void* buffer) override
|
||||
{
|
||||
if (position == end)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (buffer == nullptr)
|
||||
{
|
||||
for (size_t i = 0; i < fetch; i++)
|
||||
{
|
||||
if (position == end)
|
||||
{
|
||||
return i == 0 ? -1 : i;
|
||||
}
|
||||
|
||||
++position;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < fetch; i++)
|
||||
{
|
||||
if (position == end)
|
||||
{
|
||||
return i == 0 ? -1 : i;
|
||||
}
|
||||
|
||||
memcpy(buffer, &*position, size);
|
||||
|
||||
++position;
|
||||
buffer = (void*)((size_t)buffer + size);
|
||||
}
|
||||
|
||||
return fetch;
|
||||
}
|
||||
};
|
||||
|
||||
XCONTENT_DATA XamMakeContent(DWORD type, const std::string_view& name);
|
||||
XCONTENT_DATA XamMakeContent(uint32_t type, const std::string_view& name);
|
||||
void XamRegisterContent(const XCONTENT_DATA& data, const std::string_view& root);
|
||||
|
||||
std::string_view XamGetRootPath(const std::string_view& root);
|
||||
void XamRootCreate(const std::string_view& root, const std::string_view& path);
|
||||
|
||||
SWA_API DWORD XamNotifyCreateListener(uint64_t qwAreas);
|
||||
SWA_API void XamNotifyEnqueueEvent(DWORD dwId, DWORD dwParam); // i made it the fuck up
|
||||
SWA_API bool XNotifyGetNext(DWORD hNotification, DWORD dwMsgFilter, XDWORD* pdwId, XDWORD* pParam);
|
||||
SWA_API uint32_t XamNotifyCreateListener(uint64_t qwAreas);
|
||||
SWA_API void XamNotifyEnqueueEvent(uint32_t dwId, uint32_t dwParam); // i made it the fuck up
|
||||
SWA_API bool XNotifyGetNext(uint32_t hNotification, uint32_t dwMsgFilter, be<uint32_t>* pdwId, be<uint32_t>* pParam);
|
||||
|
||||
SWA_API uint32_t XamShowMessageBoxUI(DWORD dwUserIndex, XWORD* wszTitle, XWORD* wszText, DWORD cButtons,
|
||||
xpointer<XWORD>* pwszButtons, DWORD dwFocusButton, DWORD dwFlags, XLPDWORD pResult, XXOVERLAPPED* pOverlapped);
|
||||
SWA_API 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);
|
||||
|
||||
SWA_API uint32_t XamContentCreateEnumerator(DWORD dwUserIndex, DWORD DeviceID, DWORD dwContentType,
|
||||
DWORD dwContentFlags, DWORD cItem, XLPDWORD pcbBuffer, XLPDWORD phEnum);
|
||||
SWA_API uint32_t XamEnumerate(uint32_t hEnum, DWORD dwFlags, PVOID pvBuffer, DWORD cbBuffer, XLPDWORD pcItemsReturned, XXOVERLAPPED* pOverlapped);
|
||||
SWA_API uint32_t XamContentCreateEnumerator(uint32_t dwUserIndex, uint32_t DeviceID, uint32_t dwContentType,
|
||||
uint32_t dwContentFlags, uint32_t cItem, be<uint32_t>* pcbBuffer, be<uint32_t>* phEnum);
|
||||
SWA_API uint32_t XamEnumerate(uint32_t hEnum, uint32_t dwFlags, void* pvBuffer, uint32_t cbBuffer, be<uint32_t>* pcItemsReturned, XXOVERLAPPED* pOverlapped);
|
||||
|
||||
SWA_API uint32_t XamContentCreateEx(DWORD dwUserIndex, LPCSTR szRootName, const XCONTENT_DATA* pContentData,
|
||||
DWORD dwContentFlags, XLPDWORD pdwDisposition, XLPDWORD pdwLicenseMask,
|
||||
DWORD dwFileCacheSize, uint64_t uliContentSize, PXXOVERLAPPED pOverlapped);
|
||||
SWA_API uint32_t XamContentGetDeviceData(DWORD DeviceID, XDEVICE_DATA* pDeviceData);
|
||||
SWA_API uint32_t XamContentClose(LPCSTR szRootName, XXOVERLAPPED* pOverlapped);
|
||||
SWA_API uint32_t XamContentCreateEx(uint32_t dwUserIndex, const char* szRootName, const XCONTENT_DATA* pContentData,
|
||||
uint32_t dwContentFlags, be<uint32_t>* pdwDisposition, be<uint32_t>* pdwLicenseMask,
|
||||
uint32_t dwFileCacheSize, uint64_t uliContentSize, PXXOVERLAPPED pOverlapped);
|
||||
SWA_API uint32_t XamContentGetDeviceData(uint32_t DeviceID, XDEVICE_DATA* pDeviceData);
|
||||
SWA_API uint32_t XamContentClose(const char* szRootName, XXOVERLAPPED* pOverlapped);
|
||||
|
||||
SWA_API uint32_t XamInputGetCapabilities(uint32_t unk, uint32_t userIndex, uint32_t flags, XAMINPUT_CAPABILITIES* caps);
|
||||
SWA_API uint32_t XamInputGetState(uint32_t userIndex, uint32_t flags, XAMINPUT_STATE* state);
|
||||
|
|
|
@ -2,47 +2,36 @@
|
|||
#include "xdm.h"
|
||||
#include "freelist.h"
|
||||
|
||||
FreeList<std::tuple<std::unique_ptr<char>, TypeDestructor_t>> gKernelObjects;
|
||||
Mutex gKernelLock;
|
||||
Mutex g_kernelLock;
|
||||
|
||||
void* ObQueryObject(size_t handle)
|
||||
void DestroyKernelObject(KernelObject* obj)
|
||||
{
|
||||
std::lock_guard guard{ gKernelLock };
|
||||
|
||||
if (handle >= gKernelObjects.items.size())
|
||||
return nullptr;
|
||||
|
||||
return std::get<0>(gKernelObjects[handle]).get();
|
||||
obj->~KernelObject();
|
||||
g_userHeap.Free(obj);
|
||||
}
|
||||
|
||||
uint32_t ObInsertObject(void* object, TypeDestructor_t destructor)
|
||||
uint32_t GetKernelHandle(KernelObject* obj)
|
||||
{
|
||||
std::lock_guard guard{ gKernelLock };
|
||||
|
||||
const auto handle = gKernelObjects.Alloc();
|
||||
|
||||
auto& holder = gKernelObjects[handle];
|
||||
|
||||
std::get<0>(holder).reset(static_cast<char*>(object));
|
||||
std::get<1>(holder) = destructor;
|
||||
|
||||
return handle;
|
||||
assert(obj != GetInvalidKernelObject());
|
||||
return g_memory.MapVirtual(obj);
|
||||
}
|
||||
|
||||
void ObCloseHandle(uint32_t handle)
|
||||
void DestroyKernelObject(uint32_t handle)
|
||||
{
|
||||
std::lock_guard guard{ gKernelLock };
|
||||
|
||||
auto& obj = gKernelObjects[handle];
|
||||
|
||||
if (std::get<1>(obj)(std::get<0>(obj).get()))
|
||||
{
|
||||
std::get<0>(obj).reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
std::get<0>(obj).release();
|
||||
}
|
||||
|
||||
gKernelObjects.Free(handle);
|
||||
DestroyKernelObject(GetKernelObject(handle));
|
||||
}
|
||||
|
||||
bool IsKernelObject(uint32_t handle)
|
||||
{
|
||||
return (handle & 0x80000000) != 0;
|
||||
}
|
||||
|
||||
bool IsKernelObject(void* obj)
|
||||
{
|
||||
return IsKernelObject(g_memory.MapVirtual(obj));
|
||||
}
|
||||
|
||||
bool IsInvalidKernelObject(void* obj)
|
||||
{
|
||||
return obj == GetInvalidKernelObject();
|
||||
}
|
||||
|
|
|
@ -1,56 +1,153 @@
|
|||
#pragma once
|
||||
#define DUMMY_HANDLE (DWORD)('HAND')
|
||||
#define OBJECT_SIGNATURE (DWORD)'XBOX'
|
||||
|
||||
extern Mutex gKernelLock;
|
||||
#include "heap.h"
|
||||
#include "memory.h"
|
||||
|
||||
void* ObQueryObject(size_t handle);
|
||||
uint32_t ObInsertObject(void* object, TypeDestructor_t destructor);
|
||||
void ObCloseHandle(uint32_t handle);
|
||||
#define OBJECT_SIGNATURE (uint32_t)'XBOX'
|
||||
#define GUEST_INVALID_HANDLE_VALUE 0xFFFFFFFF
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
#define S_OK 0x00000000
|
||||
#define FALSE 0x00000000
|
||||
#define TRUE 0x00000001
|
||||
#define STATUS_SUCCESS 0x00000000
|
||||
#define STATUS_WAIT_0 0x00000000
|
||||
#define STATUS_USER_APC 0x000000C0
|
||||
#define STATUS_TIMEOUT 0x00000102
|
||||
#define STATUS_FAIL_CHECK 0xC0000229
|
||||
#define INFINITE 0xFFFFFFFF
|
||||
#define FILE_ATTRIBUTE_DIRECTORY 0x00000010
|
||||
#define FILE_ATTRIBUTE_NORMAL 0x00000080
|
||||
#define GENERIC_READ 0x80000000
|
||||
#define GENERIC_WRITE 0x40000000
|
||||
#define FILE_READ_DATA 0x0001
|
||||
#define FILE_SHARE_READ 0x00000001
|
||||
#define FILE_SHARE_WRITE 0x00000002
|
||||
#define CREATE_NEW 1
|
||||
#define CREATE_ALWAYS 2
|
||||
#define OPEN_EXISTING 3
|
||||
#define INVALID_FILE_SIZE 0xFFFFFFFF
|
||||
#define INVALID_SET_FILE_POINTER 0xFFFFFFFF
|
||||
#define INVALID_FILE_ATTRIBUTES 0xFFFFFFFF
|
||||
#define FILE_BEGIN 0
|
||||
#define FILE_CURRENT 1
|
||||
#define FILE_END 2
|
||||
#define ERROR_NO_MORE_FILES 0x12
|
||||
#define ERROR_NO_SUCH_USER 0x525
|
||||
#define ERROR_SUCCESS 0x0
|
||||
#define ERROR_PATH_NOT_FOUND 0x3
|
||||
#define ERROR_BAD_ARGUMENTS 0xA0
|
||||
#define ERROR_DEVICE_NOT_CONNECTED 0x48F
|
||||
#define PAGE_READWRITE 0x04
|
||||
|
||||
typedef union _LARGE_INTEGER {
|
||||
struct {
|
||||
uint32_t LowPart;
|
||||
int32_t HighPart;
|
||||
};
|
||||
struct {
|
||||
uint32_t LowPart;
|
||||
int32_t HighPart;
|
||||
} u;
|
||||
int64_t QuadPart;
|
||||
} LARGE_INTEGER;
|
||||
|
||||
static_assert(sizeof(LARGE_INTEGER) == 8);
|
||||
|
||||
typedef struct _FILETIME
|
||||
{
|
||||
uint32_t dwLowDateTime;
|
||||
uint32_t dwHighDateTime;
|
||||
} FILETIME;
|
||||
|
||||
static_assert(sizeof(FILETIME) == 8);
|
||||
|
||||
typedef struct _WIN32_FIND_DATAA
|
||||
{
|
||||
uint32_t dwFileAttributes;
|
||||
FILETIME ftCreationTime;
|
||||
FILETIME ftLastAccessTime;
|
||||
FILETIME ftLastWriteTime;
|
||||
uint32_t nFileSizeHigh;
|
||||
uint32_t nFileSizeLow;
|
||||
uint32_t dwReserved0;
|
||||
uint32_t dwReserved1;
|
||||
char cFileName[260];
|
||||
char cAlternateFileName[14];
|
||||
} WIN32_FIND_DATAA;
|
||||
|
||||
static_assert(sizeof(WIN32_FIND_DATAA) == 320);
|
||||
|
||||
#endif
|
||||
|
||||
struct KernelObject
|
||||
{
|
||||
virtual ~KernelObject()
|
||||
{
|
||||
}
|
||||
|
||||
virtual uint32_t Wait(uint32_t timeout)
|
||||
{
|
||||
assert(false && "Wait not implemented for this kernel object.");
|
||||
return STATUS_TIMEOUT;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename... Args>
|
||||
inline T* CreateKernelObject(Args&&... args)
|
||||
{
|
||||
static_assert(std::is_base_of_v<KernelObject, T>);
|
||||
return g_userHeap.AllocPhysical<T>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename T = KernelObject>
|
||||
inline T* GetKernelObject(uint32_t handle)
|
||||
{
|
||||
assert(handle != GUEST_INVALID_HANDLE_VALUE);
|
||||
return reinterpret_cast<T*>(g_memory.Translate(handle));
|
||||
}
|
||||
|
||||
uint32_t GetKernelHandle(KernelObject* obj);
|
||||
|
||||
void DestroyKernelObject(KernelObject* obj);
|
||||
void DestroyKernelObject(uint32_t handle);
|
||||
|
||||
bool IsKernelObject(uint32_t handle);
|
||||
bool IsKernelObject(void* obj);
|
||||
|
||||
bool IsInvalidKernelObject(void* obj);
|
||||
|
||||
template<typename T = void>
|
||||
inline T* GetInvalidKernelObject()
|
||||
{
|
||||
return reinterpret_cast<T*>(g_memory.Translate(GUEST_INVALID_HANDLE_VALUE));
|
||||
}
|
||||
|
||||
extern Mutex g_kernelLock;
|
||||
|
||||
template<typename T>
|
||||
T* ObQueryObject(XDISPATCHER_HEADER& header)
|
||||
inline T* QueryKernelObject(XDISPATCHER_HEADER& header)
|
||||
{
|
||||
std::lock_guard guard{ gKernelLock };
|
||||
std::lock_guard guard{ g_kernelLock };
|
||||
if (header.WaitListHead.Flink != OBJECT_SIGNATURE)
|
||||
{
|
||||
header.WaitListHead.Flink = OBJECT_SIGNATURE;
|
||||
auto* obj = new T(reinterpret_cast<typename T::guest_type*>(&header));
|
||||
header.WaitListHead.Blink = ObInsertObject(obj, DestroyObject<T>);
|
||||
auto* obj = CreateKernelObject<T>(reinterpret_cast<typename T::guest_type*>(&header));
|
||||
header.WaitListHead.Blink = g_memory.MapVirtual(obj);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
return static_cast<T*>(ObQueryObject(header.WaitListHead.Blink.get()));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
size_t ObInsertObject(T* object)
|
||||
{
|
||||
return ObInsertObject(object, DestroyObject<T>);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T* ObCreateObject(int& handle)
|
||||
{
|
||||
auto* obj = new T();
|
||||
handle = ::ObInsertObject(obj, DestroyObject<T>);
|
||||
|
||||
return obj;
|
||||
return static_cast<T*>(g_memory.Translate(header.WaitListHead.Blink.get()));
|
||||
}
|
||||
|
||||
// Get object without initialisation
|
||||
template<typename T>
|
||||
T* ObTryQueryObject(XDISPATCHER_HEADER& header)
|
||||
inline T* TryQueryKernelObject(XDISPATCHER_HEADER& header)
|
||||
{
|
||||
if (header.WaitListHead.Flink != OBJECT_SIGNATURE)
|
||||
return nullptr;
|
||||
|
||||
return static_cast<T*>(ObQueryObject(header.WaitListHead.Blink));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T* ObTryQueryObject(int handle)
|
||||
{
|
||||
return static_cast<T*>(ObQueryObject(handle));
|
||||
return static_cast<T*>(g_memory.Translate(header.WaitListHead.Blink.get()));
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
#include <user/config.h>
|
||||
#include <user/config_detail.h>
|
||||
|
||||
#define CONFIG_DEFINE_LOCALE(name) \
|
||||
CONFIG_LOCALE Config::g_##name##_locale =
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#include <user/config.h>
|
||||
#include <locale/locale.h>
|
||||
|
||||
std::unordered_map<std::string, std::unordered_map<ELanguage, std::string>> g_locale =
|
||||
|
|
|
@ -1,6 +1,14 @@
|
|||
#pragma once
|
||||
|
||||
#include <user/config.h>
|
||||
enum class ELanguage : uint32_t
|
||||
{
|
||||
English = 1,
|
||||
Japanese,
|
||||
German,
|
||||
French,
|
||||
Spanish,
|
||||
Italian
|
||||
};
|
||||
|
||||
inline std::string g_localeMissing = "<missing string>";
|
||||
|
||||
|
|
|
@ -50,15 +50,17 @@ void KiSystemStartup()
|
|||
{
|
||||
const auto gameContent = XamMakeContent(XCONTENTTYPE_RESERVED, "Game");
|
||||
const auto updateContent = XamMakeContent(XCONTENTTYPE_RESERVED, "Update");
|
||||
XamRegisterContent(gameContent, DirectoryExists(".\\game") ? ".\\game" : ".");
|
||||
XamRegisterContent(updateContent, ".\\update");
|
||||
XamRegisterContent(gameContent, GAME_INSTALL_DIRECTORY "/game");
|
||||
XamRegisterContent(updateContent, GAME_INSTALL_DIRECTORY "/update");
|
||||
|
||||
const auto savePath = GetSavePath();
|
||||
const auto saveName = "SYS-DATA";
|
||||
|
||||
// TODO: implement save slots?
|
||||
if (std::filesystem::exists(savePath / saveName))
|
||||
XamRegisterContent(XamMakeContent(XCONTENTTYPE_SAVEDATA, saveName), savePath.string());
|
||||
{
|
||||
std::u8string savePathU8 = savePath.u8string();
|
||||
XamRegisterContent(XamMakeContent(XCONTENTTYPE_SAVEDATA, saveName), (const char *)(savePathU8.c_str()));
|
||||
}
|
||||
|
||||
// Mount game
|
||||
XamContentCreateEx(0, "game", &gameContent, OPEN_EXISTING, nullptr, nullptr, 0, 0, nullptr);
|
||||
|
@ -67,33 +69,23 @@ void KiSystemStartup()
|
|||
// OS mounts game data to D:
|
||||
XamContentCreateEx(0, "D", &gameContent, OPEN_EXISTING, nullptr, nullptr, 0, 0, nullptr);
|
||||
|
||||
WIN32_FIND_DATAA fdata;
|
||||
const auto findHandle = FindFirstFileA(".\\dlc\\*.*", &fdata);
|
||||
if (findHandle != INVALID_HANDLE_VALUE)
|
||||
std::error_code ec;
|
||||
for (auto& file : std::filesystem::directory_iterator(GAME_INSTALL_DIRECTORY "/dlc", ec))
|
||||
{
|
||||
char strBuf[256];
|
||||
do
|
||||
if (file.is_directory())
|
||||
{
|
||||
if (strcmp(fdata.cFileName, ".") == 0 || strcmp(fdata.cFileName, "..") == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
{
|
||||
snprintf(strBuf, sizeof(strBuf), ".\\dlc\\%s", fdata.cFileName);
|
||||
XamRegisterContent(XamMakeContent(XCONTENTTYPE_DLC, fdata.cFileName), strBuf);
|
||||
}
|
||||
} while (FindNextFileA(findHandle, &fdata));
|
||||
FindClose(findHandle);
|
||||
std::u8string fileNameU8 = file.path().filename().u8string();
|
||||
std::u8string filePathU8 = file.path().u8string();
|
||||
XamRegisterContent(XamMakeContent(XCONTENTTYPE_DLC, (const char*)(fileNameU8.c_str())), (const char*)(filePathU8.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
XAudioInitializeSystem();
|
||||
}
|
||||
|
||||
uint32_t LdrLoadModule(const char* path)
|
||||
uint32_t LdrLoadModule(const std::filesystem::path &path)
|
||||
{
|
||||
auto loadResult = LoadFile(FileSystem::TransformPath(GAME_XEX_PATH));
|
||||
auto loadResult = LoadFile(path);
|
||||
if (loadResult.empty())
|
||||
{
|
||||
assert("Failed to load module" && false);
|
||||
|
@ -145,27 +137,33 @@ uint32_t LdrLoadModule(const char* path)
|
|||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
#ifdef _WIN32
|
||||
timeBeginPeriod(1);
|
||||
#endif
|
||||
|
||||
os::logger::Init();
|
||||
|
||||
bool forceInstaller = false;
|
||||
bool forceDLCInstaller = false;
|
||||
bool sdlVideoDefault = false;
|
||||
for (uint32_t i = 1; i < argc; i++)
|
||||
{
|
||||
forceInstaller = forceInstaller || (strcmp(argv[i], "--install") == 0);
|
||||
forceDLCInstaller = forceDLCInstaller || (strcmp(argv[i], "--install-dlc") == 0);
|
||||
sdlVideoDefault = sdlVideoDefault || (strcmp(argv[i], "--sdl-video-default") == 0);
|
||||
}
|
||||
|
||||
Config::Load();
|
||||
|
||||
HostStartup();
|
||||
|
||||
bool isGameInstalled = Installer::checkGameInstall(".");
|
||||
bool isGameInstalled = Installer::checkGameInstall(GAME_INSTALL_DIRECTORY);
|
||||
bool runInstallerWizard = forceInstaller || forceDLCInstaller || !isGameInstalled;
|
||||
if (runInstallerWizard)
|
||||
{
|
||||
Video::CreateHostDevice();
|
||||
Video::CreateHostDevice(sdlVideoDefault);
|
||||
|
||||
if (!InstallerWizard::Run(isGameInstalled && forceDLCInstaller))
|
||||
if (!InstallerWizard::Run(GAME_INSTALL_DIRECTORY, isGameInstalled && forceDLCInstaller))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
@ -175,14 +173,15 @@ int main(int argc, char *argv[])
|
|||
|
||||
KiSystemStartup();
|
||||
|
||||
uint32_t entry = LdrLoadModule(FileSystem::TransformPath(GAME_XEX_PATH));
|
||||
const char *modulePath = FileSystem::TransformPath(GAME_XEX_PATH);
|
||||
uint32_t entry = LdrLoadModule(std::u8string_view((const char8_t*)(modulePath)));
|
||||
|
||||
if (!runInstallerWizard)
|
||||
Video::CreateHostDevice();
|
||||
Video::CreateHostDevice(sdlVideoDefault);
|
||||
|
||||
Video::StartPipelinePrecompilation();
|
||||
|
||||
GuestThread::Start(entry);
|
||||
GuestThread::Start({ entry, 0, 0 });
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1,18 +1,23 @@
|
|||
#include "stdafx.h"
|
||||
#include <kernel/function.h>
|
||||
#include <kernel/xdm.h>
|
||||
|
||||
BOOL QueryPerformanceCounterImpl(LARGE_INTEGER* lpPerformanceCount)
|
||||
uint32_t QueryPerformanceCounterImpl(LARGE_INTEGER* lpPerformanceCount)
|
||||
{
|
||||
BOOL result = QueryPerformanceCounter(lpPerformanceCount);
|
||||
ByteSwapInplace(lpPerformanceCount->QuadPart);
|
||||
return result;
|
||||
lpPerformanceCount->QuadPart = ByteSwap(std::chrono::steady_clock::now().time_since_epoch().count());
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL QueryPerformanceFrequencyImpl(LARGE_INTEGER* lpFrequency)
|
||||
uint32_t QueryPerformanceFrequencyImpl(LARGE_INTEGER* lpFrequency)
|
||||
{
|
||||
BOOL result = QueryPerformanceFrequency(lpFrequency);
|
||||
ByteSwapInplace(lpFrequency->QuadPart);
|
||||
return result;
|
||||
constexpr auto Frequency = std::chrono::steady_clock::period::den / std::chrono::steady_clock::period::num;
|
||||
lpFrequency->QuadPart = ByteSwap(Frequency);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
uint32_t GetTickCountImpl()
|
||||
{
|
||||
return uint32_t(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count());
|
||||
}
|
||||
|
||||
void GlobalMemoryStatusImpl(XLPMEMORYSTATUS lpMemoryStatus)
|
||||
|
@ -38,11 +43,15 @@ GUEST_FUNCTION_HOOK(sub_831B5E00, memmove);
|
|||
GUEST_FUNCTION_HOOK(sub_831B0BA0, memset);
|
||||
GUEST_FUNCTION_HOOK(sub_831CCAA0, memset);
|
||||
|
||||
#ifdef _WIN32
|
||||
GUEST_FUNCTION_HOOK(sub_82BD4CA8, OutputDebugStringA);
|
||||
#else
|
||||
GUEST_FUNCTION_STUB(sub_82BD4CA8);
|
||||
#endif
|
||||
|
||||
GUEST_FUNCTION_HOOK(sub_82BD4AC8, QueryPerformanceCounterImpl);
|
||||
GUEST_FUNCTION_HOOK(sub_831CD040, QueryPerformanceFrequencyImpl);
|
||||
GUEST_FUNCTION_HOOK(sub_831CDAD0, GetTickCount);
|
||||
GUEST_FUNCTION_HOOK(sub_831CDAD0, GetTickCountImpl);
|
||||
|
||||
GUEST_FUNCTION_HOOK(sub_82BD4BC0, GlobalMemoryStatusImpl);
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
struct Mutex : CRITICAL_SECTION
|
||||
{
|
||||
Mutex()
|
||||
|
@ -21,3 +23,9 @@ struct Mutex : CRITICAL_SECTION
|
|||
LeaveCriticalSection(this);
|
||||
}
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
using Mutex = std::mutex;
|
||||
|
||||
#endif
|
||||
|
|
17
UnleashedRecomp/os/linux/logger_linux.cpp
Normal file
17
UnleashedRecomp/os/linux/logger_linux.cpp
Normal file
|
@ -0,0 +1,17 @@
|
|||
#include <os/logger_detail.h>
|
||||
|
||||
void os::logger::detail::Init()
|
||||
{
|
||||
}
|
||||
|
||||
void os::logger::detail::Log(const std::string_view str, detail::ELogType type, const char* func)
|
||||
{
|
||||
if (func)
|
||||
{
|
||||
fmt::println("[{}] {}", func, str);
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::println("{}", str);
|
||||
}
|
||||
}
|
7
UnleashedRecomp/os/linux/media_linux.cpp
Normal file
7
UnleashedRecomp/os/linux/media_linux.cpp
Normal file
|
@ -0,0 +1,7 @@
|
|||
#include <os/media_detail.h>
|
||||
|
||||
bool os::media::detail::IsExternalMediaPlaying()
|
||||
{
|
||||
// This functionality is not supported in Linux.
|
||||
return false;
|
||||
}
|
57
UnleashedRecomp/os/linux/process_linux.cpp
Normal file
57
UnleashedRecomp/os/linux/process_linux.cpp
Normal file
|
@ -0,0 +1,57 @@
|
|||
#include <os/process_detail.h>
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
std::filesystem::path os::process::detail::GetExecutablePath()
|
||||
{
|
||||
char exePath[PATH_MAX] = {};
|
||||
if (readlink("/proc/self/exe", exePath, PATH_MAX) > 0)
|
||||
{
|
||||
return std::filesystem::path(std::u8string_view((const char8_t*)(exePath)));
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::filesystem::path();
|
||||
}
|
||||
}
|
||||
|
||||
std::filesystem::path os::process::detail::GetWorkingDirectory()
|
||||
{
|
||||
char cwd[PATH_MAX] = {};
|
||||
char *res = getcwd(cwd, sizeof(cwd));
|
||||
if (res != nullptr)
|
||||
{
|
||||
return std::filesystem::path(std::u8string_view((const char8_t*)(cwd)));
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::filesystem::path();
|
||||
}
|
||||
}
|
||||
|
||||
bool os::process::detail::StartProcess(const std::filesystem::path path, const std::vector<std::string> args, std::filesystem::path work)
|
||||
{
|
||||
pid_t pid = fork();
|
||||
if (pid < 0)
|
||||
return false;
|
||||
|
||||
if (pid == 0)
|
||||
{
|
||||
setsid();
|
||||
|
||||
std::u8string workU8 = work.u8string();
|
||||
chdir((const char*)(workU8.c_str()));
|
||||
|
||||
std::u8string pathU8 = path.u8string();
|
||||
std::vector<char*> argStrs;
|
||||
argStrs.push_back((char*)(pathU8.c_str()));
|
||||
for (const std::string& arg : args)
|
||||
argStrs.push_back((char *)(arg.c_str()));
|
||||
|
||||
argStrs.push_back(nullptr);
|
||||
execvp((const char*)(pathU8.c_str()), argStrs.data());
|
||||
raise(SIGKILL);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
7
UnleashedRecomp/os/linux/version_linux.cpp
Normal file
7
UnleashedRecomp/os/linux/version_linux.cpp
Normal file
|
@ -0,0 +1,7 @@
|
|||
#include <os/version_detail.h>
|
||||
|
||||
os::version::detail::OSVersion os::version::detail::GetOSVersion()
|
||||
{
|
||||
assert(false && "Unimplemented.");
|
||||
return os::version::detail::OSVersion();
|
||||
}
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
std::filesystem::path os::process::detail::GetExecutablePath()
|
||||
{
|
||||
char exePath[MAX_PATH];
|
||||
WCHAR exePath[MAX_PATH];
|
||||
|
||||
if (!GetModuleFileNameA(nullptr, exePath, MAX_PATH))
|
||||
if (!GetModuleFileNameW(nullptr, exePath, MAX_PATH))
|
||||
return std::filesystem::path();
|
||||
|
||||
return std::filesystem::path(exePath);
|
||||
|
@ -12,9 +12,9 @@ std::filesystem::path os::process::detail::GetExecutablePath()
|
|||
|
||||
std::filesystem::path os::process::detail::GetWorkingDirectory()
|
||||
{
|
||||
char workPath[MAX_PATH];
|
||||
WCHAR workPath[MAX_PATH];
|
||||
|
||||
if (!GetCurrentDirectoryA(MAX_PATH, workPath))
|
||||
if (!GetCurrentDirectoryW(MAX_PATH, workPath))
|
||||
return std::filesystem::path();
|
||||
|
||||
return std::filesystem::path(workPath);
|
||||
|
@ -28,14 +28,17 @@ bool os::process::detail::StartProcess(const std::filesystem::path path, const s
|
|||
if (work.empty())
|
||||
work = path.parent_path();
|
||||
|
||||
auto cli = path.string();
|
||||
auto cli = path.wstring();
|
||||
|
||||
// NOTE: We assume the CLI arguments only contain ASCII characters.
|
||||
for (auto& arg : args)
|
||||
cli += " " + arg;
|
||||
cli += L" " + std::wstring(arg.begin(), arg.end());
|
||||
|
||||
STARTUPINFOA startInfo{ sizeof(STARTUPINFOA) };
|
||||
STARTUPINFOW startInfo{ sizeof(STARTUPINFOW) };
|
||||
PROCESS_INFORMATION procInfo{};
|
||||
|
||||
if (!CreateProcessA(path.string().c_str(), cli.data(), nullptr, nullptr, false, 0, nullptr, work.string().c_str(), &startInfo, &procInfo))
|
||||
std::wstring pathW = path.wstring();
|
||||
std::wstring workW = work.wstring();
|
||||
if (!CreateProcessW(pathW.c_str(), cli.data(), nullptr, nullptr, false, 0, nullptr, workW.c_str(), &startInfo, &procInfo))
|
||||
return false;
|
||||
|
||||
CloseHandle(procInfo.hProcess);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include <cpu/guest_code.h>
|
||||
#include <api/SWA.h>
|
||||
#include <ui/window.h>
|
||||
#include <ui/game_window.h>
|
||||
#include <user/config.h>
|
||||
|
||||
constexpr float m_baseAspectRatio = 16.0f / 9.0f;
|
||||
|
@ -8,7 +8,7 @@ constexpr float m_baseAspectRatio = 16.0f / 9.0f;
|
|||
bool CameraAspectRatioMidAsmHook(PPCRegister& r31)
|
||||
{
|
||||
auto pCamera = (SWA::CCamera*)g_memory.Translate(r31.u32);
|
||||
auto newAspectRatio = (float)Window::s_width / (float)Window::s_height;
|
||||
auto newAspectRatio = (float)GameWindow::s_width / (float)GameWindow::s_height;
|
||||
|
||||
// Dynamically adjust horizontal aspect ratio to window dimensions.
|
||||
pCamera->m_HorzAspectRatio = newAspectRatio;
|
||||
|
@ -27,7 +27,7 @@ bool CameraBoostAspectRatioMidAsmHook(PPCRegister& r31, PPCRegister& f0, PPCRegi
|
|||
{
|
||||
auto pCamera = (SWA::CCamera*)g_memory.Translate(r31.u32);
|
||||
|
||||
if (Window::s_width < Window::s_height)
|
||||
if (GameWindow::s_width < GameWindow::s_height)
|
||||
{
|
||||
pCamera->m_VertFieldOfView = pCamera->m_HorzFieldOfView + f10.f64;
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
#include <cpu/guest_code.h>
|
||||
#include <api/SWA.h>
|
||||
#include <ui/window.h>
|
||||
#include <ui/game_window.h>
|
||||
#include <user/config.h>
|
||||
#include <app.h>
|
||||
|
||||
float m_lastLoadingFrameDelta = 0.0f;
|
||||
std::chrono::steady_clock::time_point m_lastLoadingFrameTime;
|
||||
std::chrono::high_resolution_clock::time_point m_lastLoadingFrameTime;
|
||||
|
||||
void DownForceDeltaTimeFixMidAsmHook(PPCRegister& f0)
|
||||
{
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include <cpu/guest_code.h>
|
||||
#include <api/SWA.h>
|
||||
#include <ui/window.h>
|
||||
#include <ui/game_window.h>
|
||||
#include <user/achievement_data.h>
|
||||
#include <user/config.h>
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include <cpu/guest_code.h>
|
||||
#include <api/SWA.h>
|
||||
#include <ui/window.h>
|
||||
#include <ui/game_window.h>
|
||||
#include <ui/window_events.h>
|
||||
#include <user/config.h>
|
||||
#include <os/logger.h>
|
||||
|
@ -126,7 +126,7 @@ PPC_FUNC(sub_823B49D8)
|
|||
{
|
||||
__imp__sub_823B49D8(ctx, base);
|
||||
|
||||
SDL_User_EvilSonic(Window::s_pWindow, true);
|
||||
SDL_User_EvilSonic(GameWindow::s_pWindow, true);
|
||||
}
|
||||
|
||||
// SWA::Player::CEvilSonicContext::Dtor
|
||||
|
@ -135,5 +135,5 @@ PPC_FUNC(sub_823B4590)
|
|||
{
|
||||
__imp__sub_823B4590(ctx, base);
|
||||
|
||||
SDL_User_EvilSonic(Window::s_pWindow, false);
|
||||
SDL_User_EvilSonic(GameWindow::s_pWindow, false);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#include <cpu/guest_code.h>
|
||||
#include <user/config.h>
|
||||
#include <api/SWA.h>
|
||||
#include <ui/window.h>
|
||||
#include <ui/game_window.h>
|
||||
|
||||
// TODO: to be removed.
|
||||
constexpr float m_baseAspectRatio = 16.0f / 9.0f;
|
||||
|
@ -12,7 +12,7 @@ void CSDAspectRatioMidAsmHook(PPCRegister& f1, PPCRegister& f2)
|
|||
if (Config::UIScaleMode == EUIScaleMode::Stretch)
|
||||
return;
|
||||
|
||||
auto newAspectRatio = (float)Window::s_width / (float)Window::s_height;
|
||||
auto newAspectRatio = (float)GameWindow::s_width / (float)GameWindow::s_height;
|
||||
|
||||
if (newAspectRatio > m_baseAspectRatio)
|
||||
{
|
||||
|
|
|
@ -1,10 +1,4 @@
|
|||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include <stb_image.h>
|
||||
|
||||
#define MINIAUDIO_IMPLEMENTATION
|
||||
#include <miniaudio.h>
|
||||
|
||||
#define ma_offset_pcm_frames_ptr (char*)ma_offset_pcm_frames_ptr
|
||||
#include <extras/miniaudio_libvorbis.h>
|
||||
|
||||
#include "stdafx.h"
|
||||
|
|
|
@ -2,9 +2,21 @@
|
|||
|
||||
#define NOMINMAX
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <windows.h>
|
||||
#include <dxcapi.h>
|
||||
#include <ShlObj_core.h>
|
||||
#include <wrl/client.h>
|
||||
|
||||
using Microsoft::WRL::ComPtr;
|
||||
#elif defined(__linux__)
|
||||
#include <unistd.h>
|
||||
#include <pwd.h>
|
||||
#endif
|
||||
|
||||
#ifdef SWA_D3D12
|
||||
#include <dxcapi.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <mutex>
|
||||
#include <filesystem>
|
||||
|
@ -22,21 +34,23 @@
|
|||
#include <toml++/toml.hpp>
|
||||
#include <zstd.h>
|
||||
#include <stb_image.h>
|
||||
#include <concurrentqueue/blockingconcurrentqueue.h>
|
||||
#include <blockingconcurrentqueue.h>
|
||||
#include <SDL.h>
|
||||
#include <SDL_mixer.h>
|
||||
#include <imgui.h>
|
||||
#include <imgui_internal.h>
|
||||
#include <imgui_impl_sdl2.h>
|
||||
#include <backends/imgui_impl_sdl2.h>
|
||||
#include <o1heap.h>
|
||||
#include <cstddef>
|
||||
#include <wrl/client.h>
|
||||
#include <smolv.h>
|
||||
#include <set>
|
||||
#include <miniaudio.h>
|
||||
#include <extras/miniaudio_libvorbis.h>
|
||||
#include <fmt/core.h>
|
||||
|
||||
using Microsoft::WRL::ComPtr;
|
||||
#include <list>
|
||||
#include <semaphore>
|
||||
|
||||
#include "framework.h"
|
||||
#include "mutex.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
|
|
@ -281,9 +281,14 @@ static void DrawAchievement(int rowIndex, float yOffset, Achievement& achievemen
|
|||
return;
|
||||
|
||||
char buffer[32];
|
||||
struct tm time;
|
||||
localtime_s(&time, ×tamp);
|
||||
strftime(buffer, sizeof(buffer), "%Y/%m/%d %H:%M", &time);
|
||||
#ifdef _WIN32
|
||||
tm timeStruct;
|
||||
tm *timePtr = &timeStruct;
|
||||
localtime_s(timePtr, ×tamp);
|
||||
#else
|
||||
tm *timePtr = localtime(×tamp);
|
||||
#endif
|
||||
strftime(buffer, sizeof(buffer), "%Y/%m/%d %H:%M", timePtr);
|
||||
|
||||
fontSize = Scale(12);
|
||||
textSize = g_fntNewRodinDB->CalcTextSizeA(fontSize, FLT_MAX, 0, buffer);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#include "window.h"
|
||||
#include "game_window.h"
|
||||
#include "sdl_listener.h"
|
||||
#include <user/config.h>
|
||||
#include <SDL_syswm.h>
|
||||
|
@ -32,15 +32,15 @@ int Window_OnSDLEvent(void*, SDL_Event* event)
|
|||
if (!(event->key.keysym.mod & KMOD_ALT) || !m_isFullscreenKeyReleased)
|
||||
break;
|
||||
|
||||
Config::Fullscreen = Window::SetFullscreen(!Window::IsFullscreen());
|
||||
Config::Fullscreen = GameWindow::SetFullscreen(!GameWindow::IsFullscreen());
|
||||
|
||||
if (Config::Fullscreen)
|
||||
{
|
||||
Config::Monitor = Window::GetDisplay();
|
||||
Config::Monitor = GameWindow::GetDisplay();
|
||||
}
|
||||
else
|
||||
{
|
||||
Config::WindowState = Window::SetMaximised(Config::WindowState == EWindowState::Maximised);
|
||||
Config::WindowState = GameWindow::SetMaximised(Config::WindowState == EWindowState::Maximised);
|
||||
}
|
||||
|
||||
// Block holding ALT+ENTER spamming window changes.
|
||||
|
@ -51,17 +51,17 @@ int Window_OnSDLEvent(void*, SDL_Event* event)
|
|||
|
||||
// Restore original window dimensions on F2.
|
||||
case SDLK_F2:
|
||||
Config::Fullscreen = Window::SetFullscreen(false);
|
||||
Window::ResetDimensions();
|
||||
Config::Fullscreen = GameWindow::SetFullscreen(false);
|
||||
GameWindow::ResetDimensions();
|
||||
break;
|
||||
|
||||
// Recentre window on F3.
|
||||
case SDLK_F3:
|
||||
{
|
||||
if (Window::IsFullscreen())
|
||||
if (GameWindow::IsFullscreen())
|
||||
break;
|
||||
|
||||
Window::SetDimensions(Window::s_width, Window::s_height);
|
||||
GameWindow::SetDimensions(GameWindow::s_width, GameWindow::s_height);
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -86,16 +86,16 @@ int Window_OnSDLEvent(void*, SDL_Event* event)
|
|||
switch (event->window.event)
|
||||
{
|
||||
case SDL_WINDOWEVENT_FOCUS_LOST:
|
||||
Window::s_isFocused = false;
|
||||
GameWindow::s_isFocused = false;
|
||||
SDL_ShowCursor(SDL_ENABLE);
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT_FOCUS_GAINED:
|
||||
{
|
||||
Window::s_isFocused = true;
|
||||
GameWindow::s_isFocused = true;
|
||||
|
||||
if (Window::IsFullscreen())
|
||||
SDL_ShowCursor(Window::s_isFullscreenCursorVisible ? SDL_ENABLE : SDL_DISABLE);
|
||||
if (GameWindow::IsFullscreen())
|
||||
SDL_ShowCursor(GameWindow::s_isFullscreenCursorVisible ? SDL_ENABLE : SDL_DISABLE);
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -110,14 +110,14 @@ int Window_OnSDLEvent(void*, SDL_Event* event)
|
|||
|
||||
case SDL_WINDOWEVENT_RESIZED:
|
||||
m_isResizing = true;
|
||||
Window::s_width = event->window.data1;
|
||||
Window::s_height = event->window.data2;
|
||||
Window::SetTitle(fmt::format("{} - [{}x{}]", Window::GetTitle(), Window::s_width, Window::s_height).c_str());
|
||||
GameWindow::s_width = event->window.data1;
|
||||
GameWindow::s_height = event->window.data2;
|
||||
GameWindow::SetTitle(fmt::format("{} - [{}x{}]", GameWindow::GetTitle(), GameWindow::s_width, GameWindow::s_height).c_str());
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT_MOVED:
|
||||
Window::s_x = event->window.data1;
|
||||
Window::s_y = event->window.data2;
|
||||
GameWindow::s_x = event->window.data1;
|
||||
GameWindow::s_y = event->window.data2;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -125,12 +125,12 @@ int Window_OnSDLEvent(void*, SDL_Event* event)
|
|||
}
|
||||
|
||||
case SDL_USER_EVILSONIC:
|
||||
Window::s_isIconNight = event->user.code;
|
||||
Window::SetIcon(Window::s_isIconNight);
|
||||
GameWindow::s_isIconNight = event->user.code;
|
||||
GameWindow::SetIcon(GameWindow::s_isIconNight);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!Window::IsFullscreen())
|
||||
if (!GameWindow::IsFullscreen())
|
||||
{
|
||||
if (event->type == SDL_CONTROLLERBUTTONDOWN || event->type == SDL_CONTROLLERBUTTONUP || event->type == SDL_CONTROLLERAXISMOTION)
|
||||
{
|
||||
|
@ -147,12 +147,33 @@ int Window_OnSDLEvent(void*, SDL_Event* event)
|
|||
return 0;
|
||||
}
|
||||
|
||||
void Window::Init()
|
||||
void GameWindow::Init(bool sdlVideoDefault)
|
||||
{
|
||||
SDL_InitSubSystem(SDL_INIT_VIDEO);
|
||||
#ifdef __linux__
|
||||
SDL_SetHint("SDL_APP_ID", "io.github.hedge_dev.unleashedrecomp");
|
||||
|
||||
if (!sdlVideoDefault)
|
||||
{
|
||||
int videoRes = SDL_VideoInit("wayland");
|
||||
if (videoRes != 0)
|
||||
sdlVideoDefault = true;
|
||||
}
|
||||
#else
|
||||
sdlVideoDefault = true;
|
||||
#endif
|
||||
|
||||
if (sdlVideoDefault)
|
||||
SDL_VideoInit(nullptr);
|
||||
|
||||
const char* videoDriverName = SDL_GetCurrentVideoDriver();
|
||||
if (videoDriverName != nullptr)
|
||||
fmt::println("SDL Video Driver: {}", videoDriverName);
|
||||
|
||||
SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
|
||||
SDL_AddEventWatch(Window_OnSDLEvent, s_pWindow);
|
||||
#ifdef _WIN32
|
||||
SetProcessDPIAware();
|
||||
#endif
|
||||
|
||||
s_x = Config::WindowX;
|
||||
s_y = Config::WindowY;
|
||||
|
@ -163,7 +184,7 @@ void Window::Init()
|
|||
s_x = s_y = SDL_WINDOWPOS_CENTERED;
|
||||
|
||||
if (!IsPositionValid())
|
||||
Window::ResetDimensions();
|
||||
GameWindow::ResetDimensions();
|
||||
|
||||
s_pWindow = SDL_CreateWindow("SWA", s_x, s_y, s_width, s_height, GetWindowFlags());
|
||||
|
||||
|
@ -180,21 +201,28 @@ void Window::Init()
|
|||
SDL_VERSION(&info.version);
|
||||
SDL_GetWindowWMInfo(s_pWindow, &info);
|
||||
|
||||
s_handle = info.info.win.window;
|
||||
|
||||
#if defined(_WIN32)
|
||||
s_renderWindow = info.info.win.window;
|
||||
SetDarkTitleBar(true);
|
||||
#elif defined(SDL_VULKAN_ENABLED)
|
||||
s_renderWindow = s_pWindow;
|
||||
#elif defined(__linux__)
|
||||
s_renderWindow = { info.info.x11.display, info.info.x11.window };
|
||||
#else
|
||||
static_assert(false, "Unknown platform.");
|
||||
#endif
|
||||
|
||||
SDL_ShowWindow(s_pWindow);
|
||||
}
|
||||
|
||||
void Window::Update()
|
||||
void GameWindow::Update()
|
||||
{
|
||||
if (!Window::IsFullscreen() && !Window::IsMaximised() && !s_isChangingDisplay)
|
||||
if (!GameWindow::IsFullscreen() && !GameWindow::IsMaximised() && !s_isChangingDisplay)
|
||||
{
|
||||
Config::WindowX = Window::s_x;
|
||||
Config::WindowY = Window::s_y;
|
||||
Config::WindowWidth = Window::s_width;
|
||||
Config::WindowHeight = Window::s_height;
|
||||
Config::WindowX = GameWindow::s_x;
|
||||
Config::WindowY = GameWindow::s_y;
|
||||
Config::WindowWidth = GameWindow::s_width;
|
||||
Config::WindowHeight = GameWindow::s_height;
|
||||
}
|
||||
|
||||
if (m_isResizing)
|
|
@ -6,6 +6,7 @@
|
|||
#include <os/version.h>
|
||||
#include <ui/window_events.h>
|
||||
#include <user/config.h>
|
||||
#include <gpu/rhi/plume_render_interface_types.h>
|
||||
|
||||
#if _WIN32
|
||||
#include <dwmapi.h>
|
||||
|
@ -15,11 +16,11 @@
|
|||
#define DEFAULT_WIDTH 1280
|
||||
#define DEFAULT_HEIGHT 720
|
||||
|
||||
class Window
|
||||
class GameWindow
|
||||
{
|
||||
public:
|
||||
static inline SDL_Window* s_pWindow;
|
||||
static inline HWND s_handle;
|
||||
static inline plume::RenderWindow s_renderWindow;
|
||||
|
||||
static inline int s_x;
|
||||
static inline int s_y;
|
||||
|
@ -88,7 +89,7 @@ public:
|
|||
: 19; // DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1
|
||||
|
||||
const DWORD useImmersiveDarkMode = isEnabled;
|
||||
DwmSetWindowAttribute(s_handle, flag, &useImmersiveDarkMode, sizeof(useImmersiveDarkMode));
|
||||
DwmSetWindowAttribute(s_renderWindow, flag, &useImmersiveDarkMode, sizeof(useImmersiveDarkMode));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -109,7 +110,7 @@ public:
|
|||
SDL_SetWindowFullscreen(s_pWindow, 0);
|
||||
SDL_ShowCursor(SDL_ENABLE);
|
||||
|
||||
SetIcon(Window::s_isIconNight);
|
||||
SetIcon(GameWindow::s_isIconNight);
|
||||
SetDimensions(Config::WindowWidth, Config::WindowHeight, Config::WindowX, Config::WindowY);
|
||||
}
|
||||
|
||||
|
@ -198,6 +199,10 @@ public:
|
|||
if (Config::Fullscreen)
|
||||
flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||
|
||||
#ifdef SDL_VULKAN_ENABLED
|
||||
flags |= SDL_WINDOW_VULKAN;
|
||||
#endif
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
|
@ -298,6 +303,6 @@ public:
|
|||
return false;
|
||||
}
|
||||
|
||||
static void Init();
|
||||
static void Init(bool sdlVideoDefault);
|
||||
static void Update();
|
||||
};
|
|
@ -13,7 +13,7 @@
|
|||
#include <ui/button_guide.h>
|
||||
#include <ui/message_window.h>
|
||||
#include <ui/sdl_listener.h>
|
||||
#include <ui/window.h>
|
||||
#include <ui/game_window.h>
|
||||
#include <decompressor.h>
|
||||
|
||||
#include <res/images/installer/install_001.dds.h>
|
||||
|
@ -96,7 +96,7 @@ static double g_appearTime = 0.0;
|
|||
static double g_disappearTime = DBL_MAX;
|
||||
static bool g_isDisappearing = false;
|
||||
|
||||
static std::filesystem::path g_installPath = ".";
|
||||
static std::filesystem::path g_installPath;
|
||||
static std::filesystem::path g_gameSourcePath;
|
||||
static std::filesystem::path g_updateSourcePath;
|
||||
static std::array<std::filesystem::path, int(DLC::Count)> g_dlcSourcePaths;
|
||||
|
@ -133,8 +133,13 @@ static WizardPage g_firstPage = WizardPage::SelectLanguage;
|
|||
static WizardPage g_currentPage = g_firstPage;
|
||||
static std::string g_currentMessagePrompt = "";
|
||||
static bool g_currentMessagePromptConfirmation = false;
|
||||
static std::list<std::filesystem::path> g_currentPickerResults;
|
||||
static std::atomic<bool> g_currentPickerResultsReady = false;
|
||||
static std::string g_currentPickerErrorMessage;
|
||||
static std::unique_ptr<std::thread> g_currentPickerThread;
|
||||
static bool g_currentPickerVisible = false;
|
||||
static bool g_currentPickerFolderMode = false;
|
||||
static int g_currentMessageResult = -1;
|
||||
static bool g_filesPickerSkipUpdate = false;
|
||||
static ImVec2 g_joypadAxis = {};
|
||||
static int g_currentCursorIndex = -1;
|
||||
static int g_currentCursorDefault = 0;
|
||||
|
@ -148,7 +153,7 @@ public:
|
|||
{
|
||||
constexpr float AxisValueRange = 32767.0f;
|
||||
constexpr float AxisTapRange = 0.5f;
|
||||
if (!InstallerWizard::s_isVisible || !g_currentMessagePrompt.empty())
|
||||
if (!InstallerWizard::s_isVisible || !g_currentMessagePrompt.empty() || g_currentPickerVisible)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -217,7 +222,7 @@ public:
|
|||
case SDL_MOUSEBUTTONDOWN:
|
||||
case SDL_MOUSEMOTION:
|
||||
{
|
||||
for (size_t i = 0; i < g_currentCursorRects.size() && !g_filesPickerSkipUpdate; i++)
|
||||
for (size_t i = 0; i < g_currentCursorRects.size(); i++)
|
||||
{
|
||||
auto ¤tRect = g_currentCursorRects[i];
|
||||
if (ImGui::IsMouseHoveringRect(currentRect.first, currentRect.second, false))
|
||||
|
@ -734,7 +739,7 @@ static void DrawButton(ImVec2 min, ImVec2 max, const char *buttonText, bool sour
|
|||
|
||||
int baser = 0;
|
||||
int baseg = 0;
|
||||
if (g_currentMessagePrompt.empty() && !sourceButton && buttonEnabled && (alpha >= 1.0f))
|
||||
if (g_currentMessagePrompt.empty() && !g_currentPickerVisible && !sourceButton && buttonEnabled && (alpha >= 1.0f))
|
||||
{
|
||||
bool cursorOnButton = PushCursorRect(min, max, buttonPressed, makeDefault);
|
||||
if (cursorOnButton)
|
||||
|
@ -868,58 +873,65 @@ static bool ConvertPathSet(const nfdpathset_t *pathSet, std::list<std::filesyste
|
|||
|
||||
for (nfdpathsetsize_t i = 0; i < pathSetCount; i++)
|
||||
{
|
||||
char *pathSetPath = nullptr;
|
||||
if (NFD_PathSet_GetPathU8(pathSet, i, &pathSetPath) != NFD_OKAY)
|
||||
nfdnchar_t *pathSetPath = nullptr;
|
||||
if (NFD_PathSet_GetPathN(pathSet, i, &pathSetPath) != NFD_OKAY)
|
||||
{
|
||||
filePaths.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
filePaths.emplace_back(std::filesystem::path(std::u8string_view((const char8_t *)(pathSetPath))));
|
||||
NFD_PathSet_FreePathU8(pathSetPath);
|
||||
filePaths.emplace_back(std::filesystem::path(pathSetPath));
|
||||
NFD_PathSet_FreePathN(pathSetPath);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ShowFilesPicker(std::list<std::filesystem::path> &filePaths)
|
||||
static void PickerThreadProcess()
|
||||
{
|
||||
filePaths.clear();
|
||||
|
||||
const nfdpathset_t *pathSet;
|
||||
nfdresult_t result = NFD_OpenDialogMultipleU8(&pathSet, nullptr, 0, nullptr);
|
||||
g_filesPickerSkipUpdate = true;
|
||||
|
||||
if (result == NFD_OKAY)
|
||||
nfdresult_t result = NFD_ERROR;
|
||||
if (g_currentPickerFolderMode)
|
||||
{
|
||||
bool pathsConverted = ConvertPathSet(pathSet, filePaths);
|
||||
NFD_PathSet_Free(pathSet);
|
||||
return pathsConverted;
|
||||
result = NFD_PickFolderMultipleN(&pathSet, nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
result = NFD_OpenDialogMultipleN(&pathSet, nullptr, 0, nullptr);
|
||||
}
|
||||
|
||||
if (result == NFD_OKAY)
|
||||
{
|
||||
bool pathsConverted = ConvertPathSet(pathSet, g_currentPickerResults);
|
||||
NFD_PathSet_Free(pathSet);
|
||||
}
|
||||
else if (result == NFD_ERROR)
|
||||
{
|
||||
g_currentPickerErrorMessage = NFD_GetError();
|
||||
}
|
||||
|
||||
g_currentPickerResultsReady = true;
|
||||
}
|
||||
|
||||
static bool ShowFoldersPicker(std::list<std::filesystem::path> &folderPaths)
|
||||
static void ShowPicker(bool folderMode)
|
||||
{
|
||||
folderPaths.clear();
|
||||
|
||||
const nfdpathset_t *pathSet;
|
||||
nfdresult_t result = NFD_PickFolderMultipleU8(&pathSet, nullptr);
|
||||
g_filesPickerSkipUpdate = true;
|
||||
|
||||
if (result == NFD_OKAY)
|
||||
if (g_currentPickerThread != nullptr)
|
||||
{
|
||||
bool pathsConverted = ConvertPathSet(pathSet, folderPaths);
|
||||
NFD_PathSet_Free(pathSet);
|
||||
return pathsConverted;
|
||||
g_currentPickerThread->join();
|
||||
g_currentPickerThread.reset();
|
||||
}
|
||||
|
||||
g_currentPickerResults.clear();
|
||||
g_currentPickerFolderMode = folderMode;
|
||||
g_currentPickerResultsReady = false;
|
||||
g_currentPickerVisible = true;
|
||||
|
||||
// Optional single thread mode for testing on systems that do not interact well with the separate thread being used for NFD.
|
||||
constexpr bool singleThreadMode = false;
|
||||
if (singleThreadMode)
|
||||
PickerThreadProcess();
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
g_currentPickerThread = std::make_unique<std::thread>(PickerThreadProcess);
|
||||
}
|
||||
|
||||
static void ParseSourcePaths(std::list<std::filesystem::path> &paths)
|
||||
|
@ -973,7 +985,8 @@ static void ParseSourcePaths(std::list<std::filesystem::path> &paths)
|
|||
stringStream << Localise("Installer_Message_InvalidFilesList") << std::endl;
|
||||
for (const std::filesystem::path &path : failedPaths)
|
||||
{
|
||||
stringStream << std::endl << "- " << Truncate(path.filename().string(), 32, true, true);
|
||||
std::u8string filenameU8 = path.filename().u8string();
|
||||
stringStream << std::endl << "- " << Truncate(std::string(filenameU8.begin(), filenameU8.end()), 32, true, true);
|
||||
}
|
||||
|
||||
if (isFailedPathsOverLimit)
|
||||
|
@ -1012,8 +1025,6 @@ static void DrawLanguagePicker()
|
|||
|
||||
static void DrawSourcePickers()
|
||||
{
|
||||
g_filesPickerSkipUpdate = false;
|
||||
|
||||
bool buttonPressed = false;
|
||||
std::list<std::filesystem::path> paths;
|
||||
if (g_currentPage == WizardPage::SelectGameAndUpdate || g_currentPage == WizardPage::SelectDLC)
|
||||
|
@ -1027,9 +1038,9 @@ static void DrawSourcePickers()
|
|||
ImVec2 min = { Scale(AlignToNextGrid(CONTAINER_X) + BOTTOM_X_GAP), Scale(AlignToNextGrid(CONTAINER_Y + CONTAINER_HEIGHT) + BOTTOM_Y_GAP) };
|
||||
ImVec2 max = { Scale(AlignToNextGrid(CONTAINER_X) + BOTTOM_X_GAP + textSize.x * squashRatio), Scale(AlignToNextGrid(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 && ShowFilesPicker(paths))
|
||||
if (buttonPressed)
|
||||
{
|
||||
ParseSourcePaths(paths);
|
||||
ShowPicker(false);
|
||||
}
|
||||
|
||||
min.x += Scale(BOTTOM_X_GAP + textSize.x * squashRatio);
|
||||
|
@ -1040,9 +1051,9 @@ static void DrawSourcePickers()
|
|||
|
||||
max.x = min.x + Scale(textSize.x * squashRatio);
|
||||
DrawButton(min, max, addFolderText.c_str(), false, true, buttonPressed, ADD_BUTTON_MAX_TEXT_WIDTH);
|
||||
if (buttonPressed && ShowFoldersPicker(paths))
|
||||
if (buttonPressed)
|
||||
{
|
||||
ParseSourcePaths(paths);
|
||||
ShowPicker(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1304,14 +1315,6 @@ static void DrawBorders()
|
|||
|
||||
static void DrawMessagePrompt()
|
||||
{
|
||||
if (g_filesPickerSkipUpdate)
|
||||
{
|
||||
// If a blocking function like the files picker is called, we must wait one update before actually showing
|
||||
// the message box, as a lot of time has passed since the last real update. Otherwise, animations will play
|
||||
// too quickly and input glitches might happen.
|
||||
return;
|
||||
}
|
||||
|
||||
if (g_currentMessagePrompt.empty())
|
||||
{
|
||||
return;
|
||||
|
@ -1341,6 +1344,25 @@ static void DrawMessagePrompt()
|
|||
}
|
||||
}
|
||||
|
||||
static void CheckPickerResults()
|
||||
{
|
||||
if (!g_currentPickerResultsReady)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!g_currentPickerErrorMessage.empty())
|
||||
{
|
||||
g_currentMessagePrompt = g_currentPickerErrorMessage;
|
||||
g_currentMessagePromptConfirmation = false;
|
||||
g_currentPickerErrorMessage.clear();
|
||||
}
|
||||
|
||||
ParseSourcePaths(g_currentPickerResults);
|
||||
g_currentPickerResultsReady = false;
|
||||
g_currentPickerVisible = false;
|
||||
}
|
||||
|
||||
void InstallerWizard::Init()
|
||||
{
|
||||
auto &io = ImGui::GetIO();
|
||||
|
@ -1379,6 +1401,7 @@ void InstallerWizard::Draw()
|
|||
DrawNextButton();
|
||||
DrawBorders();
|
||||
DrawMessagePrompt();
|
||||
CheckPickerResults();
|
||||
|
||||
if (g_isDisappearing)
|
||||
{
|
||||
|
@ -1392,13 +1415,19 @@ void InstallerWizard::Draw()
|
|||
|
||||
void InstallerWizard::Shutdown()
|
||||
{
|
||||
// Wait for and erase the thread.
|
||||
// Wait for and erase the threads.
|
||||
if (g_installerThread != nullptr)
|
||||
{
|
||||
g_installerThread->join();
|
||||
g_installerThread.reset();
|
||||
}
|
||||
|
||||
if (g_currentPickerThread != nullptr)
|
||||
{
|
||||
g_currentPickerThread->join();
|
||||
g_currentPickerThread.reset();
|
||||
}
|
||||
|
||||
// Erase the sources.
|
||||
g_installerSources.game.reset();
|
||||
g_installerSources.update.reset();
|
||||
|
@ -1418,8 +1447,10 @@ void InstallerWizard::Shutdown()
|
|||
}
|
||||
}
|
||||
|
||||
bool InstallerWizard::Run(bool skipGame)
|
||||
bool InstallerWizard::Run(std::filesystem::path installPath, bool skipGame)
|
||||
{
|
||||
g_installPath = installPath;
|
||||
|
||||
EmbeddedPlayer::Init();
|
||||
NFD_Init();
|
||||
|
||||
|
@ -1438,18 +1469,18 @@ bool InstallerWizard::Run(bool skipGame)
|
|||
g_currentPage = g_firstPage;
|
||||
}
|
||||
|
||||
Window::SetFullscreenCursorVisibility(true);
|
||||
GameWindow::SetFullscreenCursorVisibility(true);
|
||||
s_isVisible = true;
|
||||
|
||||
while (s_isVisible)
|
||||
{
|
||||
SDL_PumpEvents();
|
||||
SDL_FlushEvents(SDL_FIRSTEVENT, SDL_LASTEVENT);
|
||||
Window::Update();
|
||||
GameWindow::Update();
|
||||
Video::HostPresent();
|
||||
}
|
||||
|
||||
Window::SetFullscreenCursorVisibility(false);
|
||||
GameWindow::SetFullscreenCursorVisibility(false);
|
||||
NFD_Quit();
|
||||
|
||||
InstallerWizard::Shutdown();
|
||||
|
|
|
@ -9,5 +9,5 @@ struct InstallerWizard
|
|||
static void Init();
|
||||
static void Draw();
|
||||
static void Shutdown();
|
||||
static bool Run(bool skipGame);
|
||||
static bool Run(std::filesystem::path installPath, bool skipGame);
|
||||
};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#include "options_menu.h"
|
||||
#include "options_menu_thumbnails.h"
|
||||
#include "imgui_utils.h"
|
||||
#include "window.h"
|
||||
#include "game_window.h"
|
||||
#include "exports.h"
|
||||
|
||||
#include <api/SWA/System/InputState.h>
|
||||
|
@ -444,7 +444,7 @@ static void DrawConfigOption(int32_t rowIndex, float yOffset, ConfigDef<T>* conf
|
|||
ImVec2 min = { clipRectMin.x, clipRectMin.y + (optionHeight + optionPadding) * rowIndex + yOffset };
|
||||
ImVec2 max = { min.x + optionWidth, min.y + optionHeight };
|
||||
|
||||
auto configName = config->GetNameLocalised();
|
||||
auto configName = config->GetNameLocalised(Config::Language);
|
||||
auto size = Scale(26.0f);
|
||||
auto textSize = g_seuratFont->CalcTextSizeA(size, FLT_MAX, 0.0f, configName.c_str());
|
||||
|
||||
|
@ -757,7 +757,7 @@ static void DrawConfigOption(int32_t rowIndex, float yOffset, ConfigDef<T>* conf
|
|||
}
|
||||
else
|
||||
{
|
||||
valueText = config->GetValueLocalised();
|
||||
valueText = config->GetValueLocalised(Config::Language);
|
||||
}
|
||||
|
||||
size = Scale(20.0f);
|
||||
|
@ -837,7 +837,7 @@ static void DrawConfigOptions()
|
|||
{
|
||||
// TODO: expose WindowWidth/WindowHeight as WindowSize.
|
||||
|
||||
auto displayCount = Window::GetDisplayCount();
|
||||
auto displayCount = GameWindow::GetDisplayCount();
|
||||
auto canChangeMonitor = Config::Fullscreen && displayCount > 1;
|
||||
auto monitorReason = &Localise("Options_Desc_NotAvailableWindowed");
|
||||
|
||||
|
@ -978,7 +978,7 @@ static void DrawInfoPanel()
|
|||
|
||||
if (g_selectedItem)
|
||||
{
|
||||
auto desc = g_selectedItem->GetDescription();
|
||||
auto desc = g_selectedItem->GetDescription(Config::Language);
|
||||
auto thumbnail = GetThumbnail(g_selectedItem);
|
||||
|
||||
if (thumbnail)
|
||||
|
@ -997,13 +997,13 @@ static void DrawInfoPanel()
|
|||
auto resScale = round(*(float*)g_selectedItem->GetValue() * 1000) / 1000;
|
||||
|
||||
std::snprintf(buf, sizeof(buf), desc.c_str(),
|
||||
(int)((float)Window::s_width * resScale),
|
||||
(int)((float)Window::s_height * resScale));
|
||||
(int)((float)GameWindow::s_width * resScale),
|
||||
(int)((float)GameWindow::s_height * resScale));
|
||||
|
||||
desc = buf;
|
||||
}
|
||||
|
||||
desc += "\n\n" + g_selectedItem->GetValueDescription();
|
||||
desc += "\n\n" + g_selectedItem->GetValueDescription(Config::Language);
|
||||
}
|
||||
|
||||
auto size = Scale(26.0f);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <SDL.h>
|
||||
#include "ui/window.h"
|
||||
#include "ui/game_window.h"
|
||||
|
||||
#define SDL_USER_EVILSONIC (SDL_USEREVENT + 1)
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ void Config::Load()
|
|||
toml = toml::parse(tomlStream);
|
||||
}
|
||||
|
||||
for (auto def : Config::Definitions)
|
||||
for (auto def : g_configDefinitions)
|
||||
{
|
||||
def->ReadValue(toml);
|
||||
#if _DEBUG
|
||||
|
@ -44,7 +44,7 @@ void Config::Save()
|
|||
std::string result;
|
||||
std::string section;
|
||||
|
||||
for (auto def : Config::Definitions)
|
||||
for (auto def : g_configDefinitions)
|
||||
{
|
||||
auto isFirstSection = section.empty();
|
||||
auto isDefWithSection = section != def->GetSection();
|
||||
|
|
|
@ -1,14 +1,592 @@
|
|||
#pragma once
|
||||
|
||||
#include <user/config_detail.h>
|
||||
#include <locale/locale.h>
|
||||
#include <user/paths.h>
|
||||
#include <exports.h>
|
||||
|
||||
class IConfigDef
|
||||
{
|
||||
public:
|
||||
virtual ~IConfigDef() = default;
|
||||
virtual void ReadValue(toml::v3::ex::parse_result& toml) = 0;
|
||||
virtual void MakeDefault() = 0;
|
||||
virtual std::string_view GetSection() const = 0;
|
||||
virtual std::string_view GetName() const = 0;
|
||||
virtual std::string GetNameLocalised(ELanguage language) const = 0;
|
||||
virtual std::string GetDescription(ELanguage language) const = 0;
|
||||
virtual bool IsDefaultValue() const = 0;
|
||||
virtual const void* GetValue() const = 0;
|
||||
virtual std::string GetValueLocalised(ELanguage language) const = 0;
|
||||
virtual std::string GetValueDescription(ELanguage language) const = 0;
|
||||
virtual std::string GetDefinition(bool withSection = false) const = 0;
|
||||
virtual std::string ToString(bool strWithQuotes = true) const = 0;
|
||||
virtual void GetLocaleStrings(std::vector<std::string_view>& localeStrings) const = 0;
|
||||
};
|
||||
|
||||
#define CONFIG_LOCALE std::unordered_map<ELanguage, std::tuple<std::string, std::string>>
|
||||
#define CONFIG_ENUM_LOCALE(type) std::unordered_map<ELanguage, std::unordered_map<type, std::tuple<std::string, std::string>>>
|
||||
|
||||
#define CONFIG_DEFINE(section, type, name, defaultValue) \
|
||||
static inline ConfigDef<type> name{section, #name, defaultValue};
|
||||
|
||||
#define CONFIG_DEFINE_LOCALISED(section, type, name, defaultValue) \
|
||||
static CONFIG_LOCALE g_##name##_locale; \
|
||||
static inline ConfigDef<type> name{section, #name, &g_##name##_locale, defaultValue};
|
||||
|
||||
#define CONFIG_DEFINE_ENUM(section, type, name, defaultValue) \
|
||||
static inline ConfigDef<type> name{section, #name, defaultValue, &g_##type##_template};
|
||||
|
||||
#define CONFIG_DEFINE_ENUM_LOCALISED(section, type, name, defaultValue) \
|
||||
static CONFIG_LOCALE g_##name##_locale; \
|
||||
static CONFIG_ENUM_LOCALE(type) g_##type##_locale; \
|
||||
static inline ConfigDef<type> name{section, #name, &g_##name##_locale, defaultValue, &g_##type##_template, &g_##type##_locale};
|
||||
|
||||
#define CONFIG_DEFINE_CALLBACK(section, type, name, defaultValue, readCallback) \
|
||||
static CONFIG_LOCALE g_##name##_locale; \
|
||||
static inline ConfigDef<type> name{section, #name, defaultValue, [](ConfigDef<type>* def) readCallback};
|
||||
|
||||
#define CONFIG_DEFINE_ENUM_TEMPLATE(type) \
|
||||
inline std::unordered_map<std::string, type> g_##type##_template =
|
||||
|
||||
#define WINDOWPOS_CENTRED 0x2FFF0000
|
||||
|
||||
inline std::vector<IConfigDef*> g_configDefinitions;
|
||||
|
||||
CONFIG_DEFINE_ENUM_TEMPLATE(ELanguage)
|
||||
{
|
||||
{ "English", ELanguage::English },
|
||||
{ "Japanese", ELanguage::Japanese },
|
||||
{ "German", ELanguage::German },
|
||||
{ "French", ELanguage::French },
|
||||
{ "Spanish", ELanguage::Spanish },
|
||||
{ "Italian", ELanguage::Italian }
|
||||
};
|
||||
|
||||
enum class EUnleashGaugeBehaviour : uint32_t
|
||||
{
|
||||
Original,
|
||||
Revised
|
||||
};
|
||||
|
||||
CONFIG_DEFINE_ENUM_TEMPLATE(EUnleashGaugeBehaviour)
|
||||
{
|
||||
{ "Original", EUnleashGaugeBehaviour::Original },
|
||||
{ "Revised", EUnleashGaugeBehaviour::Revised }
|
||||
};
|
||||
|
||||
enum class ETimeOfDayTransition : uint32_t
|
||||
{
|
||||
Xbox,
|
||||
PlayStation
|
||||
};
|
||||
|
||||
CONFIG_DEFINE_ENUM_TEMPLATE(ETimeOfDayTransition)
|
||||
{
|
||||
{ "Xbox", ETimeOfDayTransition::Xbox },
|
||||
{ "PlayStation", ETimeOfDayTransition::PlayStation }
|
||||
};
|
||||
|
||||
enum class EControllerIcons : uint32_t
|
||||
{
|
||||
Auto,
|
||||
Xbox,
|
||||
PlayStation
|
||||
};
|
||||
|
||||
CONFIG_DEFINE_ENUM_TEMPLATE(EControllerIcons)
|
||||
{
|
||||
{ "Auto", EControllerIcons::Auto },
|
||||
{ "Xbox", EControllerIcons::Xbox },
|
||||
{ "PlayStation", EControllerIcons::PlayStation }
|
||||
};
|
||||
|
||||
enum class EVoiceLanguage : uint32_t
|
||||
{
|
||||
English,
|
||||
Japanese
|
||||
};
|
||||
|
||||
CONFIG_DEFINE_ENUM_TEMPLATE(EVoiceLanguage)
|
||||
{
|
||||
{ "English", EVoiceLanguage::English },
|
||||
{ "Japanese", EVoiceLanguage::Japanese }
|
||||
};
|
||||
|
||||
enum class EGraphicsAPI : uint32_t
|
||||
{
|
||||
#ifdef SWA_D3D12
|
||||
D3D12,
|
||||
#endif
|
||||
Vulkan
|
||||
};
|
||||
|
||||
CONFIG_DEFINE_ENUM_TEMPLATE(EGraphicsAPI)
|
||||
{
|
||||
#ifdef SWA_D3D12
|
||||
{ "D3D12", EGraphicsAPI::D3D12 },
|
||||
#endif
|
||||
{ "Vulkan", EGraphicsAPI::Vulkan }
|
||||
};
|
||||
|
||||
enum class EWindowState : uint32_t
|
||||
{
|
||||
Normal,
|
||||
Maximised
|
||||
};
|
||||
|
||||
CONFIG_DEFINE_ENUM_TEMPLATE(EWindowState)
|
||||
{
|
||||
{ "Normal", EWindowState::Normal },
|
||||
{ "Maximised", EWindowState::Maximised },
|
||||
{ "Maximized", EWindowState::Maximised }
|
||||
};
|
||||
|
||||
enum class EAspectRatio : uint32_t
|
||||
{
|
||||
Auto,
|
||||
Square,
|
||||
Widescreen
|
||||
};
|
||||
|
||||
CONFIG_DEFINE_ENUM_TEMPLATE(EAspectRatio)
|
||||
{
|
||||
{ "Auto", EAspectRatio::Auto },
|
||||
{ "4:3", EAspectRatio::Square },
|
||||
{ "16:9", EAspectRatio::Widescreen }
|
||||
};
|
||||
|
||||
enum class ETripleBuffering : uint32_t
|
||||
{
|
||||
Auto,
|
||||
On,
|
||||
Off
|
||||
};
|
||||
|
||||
CONFIG_DEFINE_ENUM_TEMPLATE(ETripleBuffering)
|
||||
{
|
||||
{ "Auto", ETripleBuffering::Auto },
|
||||
{ "On", ETripleBuffering::On },
|
||||
{ "Off", ETripleBuffering::Off }
|
||||
};
|
||||
|
||||
enum class EAntiAliasing : uint32_t
|
||||
{
|
||||
None = 0,
|
||||
MSAA2x = 2,
|
||||
MSAA4x = 4,
|
||||
MSAA8x = 8
|
||||
};
|
||||
|
||||
CONFIG_DEFINE_ENUM_TEMPLATE(EAntiAliasing)
|
||||
{
|
||||
{ "None", EAntiAliasing::None },
|
||||
{ "2x MSAA", EAntiAliasing::MSAA2x },
|
||||
{ "4x MSAA", EAntiAliasing::MSAA4x },
|
||||
{ "8x MSAA", EAntiAliasing::MSAA8x }
|
||||
};
|
||||
|
||||
enum class EShadowResolution : int32_t
|
||||
{
|
||||
Original = -1,
|
||||
x512 = 512,
|
||||
x1024 = 1024,
|
||||
x2048 = 2048,
|
||||
x4096 = 4096,
|
||||
x8192 = 8192
|
||||
};
|
||||
|
||||
CONFIG_DEFINE_ENUM_TEMPLATE(EShadowResolution)
|
||||
{
|
||||
{ "Original", EShadowResolution::Original },
|
||||
{ "512", EShadowResolution::x512 },
|
||||
{ "1024", EShadowResolution::x1024 },
|
||||
{ "2048", EShadowResolution::x2048 },
|
||||
{ "4096", EShadowResolution::x4096 },
|
||||
{ "8192", EShadowResolution::x8192 },
|
||||
};
|
||||
|
||||
enum class EGITextureFiltering : uint32_t
|
||||
{
|
||||
Bilinear,
|
||||
Bicubic
|
||||
};
|
||||
|
||||
CONFIG_DEFINE_ENUM_TEMPLATE(EGITextureFiltering)
|
||||
{
|
||||
{ "Bilinear", EGITextureFiltering::Bilinear },
|
||||
{ "Bicubic", EGITextureFiltering::Bicubic }
|
||||
};
|
||||
|
||||
enum class EDepthOfFieldQuality : uint32_t
|
||||
{
|
||||
Auto,
|
||||
Low,
|
||||
Medium,
|
||||
High,
|
||||
Ultra
|
||||
};
|
||||
|
||||
CONFIG_DEFINE_ENUM_TEMPLATE(EDepthOfFieldQuality)
|
||||
{
|
||||
{ "Auto", EDepthOfFieldQuality::Auto },
|
||||
{ "Low", EDepthOfFieldQuality::Low },
|
||||
{ "Medium", EDepthOfFieldQuality::Medium },
|
||||
{ "High", EDepthOfFieldQuality::High },
|
||||
{ "Ultra", EDepthOfFieldQuality::Ultra }
|
||||
};
|
||||
|
||||
enum class EMotionBlur : uint32_t
|
||||
{
|
||||
Off,
|
||||
Original,
|
||||
Enhanced
|
||||
};
|
||||
|
||||
CONFIG_DEFINE_ENUM_TEMPLATE(EMotionBlur)
|
||||
{
|
||||
{ "Off", EMotionBlur::Off },
|
||||
{ "Original", EMotionBlur::Original },
|
||||
{ "Enhanced", EMotionBlur::Enhanced }
|
||||
};
|
||||
|
||||
enum class EMovieScaleMode : uint32_t
|
||||
{
|
||||
Stretch,
|
||||
Fit,
|
||||
Fill
|
||||
};
|
||||
|
||||
CONFIG_DEFINE_ENUM_TEMPLATE(EMovieScaleMode)
|
||||
{
|
||||
{ "Stretch", EMovieScaleMode::Stretch },
|
||||
{ "Fit", EMovieScaleMode::Fit },
|
||||
{ "Fill", EMovieScaleMode::Fill }
|
||||
};
|
||||
|
||||
enum class EUIScaleMode : uint32_t
|
||||
{
|
||||
Stretch,
|
||||
Edge,
|
||||
Centre
|
||||
};
|
||||
|
||||
CONFIG_DEFINE_ENUM_TEMPLATE(EUIScaleMode)
|
||||
{
|
||||
{ "Stretch", EUIScaleMode::Stretch },
|
||||
{ "Edge", EUIScaleMode::Edge },
|
||||
{ "Centre", EUIScaleMode::Centre },
|
||||
{ "Center", EUIScaleMode::Centre }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class ConfigDef final : public IConfigDef
|
||||
{
|
||||
public:
|
||||
std::string Section{};
|
||||
std::string Name{};
|
||||
CONFIG_LOCALE* Locale{};
|
||||
T DefaultValue{};
|
||||
T Value{ DefaultValue };
|
||||
std::unordered_map<std::string, T>* EnumTemplate;
|
||||
std::map<T, std::string> EnumTemplateReverse{};
|
||||
CONFIG_ENUM_LOCALE(T)* EnumLocale{};
|
||||
std::function<void(ConfigDef<T>*)> Callback;
|
||||
|
||||
// CONFIG_DEFINE
|
||||
ConfigDef(std::string section, std::string name, T defaultValue) : Section(section), Name(name), DefaultValue(defaultValue)
|
||||
{
|
||||
g_configDefinitions.emplace_back(this);
|
||||
}
|
||||
|
||||
// CONFIG_DEFINE_LOCALISED
|
||||
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
|
||||
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
|
||||
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
|
||||
ConfigDef(std::string section, std::string name, T defaultValue, std::function<void(ConfigDef<T>*)> callback) : Section(section), Name(name), DefaultValue(defaultValue), Callback(callback)
|
||||
{
|
||||
g_configDefinitions.emplace_back(this);
|
||||
}
|
||||
|
||||
void ReadValue(toml::v3::ex::parse_result& toml) override
|
||||
{
|
||||
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>)
|
||||
{
|
||||
std::string 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);
|
||||
}
|
||||
}
|
||||
|
||||
void MakeDefault() override
|
||||
{
|
||||
Value = DefaultValue;
|
||||
}
|
||||
|
||||
std::string_view GetSection() const override
|
||||
{
|
||||
return Section;
|
||||
}
|
||||
|
||||
std::string_view GetName() const override
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
|
||||
std::string GetNameLocalised(ELanguage language) const override
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
std::string GetDescription(ELanguage language) const override
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
bool IsDefaultValue() const override
|
||||
{
|
||||
return Value == DefaultValue;
|
||||
}
|
||||
|
||||
const void* GetValue() const override
|
||||
{
|
||||
return &Value;
|
||||
}
|
||||
|
||||
std::string GetValueLocalised(ELanguage language) const override
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
std::string GetValueDescription(ELanguage language) const override
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
std::string GetDefinition(bool withSection = false) const override
|
||||
{
|
||||
std::string result;
|
||||
|
||||
if (withSection)
|
||||
result += "[" + Section + "]\n";
|
||||
|
||||
result += Name + " = " + ToString();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string ToString(bool strWithQuotes = true) const override
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
void GetLocaleStrings(std::vector<std::string_view>& localeStrings) const override
|
||||
{
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ConfigDef& operator=(const ConfigDef& other)
|
||||
{
|
||||
if (this != &other)
|
||||
Value = other.Value;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator T() const
|
||||
{
|
||||
return Value;
|
||||
}
|
||||
|
||||
void operator=(const T& other)
|
||||
{
|
||||
Value = other;
|
||||
}
|
||||
};
|
||||
|
||||
class Config
|
||||
{
|
||||
public:
|
||||
static inline std::vector<IConfigDef*> Definitions{};
|
||||
|
||||
CONFIG_DEFINE_ENUM_LOCALISED("System", ELanguage, Language, ELanguage::English);
|
||||
CONFIG_DEFINE_LOCALISED("System", bool, Hints, true);
|
||||
CONFIG_DEFINE_LOCALISED("System", bool, ControlTutorial, true);
|
||||
|
@ -28,7 +606,12 @@ public:
|
|||
CONFIG_DEFINE_LOCALISED("Audio", bool, MusicAttenuation, false);
|
||||
CONFIG_DEFINE_LOCALISED("Audio", bool, BattleTheme, true);
|
||||
|
||||
#ifdef SWA_D3D12
|
||||
CONFIG_DEFINE_ENUM("Video", EGraphicsAPI, GraphicsAPI, EGraphicsAPI::D3D12);
|
||||
#else
|
||||
CONFIG_DEFINE_ENUM("Video", EGraphicsAPI, GraphicsAPI, EGraphicsAPI::Vulkan);
|
||||
#endif
|
||||
|
||||
CONFIG_DEFINE("Video", int32_t, WindowX, WINDOWPOS_CENTRED);
|
||||
CONFIG_DEFINE("Video", int32_t, WindowY, WINDOWPOS_CENTRED);
|
||||
CONFIG_DEFINE("Video", int32_t, WindowWidth, 1280);
|
||||
|
|
|
@ -1,193 +0,0 @@
|
|||
#include "config.h"
|
||||
#include "config_detail.h"
|
||||
#include <locale/locale.h>
|
||||
|
||||
// CONFIG_DEFINE
|
||||
template<typename T>
|
||||
ConfigDef<T>::ConfigDef(std::string section, std::string name, T defaultValue) : Section(section), Name(name), DefaultValue(defaultValue)
|
||||
{
|
||||
Config::Definitions.emplace_back(this);
|
||||
}
|
||||
|
||||
// CONFIG_DEFINE_LOCALISED
|
||||
template<typename T>
|
||||
ConfigDef<T>::ConfigDef(std::string section, std::string name, CONFIG_LOCALE* locale, T defaultValue)
|
||||
: Section(section), Name(name), Locale(locale), DefaultValue(defaultValue)
|
||||
{
|
||||
Config::Definitions.emplace_back(this);
|
||||
}
|
||||
|
||||
// CONFIG_DEFINE_ENUM
|
||||
template<typename T>
|
||||
ConfigDef<T>::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;
|
||||
|
||||
Config::Definitions.emplace_back(this);
|
||||
}
|
||||
|
||||
// CONFIG_DEFINE_ENUM_LOCALISED
|
||||
template<typename T>
|
||||
ConfigDef<T>::ConfigDef(std::string section, std::string name, CONFIG_LOCALE* locale, T defaultValue, std::unordered_map<std::string, T>* enumTemplate, CONFIG_ENUM_LOCALE(T)* enumLocale)
|
||||
: Section(section), Name(name), Locale(locale), DefaultValue(defaultValue), EnumTemplate(enumTemplate), EnumLocale(enumLocale)
|
||||
{
|
||||
for (const auto& pair : *EnumTemplate)
|
||||
EnumTemplateReverse[pair.second] = pair.first;
|
||||
|
||||
Config::Definitions.emplace_back(this);
|
||||
}
|
||||
|
||||
// CONFIG_DEFINE_CALLBACK
|
||||
template<typename T>
|
||||
ConfigDef<T>::ConfigDef(std::string section, std::string name, T defaultValue, std::function<void(ConfigDef<T>*)> callback)
|
||||
: Section(section), Name(name), DefaultValue(defaultValue), Callback(callback)
|
||||
{
|
||||
Config::Definitions.emplace_back(this);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::string ConfigDef<T>::GetNameLocalised() const
|
||||
{
|
||||
if (!Locale)
|
||||
return Name;
|
||||
|
||||
if (!Locale->count(Config::Language))
|
||||
{
|
||||
if (Locale->count(ELanguage::English))
|
||||
{
|
||||
return std::get<0>(Locale->at(ELanguage::English));
|
||||
}
|
||||
else
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
|
||||
return std::get<0>(Locale->at(Config::Language));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::string ConfigDef<T>::GetDescription() const
|
||||
{
|
||||
if (!Locale)
|
||||
return "";
|
||||
|
||||
if (!Locale->count(Config::Language))
|
||||
{
|
||||
if (Locale->count(ELanguage::English))
|
||||
{
|
||||
return std::get<1>(Locale->at(ELanguage::English));
|
||||
}
|
||||
else
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
return std::get<1>(Locale->at(Config::Language));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::string ConfigDef<T>::GetValueLocalised() const
|
||||
{
|
||||
auto language = Config::Language;
|
||||
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>
|
||||
std::string ConfigDef<T>::GetValueDescription() const
|
||||
{
|
||||
auto language = Config::Language;
|
||||
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>
|
||||
inline void ConfigDef<T>::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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,436 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#define CONFIG_LOCALE std::unordered_map<ELanguage, std::tuple<std::string, std::string>>
|
||||
#define CONFIG_ENUM_LOCALE(type) std::unordered_map<ELanguage, std::unordered_map<type, std::tuple<std::string, std::string>>>
|
||||
|
||||
#define CONFIG_DEFINE(section, type, name, defaultValue) \
|
||||
static inline ConfigDef<type> name{section, #name, defaultValue};
|
||||
|
||||
#define CONFIG_DEFINE_LOCALISED(section, type, name, defaultValue) \
|
||||
static CONFIG_LOCALE g_##name##_locale; \
|
||||
static inline ConfigDef<type> name{section, #name, &g_##name##_locale, defaultValue};
|
||||
|
||||
#define CONFIG_DEFINE_ENUM(section, type, name, defaultValue) \
|
||||
static inline ConfigDef<type> name{section, #name, defaultValue, &g_##type##_template};
|
||||
|
||||
#define CONFIG_DEFINE_ENUM_LOCALISED(section, type, name, defaultValue) \
|
||||
static CONFIG_LOCALE g_##name##_locale; \
|
||||
static CONFIG_ENUM_LOCALE(type) g_##type##_locale; \
|
||||
static inline ConfigDef<type> name{section, #name, &g_##name##_locale, defaultValue, &g_##type##_template, &g_##type##_locale};
|
||||
|
||||
#define CONFIG_DEFINE_CALLBACK(section, type, name, defaultValue, readCallback) \
|
||||
static CONFIG_LOCALE g_##name##_locale; \
|
||||
static inline ConfigDef<type> name{section, #name, defaultValue, [](ConfigDef<type>* def) readCallback};
|
||||
|
||||
#define CONFIG_DEFINE_ENUM_TEMPLATE(type) \
|
||||
inline std::unordered_map<std::string, type> g_##type##_template =
|
||||
|
||||
#define WINDOWPOS_CENTRED 0x2FFF0000
|
||||
|
||||
enum class ELanguage : uint32_t
|
||||
{
|
||||
English = 1,
|
||||
Japanese,
|
||||
German,
|
||||
French,
|
||||
Spanish,
|
||||
Italian
|
||||
};
|
||||
|
||||
CONFIG_DEFINE_ENUM_TEMPLATE(ELanguage)
|
||||
{
|
||||
{ "English", ELanguage::English },
|
||||
{ "Japanese", ELanguage::Japanese },
|
||||
{ "German", ELanguage::German },
|
||||
{ "French", ELanguage::French },
|
||||
{ "Spanish", ELanguage::Spanish },
|
||||
{ "Italian", ELanguage::Italian }
|
||||
};
|
||||
|
||||
enum class EUnleashGaugeBehaviour : uint32_t
|
||||
{
|
||||
Original,
|
||||
Revised
|
||||
};
|
||||
|
||||
CONFIG_DEFINE_ENUM_TEMPLATE(EUnleashGaugeBehaviour)
|
||||
{
|
||||
{ "Original", EUnleashGaugeBehaviour::Original },
|
||||
{ "Revised", EUnleashGaugeBehaviour::Revised }
|
||||
};
|
||||
|
||||
enum class ETimeOfDayTransition : uint32_t
|
||||
{
|
||||
Xbox,
|
||||
PlayStation
|
||||
};
|
||||
|
||||
CONFIG_DEFINE_ENUM_TEMPLATE(ETimeOfDayTransition)
|
||||
{
|
||||
{ "Xbox", ETimeOfDayTransition::Xbox },
|
||||
{ "PlayStation", ETimeOfDayTransition::PlayStation }
|
||||
};
|
||||
|
||||
enum class EControllerIcons : uint32_t
|
||||
{
|
||||
Auto,
|
||||
Xbox,
|
||||
PlayStation
|
||||
};
|
||||
|
||||
CONFIG_DEFINE_ENUM_TEMPLATE(EControllerIcons)
|
||||
{
|
||||
{ "Auto", EControllerIcons::Auto },
|
||||
{ "Xbox", EControllerIcons::Xbox },
|
||||
{ "PlayStation", EControllerIcons::PlayStation }
|
||||
};
|
||||
|
||||
enum class EVoiceLanguage : uint32_t
|
||||
{
|
||||
English,
|
||||
Japanese
|
||||
};
|
||||
|
||||
CONFIG_DEFINE_ENUM_TEMPLATE(EVoiceLanguage)
|
||||
{
|
||||
{ "English", EVoiceLanguage::English },
|
||||
{ "Japanese", EVoiceLanguage::Japanese }
|
||||
};
|
||||
|
||||
enum class EGraphicsAPI : uint32_t
|
||||
{
|
||||
D3D12,
|
||||
Vulkan
|
||||
};
|
||||
|
||||
CONFIG_DEFINE_ENUM_TEMPLATE(EGraphicsAPI)
|
||||
{
|
||||
{ "D3D12", EGraphicsAPI::D3D12 },
|
||||
{ "Vulkan", EGraphicsAPI::Vulkan }
|
||||
};
|
||||
|
||||
enum class EWindowState : uint32_t
|
||||
{
|
||||
Normal,
|
||||
Maximised
|
||||
};
|
||||
|
||||
CONFIG_DEFINE_ENUM_TEMPLATE(EWindowState)
|
||||
{
|
||||
{ "Normal", EWindowState::Normal },
|
||||
{ "Maximised", EWindowState::Maximised },
|
||||
{ "Maximized", EWindowState::Maximised }
|
||||
};
|
||||
|
||||
enum class EAspectRatio : uint32_t
|
||||
{
|
||||
Auto,
|
||||
Square,
|
||||
Widescreen
|
||||
};
|
||||
|
||||
CONFIG_DEFINE_ENUM_TEMPLATE(EAspectRatio)
|
||||
{
|
||||
{ "Auto", EAspectRatio::Auto },
|
||||
{ "4:3", EAspectRatio::Square },
|
||||
{ "16:9", EAspectRatio::Widescreen }
|
||||
};
|
||||
|
||||
enum class ETripleBuffering : uint32_t
|
||||
{
|
||||
Auto,
|
||||
On,
|
||||
Off
|
||||
};
|
||||
|
||||
CONFIG_DEFINE_ENUM_TEMPLATE(ETripleBuffering)
|
||||
{
|
||||
{ "Auto", ETripleBuffering::Auto },
|
||||
{ "On", ETripleBuffering::On },
|
||||
{ "Off", ETripleBuffering::Off }
|
||||
};
|
||||
|
||||
enum class EAntiAliasing : uint32_t
|
||||
{
|
||||
None = 0,
|
||||
MSAA2x = 2,
|
||||
MSAA4x = 4,
|
||||
MSAA8x = 8
|
||||
};
|
||||
|
||||
CONFIG_DEFINE_ENUM_TEMPLATE(EAntiAliasing)
|
||||
{
|
||||
{ "None", EAntiAliasing::None },
|
||||
{ "2x MSAA", EAntiAliasing::MSAA2x },
|
||||
{ "4x MSAA", EAntiAliasing::MSAA4x },
|
||||
{ "8x MSAA", EAntiAliasing::MSAA8x }
|
||||
};
|
||||
|
||||
enum class EShadowResolution : int32_t
|
||||
{
|
||||
Original = -1,
|
||||
x512 = 512,
|
||||
x1024 = 1024,
|
||||
x2048 = 2048,
|
||||
x4096 = 4096,
|
||||
x8192 = 8192
|
||||
};
|
||||
|
||||
CONFIG_DEFINE_ENUM_TEMPLATE(EShadowResolution)
|
||||
{
|
||||
{ "Original", EShadowResolution::Original },
|
||||
{ "512", EShadowResolution::x512 },
|
||||
{ "1024", EShadowResolution::x1024 },
|
||||
{ "2048", EShadowResolution::x2048 },
|
||||
{ "4096", EShadowResolution::x4096 },
|
||||
{ "8192", EShadowResolution::x8192 },
|
||||
};
|
||||
|
||||
enum class EGITextureFiltering : uint32_t
|
||||
{
|
||||
Bilinear,
|
||||
Bicubic
|
||||
};
|
||||
|
||||
CONFIG_DEFINE_ENUM_TEMPLATE(EGITextureFiltering)
|
||||
{
|
||||
{ "Bilinear", EGITextureFiltering::Bilinear },
|
||||
{ "Bicubic", EGITextureFiltering::Bicubic }
|
||||
};
|
||||
|
||||
enum class EDepthOfFieldQuality : uint32_t
|
||||
{
|
||||
Auto,
|
||||
Low,
|
||||
Medium,
|
||||
High,
|
||||
Ultra
|
||||
};
|
||||
|
||||
CONFIG_DEFINE_ENUM_TEMPLATE(EDepthOfFieldQuality)
|
||||
{
|
||||
{ "Auto", EDepthOfFieldQuality::Auto },
|
||||
{ "Low", EDepthOfFieldQuality::Low },
|
||||
{ "Medium", EDepthOfFieldQuality::Medium },
|
||||
{ "High", EDepthOfFieldQuality::High },
|
||||
{ "Ultra", EDepthOfFieldQuality::Ultra }
|
||||
};
|
||||
|
||||
enum class EMotionBlur : uint32_t
|
||||
{
|
||||
Off,
|
||||
Original,
|
||||
Enhanced
|
||||
};
|
||||
|
||||
CONFIG_DEFINE_ENUM_TEMPLATE(EMotionBlur)
|
||||
{
|
||||
{ "Off", EMotionBlur::Off },
|
||||
{ "Original", EMotionBlur::Original },
|
||||
{ "Enhanced", EMotionBlur::Enhanced }
|
||||
};
|
||||
|
||||
enum class EMovieScaleMode : uint32_t
|
||||
{
|
||||
Stretch,
|
||||
Fit,
|
||||
Fill
|
||||
};
|
||||
|
||||
CONFIG_DEFINE_ENUM_TEMPLATE(EMovieScaleMode)
|
||||
{
|
||||
{ "Stretch", EMovieScaleMode::Stretch },
|
||||
{ "Fit", EMovieScaleMode::Fit },
|
||||
{ "Fill", EMovieScaleMode::Fill }
|
||||
};
|
||||
|
||||
enum class EUIScaleMode : uint32_t
|
||||
{
|
||||
Stretch,
|
||||
Edge,
|
||||
Centre
|
||||
};
|
||||
|
||||
CONFIG_DEFINE_ENUM_TEMPLATE(EUIScaleMode)
|
||||
{
|
||||
{ "Stretch", EUIScaleMode::Stretch },
|
||||
{ "Edge", EUIScaleMode::Edge },
|
||||
{ "Centre", EUIScaleMode::Centre },
|
||||
{ "Center", EUIScaleMode::Centre }
|
||||
};
|
||||
|
||||
class IConfigDef
|
||||
{
|
||||
public:
|
||||
virtual ~IConfigDef() = default;
|
||||
virtual void ReadValue(toml::v3::ex::parse_result& toml) = 0;
|
||||
virtual void MakeDefault() = 0;
|
||||
virtual std::string_view GetSection() const = 0;
|
||||
virtual std::string_view GetName() const = 0;
|
||||
virtual std::string GetNameLocalised() const = 0;
|
||||
virtual std::string GetDescription() const = 0;
|
||||
virtual bool IsDefaultValue() const = 0;
|
||||
virtual const void* GetValue() const = 0;
|
||||
virtual std::string GetValueLocalised() const = 0;
|
||||
virtual std::string GetValueDescription() const = 0;
|
||||
virtual std::string GetDefinition(bool withSection = false) const = 0;
|
||||
virtual std::string ToString(bool strWithQuotes = true) const = 0;
|
||||
virtual void GetLocaleStrings(std::vector<std::string_view>& localeStrings) const = 0;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class ConfigDef final : public IConfigDef
|
||||
{
|
||||
public:
|
||||
std::string Section{};
|
||||
std::string Name{};
|
||||
CONFIG_LOCALE* Locale{};
|
||||
T DefaultValue{};
|
||||
T Value{ DefaultValue };
|
||||
std::unordered_map<std::string, T>* EnumTemplate;
|
||||
std::map<T, std::string> EnumTemplateReverse{};
|
||||
CONFIG_ENUM_LOCALE(T)* EnumLocale{};
|
||||
std::function<void(ConfigDef<T>*)> Callback;
|
||||
|
||||
// CONFIG_DEFINE
|
||||
ConfigDef(std::string section, std::string name, T defaultValue);
|
||||
|
||||
// CONFIG_DEFINE_LOCALISED
|
||||
ConfigDef(std::string section, std::string name, CONFIG_LOCALE* nameLocale, T defaultValue);
|
||||
|
||||
// CONFIG_DEFINE_ENUM
|
||||
ConfigDef(std::string section, std::string name, T defaultValue, std::unordered_map<std::string, T>* enumTemplate);
|
||||
|
||||
// CONFIG_DEFINE_ENUM_LOCALISED
|
||||
ConfigDef(std::string section, std::string name, CONFIG_LOCALE* nameLocale, T defaultValue, std::unordered_map<std::string, T>* enumTemplate, CONFIG_ENUM_LOCALE(T)* enumLocale);
|
||||
|
||||
// CONFIG_DEFINE_CALLBACK
|
||||
ConfigDef(std::string section, std::string name, T defaultValue, std::function<void(ConfigDef<T>*)> callback);
|
||||
|
||||
void ReadValue(toml::v3::ex::parse_result& toml) override
|
||||
{
|
||||
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>)
|
||||
{
|
||||
std::string 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);
|
||||
}
|
||||
}
|
||||
|
||||
void MakeDefault() override
|
||||
{
|
||||
Value = DefaultValue;
|
||||
}
|
||||
|
||||
std::string_view GetSection() const override
|
||||
{
|
||||
return Section;
|
||||
}
|
||||
|
||||
std::string_view GetName() const override
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
|
||||
std::string GetNameLocalised() const override;
|
||||
|
||||
std::string GetDescription() const override;
|
||||
|
||||
bool IsDefaultValue() const override
|
||||
{
|
||||
return Value == DefaultValue;
|
||||
}
|
||||
|
||||
const void* GetValue() const override
|
||||
{
|
||||
return &Value;
|
||||
}
|
||||
|
||||
std::string GetValueLocalised() const override;
|
||||
|
||||
std::string GetValueDescription() const override;
|
||||
|
||||
std::string GetDefinition(bool withSection = false) const override
|
||||
{
|
||||
std::string result;
|
||||
|
||||
if (withSection)
|
||||
result += "[" + Section + "]\n";
|
||||
|
||||
result += Name + " = " + ToString();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string ToString(bool strWithQuotes = true) const override
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
void GetLocaleStrings(std::vector<std::string_view>& localeStrings) const override;
|
||||
|
||||
ConfigDef& operator=(const ConfigDef& other)
|
||||
{
|
||||
if (this != &other)
|
||||
Value = other.Value;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator T() const
|
||||
{
|
||||
return Value;
|
||||
}
|
||||
|
||||
void operator=(const T& other)
|
||||
{
|
||||
Value = other;
|
||||
}
|
||||
};
|
|
@ -2,24 +2,48 @@
|
|||
|
||||
#define USER_DIRECTORY "SWA"
|
||||
|
||||
#ifndef GAME_INSTALL_DIRECTORY
|
||||
#define GAME_INSTALL_DIRECTORY "."
|
||||
#endif
|
||||
|
||||
inline std::filesystem::path GetGamePath()
|
||||
{
|
||||
return std::filesystem::current_path();
|
||||
return GAME_INSTALL_DIRECTORY;
|
||||
}
|
||||
|
||||
inline std::filesystem::path GetUserPath()
|
||||
{
|
||||
if (std::filesystem::exists("portable.txt"))
|
||||
return std::filesystem::current_path();
|
||||
if (std::filesystem::exists(GAME_INSTALL_DIRECTORY "/portable.txt"))
|
||||
return GAME_INSTALL_DIRECTORY;
|
||||
|
||||
std::filesystem::path userPath{};
|
||||
std::filesystem::path userPath;
|
||||
|
||||
// TODO: handle platform-specific paths.
|
||||
#if defined(_WIN32)
|
||||
PWSTR knownPath = NULL;
|
||||
if (SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, NULL, &knownPath) == S_OK)
|
||||
userPath = std::filesystem::path{ knownPath } / USER_DIRECTORY;
|
||||
|
||||
CoTaskMemFree(knownPath);
|
||||
#elif defined(__linux__)
|
||||
const char *homeDir = getenv("HOME");
|
||||
if (homeDir == nullptr)
|
||||
{
|
||||
homeDir = getpwuid(getuid())->pw_dir;
|
||||
}
|
||||
|
||||
if (homeDir != nullptr)
|
||||
{
|
||||
// Prefer to store in the .config directory if it exists. Use the home directory otherwise.
|
||||
std::filesystem::path homePath = homeDir;
|
||||
std::filesystem::path configPath = homePath / ".config";
|
||||
if (std::filesystem::exists(configPath))
|
||||
userPath = configPath / USER_DIRECTORY;
|
||||
else
|
||||
userPath = homePath / ("." USER_DIRECTORY);
|
||||
}
|
||||
#else
|
||||
static_assert(false, "GetUserPath() not implemented for this platform.");
|
||||
#endif
|
||||
|
||||
return userPath;
|
||||
}
|
||||
|
|
|
@ -1,11 +1,17 @@
|
|||
project("UnleashedRecompLib")
|
||||
|
||||
add_compile_options(
|
||||
"/fp:strict"
|
||||
"-march=sandybridge"
|
||||
"-fno-strict-aliasing"
|
||||
-march=sandybridge
|
||||
-mlzcnt
|
||||
-fno-strict-aliasing
|
||||
)
|
||||
|
||||
if (WIN32)
|
||||
add_compile_options(/fp:strict)
|
||||
else()
|
||||
add_compile_options(-ffp-model=strict)
|
||||
endif()
|
||||
|
||||
target_compile_definitions(PowerRecomp PRIVATE CONFIG_FILE_PATH=\"${CMAKE_CURRENT_SOURCE_DIR}/config/SWA.toml\")
|
||||
|
||||
set(SWA_PPC_RECOMPILED_SOURCES
|
||||
|
@ -21,7 +27,7 @@ endforeach()
|
|||
|
||||
add_custom_command(
|
||||
OUTPUT ${SWA_PPC_RECOMPILED_SOURCES}
|
||||
COMMAND PowerRecomp
|
||||
COMMAND $<TARGET_FILE:PowerRecomp>
|
||||
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/private/default.xex" "${CMAKE_CURRENT_SOURCE_DIR}/config/SWA.toml"
|
||||
)
|
||||
|
||||
|
@ -41,7 +47,7 @@ file(GLOB SHADER_RECOMP_SOURCES
|
|||
|
||||
add_custom_command(
|
||||
OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/shader/shader_cache.cpp"
|
||||
COMMAND ShaderRecomp
|
||||
COMMAND $<TARGET_FILE:ShaderRecomp>
|
||||
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/private/shader.ar" ${SHADER_RECOMP_SOURCES} ${SHADER_RECOMP_INCLUDE}
|
||||
)
|
||||
|
||||
|
|
10
flatpak/README.md
Normal file
10
flatpak/README.md
Normal file
|
@ -0,0 +1,10 @@
|
|||
Build
|
||||
```sh
|
||||
flatpak-builder --force-clean --user --install-deps-from=flathub --repo=repo --install builddir io.github.hedge_dev.unleashedrecomp.json
|
||||
```
|
||||
|
||||
Bundle
|
||||
```sh
|
||||
flatpak build-bundle repo io.github.hedge_dev.unleashedrecomp.flatpak io.github.hedge_dev.unleashedrecomp --runtime-repo=https://flathub.org/repo/flathub.flatpakrepo
|
||||
```
|
||||
|
7
flatpak/io.github.hedge_dev.unleashedrecomp.desktop
Normal file
7
flatpak/io.github.hedge_dev.unleashedrecomp.desktop
Normal file
|
@ -0,0 +1,7 @@
|
|||
[Desktop Entry]
|
||||
Name=Unleashed Recompiled
|
||||
Exec=/app/bin/SWA
|
||||
Type=Application
|
||||
Icon=io.github.hedge_dev.unleashedrecomp
|
||||
Categories=Game;
|
||||
Comment=Static recompilation of Sonic Unleashed.
|
56
flatpak/io.github.hedge_dev.unleashedrecomp.json
Normal file
56
flatpak/io.github.hedge_dev.unleashedrecomp.json
Normal file
|
@ -0,0 +1,56 @@
|
|||
{
|
||||
"id": "io.github.hedge_dev.unleashedrecomp",
|
||||
"runtime": "org.freedesktop.Platform",
|
||||
"runtime-version": "23.08",
|
||||
"sdk": "org.freedesktop.Sdk",
|
||||
"sdk-extensions" : [ "org.freedesktop.Sdk.Extension.llvm18" ],
|
||||
"finish-args": [
|
||||
"--share=network",
|
||||
"--socket=wayland",
|
||||
"--socket=fallback-x11",
|
||||
"--socket=pulseaudio",
|
||||
"--device=all",
|
||||
"--filesystem=host",
|
||||
"--filesystem=/media",
|
||||
"--filesystem=/run/media",
|
||||
"--filesystem=/mnt"
|
||||
],
|
||||
"modules": [
|
||||
{
|
||||
"name": "UnleashedRecomp",
|
||||
"buildsystem": "simple",
|
||||
"build-commands": [
|
||||
"cmake --preset linux-release -DSWA_FLATPAK=ON -DSDL2MIXER_VORBIS=VORBISFILE",
|
||||
"cmake --build out/build/linux-release",
|
||||
"mkdir -p /app/bin",
|
||||
"cp out/build/linux-release/UnleashedRecomp/SWA /app/bin/SWA",
|
||||
"install -Dm644 UnleashedRecompResources/images/game_icon.png /app/share/icons/hicolor/128x128/apps/${FLATPAK_ID}.png",
|
||||
"install -Dm644 flatpak/io.github.hedge_dev.unleashedrecomp.metainfo.xml /app/share/metainfo/${FLATPAK_ID}.metainfo.xml",
|
||||
"install -Dm644 flatpak/io.github.hedge_dev.unleashedrecomp.desktop /app/share/applications/${FLATPAK_ID}.desktop"
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"type": "dir",
|
||||
"path": "../"
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
"path": "default.xex",
|
||||
"dest": "UnleashedRecompLib/private"
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
"path": "shader.ar",
|
||||
"dest": "UnleashedRecompLib/private"
|
||||
}
|
||||
],
|
||||
"build-options": {
|
||||
"append-path": "/usr/lib/sdk/llvm18/bin",
|
||||
"prepend-ld-library-path": "/usr/lib/sdk/llvm18/lib",
|
||||
"build-args": [
|
||||
"--share=network"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
26
flatpak/io.github.hedge_dev.unleashedrecomp.metainfo.xml
Normal file
26
flatpak/io.github.hedge_dev.unleashedrecomp.metainfo.xml
Normal file
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<component type="desktop-application">
|
||||
<id>io.github.hedge_dev.unleashedrecomp</id>
|
||||
|
||||
<name>Unleashed Recompiled</name>
|
||||
<summary>Static recompilation of Sonic Unleashed.</summary>
|
||||
|
||||
<metadata_license>CC0-1.0</metadata_license>
|
||||
<project_license>GPL-3.0+</project_license>
|
||||
|
||||
<supports>
|
||||
<control>pointing</control>
|
||||
<control>keyboard</control>
|
||||
<control>touch</control>
|
||||
</supports>
|
||||
|
||||
<description>
|
||||
<p>
|
||||
A native PC port of Sonic Unleashed for Xbox 360 achieved through static recompilation.
|
||||
|
||||
https://github.com/hedge-dev/UnleashedRecomp
|
||||
</p>
|
||||
</description>
|
||||
|
||||
<launchable type="desktop-id">io.github.hedge_dev.unleashedrecomp.desktop</launchable>
|
||||
</component>
|
19
thirdparty/CMakeLists.txt
vendored
19
thirdparty/CMakeLists.txt
vendored
|
@ -1,7 +1,22 @@
|
|||
cmake_policy(SET CMP0077 NEW)
|
||||
|
||||
set(MSDF_ATLAS_BUILD_STANDALONE OFF)
|
||||
set(MSDF_ATLAS_USE_SKIA OFF)
|
||||
set(MSDF_ATLAS_NO_ARTERY_FONT ON)
|
||||
set(MSDFGEN_DISABLE_PNG ON)
|
||||
|
||||
add_subdirectory(${SWA_THIRDPARTY_ROOT}/o1heap)
|
||||
add_subdirectory(${SWA_THIRDPARTY_ROOT}/msdf-atlas-gen)
|
||||
set(SDL2MIXER_DEPS_SHARED OFF)
|
||||
set(SDL2MIXER_VENDORED ON)
|
||||
set(SDL2MIXER_FLAC OFF)
|
||||
set(SDL2MIXER_MOD OFF)
|
||||
set(SDL2MIXER_MP3 OFF)
|
||||
set(SDL2MIXER_MIDI OFF)
|
||||
set(SDL2MIXER_OPUS OFF)
|
||||
set(SDL2MIXER_VORBIS "VORBISFILE")
|
||||
set(SDL2MIXER_WAVPACK OFF)
|
||||
|
||||
add_subdirectory("${SWA_THIRDPARTY_ROOT}/msdf-atlas-gen")
|
||||
add_subdirectory("${SWA_THIRDPARTY_ROOT}/nativefiledialog-extended")
|
||||
add_subdirectory("${SWA_THIRDPARTY_ROOT}/o1heap")
|
||||
add_subdirectory("${SWA_THIRDPARTY_ROOT}/SDL")
|
||||
add_subdirectory("${SWA_THIRDPARTY_ROOT}/SDL_mixer")
|
||||
|
|
1
thirdparty/D3D12MemoryAllocator
vendored
Submodule
1
thirdparty/D3D12MemoryAllocator
vendored
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit e00c4a7c85cf9c28c6f4a6cc75032736f416410f
|
1
thirdparty/SDL
vendored
Submodule
1
thirdparty/SDL
vendored
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 1edaad17218d67b567c149badce9ef0fc67f65fa
|
1
thirdparty/SDL_mixer
vendored
Submodule
1
thirdparty/SDL_mixer
vendored
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 437992692cf9300f2b2f04be35adc7445a9055bf
|
1
thirdparty/Vulkan-Headers
vendored
Submodule
1
thirdparty/Vulkan-Headers
vendored
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 14345dab231912ee9601136e96ca67a6e1f632e7
|
1
thirdparty/VulkanMemoryAllocator
vendored
Submodule
1
thirdparty/VulkanMemoryAllocator
vendored
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 1c35ba99ce775f8342d87a83a3f0f696f99c2a39
|
1
thirdparty/concurrentqueue
vendored
Submodule
1
thirdparty/concurrentqueue
vendored
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 6dd38b8a1dbaa7863aa907045f32308a56a6ff5d
|
1
thirdparty/imgui
vendored
Submodule
1
thirdparty/imgui
vendored
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 8199457a7d9e453f8d3d9cadc14683fb54a858b5
|
1
thirdparty/magic_enum
vendored
Submodule
1
thirdparty/magic_enum
vendored
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 1a1824df7ac798177a521eed952720681b0bf482
|
1
thirdparty/miniaudio
vendored
1
thirdparty/miniaudio
vendored
|
@ -1 +0,0 @@
|
|||
Subproject commit 12a8d4e4911c5ab4f4c089b4d039433975ed8a66
|
1
thirdparty/nativefiledialog-extended
vendored
Submodule
1
thirdparty/nativefiledialog-extended
vendored
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 388549a5badaa7cbd138f5f189f50c67d5bf060c
|
1
thirdparty/stb
vendored
Submodule
1
thirdparty/stb
vendored
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 5c205738c191bcb0abc65c4febfa9bd25ff35234
|
1
thirdparty/tiny-AES-c
vendored
Submodule
1
thirdparty/tiny-AES-c
vendored
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 23856752fbd139da0b8ca6e471a13d5bcc99a08d
|
1
thirdparty/unordered_dense
vendored
Submodule
1
thirdparty/unordered_dense
vendored
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit d911053e390816ecc5dedd5a9d6b4bb5ed92b4c9
|
1
thirdparty/vcpkg
vendored
Submodule
1
thirdparty/vcpkg
vendored
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit b322364f06308bdd24823f9d8f03fe0cc86fd46f
|
1
thirdparty/volk
vendored
Submodule
1
thirdparty/volk
vendored
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 447e21b5d92ed8d5271b0d39b071f938fcfa875f
|
86
toolchains/linux-clang.cmake
Normal file
86
toolchains/linux-clang.cmake
Normal file
|
@ -0,0 +1,86 @@
|
|||
# Copied and adapted from:
|
||||
# https://github.com/microsoft/vcpkg/blob/7adc2e4d49e8d0efc07a369079faa6bc3dbb90f3/scripts/toolchains/linux.cmake
|
||||
#
|
||||
# The original seems to be written only for GCC. This version is changed to use clang and
|
||||
# LLVM bin tools.
|
||||
|
||||
if(NOT _VCPKG_LINUX_CLANG_TOOLCHAIN)
|
||||
set(_VCPKG_LINUX_CLANG_TOOLCHAIN 1)
|
||||
|
||||
if(POLICY CMP0056)
|
||||
cmake_policy(SET CMP0056 NEW)
|
||||
endif()
|
||||
if(POLICY CMP0066)
|
||||
cmake_policy(SET CMP0066 NEW)
|
||||
endif()
|
||||
if(POLICY CMP0067)
|
||||
cmake_policy(SET CMP0067 NEW)
|
||||
endif()
|
||||
if(POLICY CMP0137)
|
||||
cmake_policy(SET CMP0137 NEW)
|
||||
endif()
|
||||
list(APPEND CMAKE_TRY_COMPILE_PLATFORM_VARIABLES
|
||||
VCPKG_CRT_LINKAGE VCPKG_TARGET_ARCHITECTURE
|
||||
VCPKG_C_FLAGS VCPKG_CXX_FLAGS
|
||||
VCPKG_C_FLAGS_DEBUG VCPKG_CXX_FLAGS_DEBUG
|
||||
VCPKG_C_FLAGS_RELEASE VCPKG_CXX_FLAGS_RELEASE
|
||||
VCPKG_LINKER_FLAGS VCPKG_LINKER_FLAGS_RELEASE VCPKG_LINKER_FLAGS_DEBUG
|
||||
)
|
||||
|
||||
set(CMAKE_SYSTEM_NAME Linux CACHE STRING "")
|
||||
|
||||
# Set compiler to clang
|
||||
set(CMAKE_C_COMPILER clang)
|
||||
set(CMAKE_CXX_COMPILER clang++)
|
||||
SET(CMAKE_ASM_COMPILER clang)
|
||||
|
||||
# Pick target architecture for clang
|
||||
if(VCPKG_TARGET_ARCHITECTURE STREQUAL "x64")
|
||||
set(CMAKE_SYSTEM_PROCESSOR x86_64 CACHE STRING "")
|
||||
set(CLANG_TARGET x86_64-unknown-linux-gnu)
|
||||
elseif(VCPKG_TARGET_ARCHITECTURE STREQUAL "x86")
|
||||
set(CMAKE_SYSTEM_PROCESSOR i686 CACHE STRING "")
|
||||
set(CLANG_TARGET i686-unknown-linux-gnu)
|
||||
elseif(VCPKG_TARGET_ARCHITECTURE STREQUAL "arm")
|
||||
set(CMAKE_SYSTEM_PROCESSOR armv7l CACHE STRING "")
|
||||
set(CLANG_TARGET armv7-unknown-linux-gnueabihf)
|
||||
elseif(VCPKG_TARGET_ARCHITECTURE STREQUAL "arm64")
|
||||
set(CMAKE_SYSTEM_PROCESSOR aarch64 CACHE STRING "")
|
||||
set(CLANG_TARGET aarch64-unknown-linux-gnu)
|
||||
endif()
|
||||
if(DEFINED CLANG_TARGET)
|
||||
set(CMAKE_C_COMPILER_TARGET ${CLANG_TARGET})
|
||||
set(CMAKE_CXX_COMPILER_TARGET ${CLANG_TARGET})
|
||||
set(CMAKE_ASM_COMPILER_TARGET ${CLANG_TARGET})
|
||||
endif()
|
||||
|
||||
if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR STREQUAL CMAKE_HOST_SYSTEM_PROCESSOR)
|
||||
set(CMAKE_CROSSCOMPILING OFF CACHE BOOL "")
|
||||
endif()
|
||||
|
||||
string(APPEND CMAKE_C_FLAGS_INIT " -fPIC ${VCPKG_C_FLAGS} ")
|
||||
string(APPEND CMAKE_CXX_FLAGS_INIT " -fPIC ${VCPKG_CXX_FLAGS} ")
|
||||
string(APPEND CMAKE_C_FLAGS_DEBUG_INIT " ${VCPKG_C_FLAGS_DEBUG} ")
|
||||
string(APPEND CMAKE_CXX_FLAGS_DEBUG_INIT " ${VCPKG_CXX_FLAGS_DEBUG} ")
|
||||
string(APPEND CMAKE_C_FLAGS_RELEASE_INIT " ${VCPKG_C_FLAGS_RELEASE} ")
|
||||
string(APPEND CMAKE_CXX_FLAGS_RELEASE_INIT " ${VCPKG_CXX_FLAGS_RELEASE} ")
|
||||
|
||||
# Use LLVM's lld linker
|
||||
set(CMAKE_LINKER_TYPE LLD)
|
||||
|
||||
string(APPEND CMAKE_MODULE_LINKER_FLAGS_INIT " ${VCPKG_LINKER_FLAGS} ")
|
||||
string(APPEND CMAKE_SHARED_LINKER_FLAGS_INIT " ${VCPKG_LINKER_FLAGS} ")
|
||||
string(APPEND CMAKE_EXE_LINKER_FLAGS_INIT " ${VCPKG_LINKER_FLAGS} ")
|
||||
if(VCPKG_CRT_LINKAGE STREQUAL "static")
|
||||
string(APPEND CMAKE_MODULE_LINKER_FLAGS_INIT "-static ")
|
||||
string(APPEND CMAKE_SHARED_LINKER_FLAGS_INIT "-static ")
|
||||
string(APPEND CMAKE_EXE_LINKER_FLAGS_INIT "-static ")
|
||||
endif()
|
||||
string(APPEND CMAKE_MODULE_LINKER_FLAGS_DEBUG_INIT " ${VCPKG_LINKER_FLAGS_DEBUG} ")
|
||||
string(APPEND CMAKE_SHARED_LINKER_FLAGS_DEBUG_INIT " ${VCPKG_LINKER_FLAGS_DEBUG} ")
|
||||
string(APPEND CMAKE_EXE_LINKER_FLAGS_DEBUG_INIT " ${VCPKG_LINKER_FLAGS_DEBUG} ")
|
||||
string(APPEND CMAKE_MODULE_LINKER_FLAGS_RELEASE_INIT " ${VCPKG_LINKER_FLAGS_RELEASE} ")
|
||||
string(APPEND CMAKE_SHARED_LINKER_FLAGS_RELEASE_INIT " ${VCPKG_LINKER_FLAGS_RELEASE} ")
|
||||
string(APPEND CMAKE_EXE_LINKER_FLAGS_RELEASE_INIT " ${VCPKG_LINKER_FLAGS_RELEASE} ")
|
||||
string(APPEND CMAKE_ASM_FLAGS_INIT " ${VCPKG_C_FLAGS} ")
|
||||
endif()
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue