create Skate 3 recompilation project
This commit is contained in:
commit
62b65e74d8
14 changed files with 3075 additions and 0 deletions
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
* text=auto eol=lf
|
||||||
31
.gitignore
vendored
Normal file
31
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
# Build output and generated recompilation sources
|
||||||
|
/out/
|
||||||
|
/build/
|
||||||
|
/generated/
|
||||||
|
|
||||||
|
# Local game dump and retail media. Users must provide their own copy.
|
||||||
|
/game/
|
||||||
|
*.iso
|
||||||
|
*.xex
|
||||||
|
|
||||||
|
# Local runtime/config overrides
|
||||||
|
skate3.toml
|
||||||
|
CMakeUserPresets.json
|
||||||
|
|
||||||
|
# Diagnostics and binary build products
|
||||||
|
*.log
|
||||||
|
*.dmp
|
||||||
|
*.wpix
|
||||||
|
*_gpu_events.csv
|
||||||
|
*_gpu_screenshot.png
|
||||||
|
*.pdb
|
||||||
|
*.ilk
|
||||||
|
*.obj
|
||||||
|
*.lib
|
||||||
|
*.exe
|
||||||
|
*.map
|
||||||
|
|
||||||
|
# Editor and CMake scratch
|
||||||
|
/.vs/
|
||||||
|
/.vscode/
|
||||||
|
/.cache/
|
||||||
161
CMakeLists.txt
Normal file
161
CMakeLists.txt
Normal file
|
|
@ -0,0 +1,161 @@
|
||||||
|
cmake_minimum_required(VERSION 3.25)
|
||||||
|
|
||||||
|
project(skate3 VERSION 0.1.0 LANGUAGES CXX)
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
enable_language(RC)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 23)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
include(cmake/Skate3Version.cmake)
|
||||||
|
|
||||||
|
skate3_resolve_version(SKATE3_FULL_VERSION
|
||||||
|
FLOOR_VERSION ${PROJECT_VERSION}
|
||||||
|
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
string(REGEX REPLACE "-.*$" "" SKATE3_NUMERIC_VERSION "${SKATE3_FULL_VERSION}")
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
if(CMAKE_SYSTEM_PROCESSOR MATCHES "AMD64|x86_64")
|
||||||
|
set(SKATE3_BUILD_PLATFORM "win-amd64")
|
||||||
|
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|ARM64")
|
||||||
|
set(SKATE3_BUILD_PLATFORM "win-arm64")
|
||||||
|
else()
|
||||||
|
set(SKATE3_BUILD_PLATFORM "win-${CMAKE_SYSTEM_PROCESSOR}")
|
||||||
|
endif()
|
||||||
|
elseif(UNIX AND NOT APPLE)
|
||||||
|
if(CMAKE_SYSTEM_PROCESSOR MATCHES "AMD64|x86_64")
|
||||||
|
set(SKATE3_BUILD_PLATFORM "linux-amd64")
|
||||||
|
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|ARM64")
|
||||||
|
set(SKATE3_BUILD_PLATFORM "linux-arm64")
|
||||||
|
else()
|
||||||
|
set(SKATE3_BUILD_PLATFORM "linux-${CMAKE_SYSTEM_PROCESSOR}")
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
set(SKATE3_BUILD_PLATFORM "${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
string(TIMESTAMP SKATE3_BUILD_TIMESTAMP "%Y%m%d_%H%M")
|
||||||
|
configure_file(
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/skate3_version.h.in
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/include/skate3_version.h
|
||||||
|
@ONLY
|
||||||
|
)
|
||||||
|
|
||||||
|
set(REXSDK_DIR
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/third_party/rexglue-sdk"
|
||||||
|
CACHE PATH "Path to the rexglue SDK source tree")
|
||||||
|
set(SKATE3_GAME_DATA_ROOT
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/game"
|
||||||
|
CACHE PATH "Path to an extracted Skate 3 retail game dump")
|
||||||
|
|
||||||
|
if(CMAKE_SYSTEM_PROCESSOR MATCHES "AMD64|x86_64")
|
||||||
|
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
|
||||||
|
add_compile_options(-march=x86-64-v3)
|
||||||
|
set(SNAPPY_REQUIRE_AVX2 ON CACHE BOOL
|
||||||
|
"Match Snappy feature detection to the project x86-64-v3 target" FORCE)
|
||||||
|
set(SNAPPY_HAVE_SSSE3 1 CACHE INTERNAL
|
||||||
|
"x86-64-v3 requires SSSE3; Snappy checks do not see directory compile options" FORCE)
|
||||||
|
set(SNAPPY_HAVE_X86_CRC32 1 CACHE INTERNAL
|
||||||
|
"x86-64-v3 requires SSE4.2 CRC32; Snappy checks do not see directory compile options" FORCE)
|
||||||
|
set(SNAPPY_HAVE_BMI2 1 CACHE INTERNAL
|
||||||
|
"x86-64-v3 requires BMI2; Snappy checks do not see directory compile options" FORCE)
|
||||||
|
elseif(MSVC)
|
||||||
|
add_compile_options(/arch:AVX2)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
file(TO_CMAKE_PATH "${CMAKE_CURRENT_SOURCE_DIR}" SKATE3_SOURCE_DIR_CONFIG)
|
||||||
|
file(TO_CMAKE_PATH "${SKATE3_GAME_DATA_ROOT}" SKATE3_GAME_DATA_ROOT_CONFIG)
|
||||||
|
|
||||||
|
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/manifests")
|
||||||
|
|
||||||
|
configure_file(
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/manifests/skate3.toml.in
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/manifests/skate3.toml
|
||||||
|
@ONLY
|
||||||
|
)
|
||||||
|
configure_file(
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/manifests/eawebkit.toml.in
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/manifests/eawebkit.toml
|
||||||
|
@ONLY
|
||||||
|
)
|
||||||
|
set(REXGLUE_ENABLE_TRACY OFF CACHE BOOL "Disable Tracy instrumentation for normal Skate 3 builds" FORCE)
|
||||||
|
set(REXGLUE_ENABLE_PERF_COUNTERS OFF CACHE BOOL "Enable lightweight rexglue perf counter instrumentation" FORCE)
|
||||||
|
|
||||||
|
include(cmake/RexGlue.cmake)
|
||||||
|
include(cmake/CodegenTargets.cmake)
|
||||||
|
|
||||||
|
set(SKATE3_COMMON_SOURCES
|
||||||
|
src/exception_compat.cpp
|
||||||
|
src/skate3_app_common.cpp
|
||||||
|
src/skate3_iso_installer.cpp
|
||||||
|
src/skate3_user_settings.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
set(SKATE3_SOURCES
|
||||||
|
src/main.cpp
|
||||||
|
${SKATE3_COMMON_SOURCES}
|
||||||
|
)
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
add_executable(skate3 WIN32 ${SKATE3_SOURCES})
|
||||||
|
configure_file(
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/skate3_app.manifest
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/skate3_app.manifest
|
||||||
|
COPYONLY
|
||||||
|
)
|
||||||
|
target_link_options(skate3 PRIVATE LINKER:/MANIFESTINPUT:skate3_app.manifest)
|
||||||
|
else()
|
||||||
|
add_executable(skate3 ${SKATE3_SOURCES})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_include_directories(skate3 PRIVATE
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/include
|
||||||
|
)
|
||||||
|
target_compile_definitions(skate3 PRIVATE SKATE3_BUILD_CONFIG="$<CONFIG>")
|
||||||
|
|
||||||
|
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/generated/sources.cmake")
|
||||||
|
include(generated/sources.cmake)
|
||||||
|
target_sources(skate3 PRIVATE ${GENERATED_SOURCES})
|
||||||
|
target_include_directories(skate3 PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/generated)
|
||||||
|
else()
|
||||||
|
message(STATUS "generated/sources.cmake not found; run the generate-all target before building skate3")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/generated/eawebkit/sources.cmake")
|
||||||
|
set(_SKATE3_SAVED_GENERATED_SOURCES ${GENERATED_SOURCES})
|
||||||
|
include(generated/eawebkit/sources.cmake)
|
||||||
|
target_sources(skate3 PRIVATE ${GENERATED_SOURCES})
|
||||||
|
target_include_directories(skate3 PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/generated/eawebkit)
|
||||||
|
set(GENERATED_SOURCES ${_SKATE3_SAVED_GENERATED_SOURCES})
|
||||||
|
unset(_SKATE3_SAVED_GENERATED_SOURCES)
|
||||||
|
else()
|
||||||
|
message(STATUS "generated/eawebkit/sources.cmake not found; run the generate-all target before building skate3")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_link_libraries(skate3 PRIVATE rex::runtime)
|
||||||
|
rexglue_configure_target(skate3)
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
target_link_libraries(skate3 PRIVATE comdlg32)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_custom_target(skate3_stage_runtime_library
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E make_directory "$<TARGET_FILE_DIR:skate3>"
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||||
|
"$<TARGET_FILE:rexruntime>"
|
||||||
|
"$<TARGET_FILE_DIR:skate3>"
|
||||||
|
DEPENDS rexruntime
|
||||||
|
VERBATIM
|
||||||
|
)
|
||||||
|
add_dependencies(skate3 skate3_stage_runtime_library)
|
||||||
|
|
||||||
|
if(MSVC)
|
||||||
|
target_compile_options(skate3 PRIVATE /utf-8)
|
||||||
|
else()
|
||||||
|
target_compile_options(skate3 PRIVATE -finput-charset=UTF-8 -fexec-charset=UTF-8)
|
||||||
|
endif()
|
||||||
97
CMakePresets.json
Normal file
97
CMakePresets.json
Normal file
|
|
@ -0,0 +1,97 @@
|
||||||
|
{
|
||||||
|
"version": 6,
|
||||||
|
"cmakeMinimumRequired": {
|
||||||
|
"major": 3,
|
||||||
|
"minor": 25,
|
||||||
|
"patch": 0
|
||||||
|
},
|
||||||
|
"configurePresets": [
|
||||||
|
{
|
||||||
|
"name": "base",
|
||||||
|
"hidden": true,
|
||||||
|
"generator": "Ninja",
|
||||||
|
"binaryDir": "${sourceDir}/out/build/${presetName}",
|
||||||
|
"cacheVariables": {
|
||||||
|
"REXSDK_DIR": "${sourceDir}/third_party/rexglue-sdk",
|
||||||
|
"SKATE3_GAME_DATA_ROOT": "${sourceDir}/game"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "linux-base",
|
||||||
|
"hidden": true,
|
||||||
|
"inherits": "base",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_C_COMPILER": "clang-20",
|
||||||
|
"CMAKE_CXX_COMPILER": "clang++-20",
|
||||||
|
"CMAKE_LINKER": "ld.lld-20"
|
||||||
|
},
|
||||||
|
"condition": {
|
||||||
|
"type": "equals",
|
||||||
|
"lhs": "${hostSystemName}",
|
||||||
|
"rhs": "Linux"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "debug",
|
||||||
|
"displayName": "Debug",
|
||||||
|
"inherits": "base",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_BUILD_TYPE": "Debug"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "release",
|
||||||
|
"displayName": "Release",
|
||||||
|
"inherits": "base",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_BUILD_TYPE": "Release"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "relwithdebinfo",
|
||||||
|
"displayName": "RelWithDebInfo",
|
||||||
|
"inherits": "base",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_BUILD_TYPE": "RelWithDebInfo"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"buildPresets": [
|
||||||
|
{
|
||||||
|
"name": "debug",
|
||||||
|
"configurePreset": "debug"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "release",
|
||||||
|
"configurePreset": "release"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "relwithdebinfo",
|
||||||
|
"configurePreset": "relwithdebinfo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "linux-relwithdebinfo",
|
||||||
|
"configurePreset": "linux-relwithdebinfo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "linux-release",
|
||||||
|
"configurePreset": "linux-release"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
25
cmake/CodegenTargets.cmake
Normal file
25
cmake/CodegenTargets.cmake
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
add_custom_target(generate-skate3
|
||||||
|
COMMAND $<TARGET_FILE:rex::rexglue> codegen
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/manifests/skate3.toml"
|
||||||
|
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||||
|
COMMENT "Generating recompiled code for default.xex"
|
||||||
|
VERBATIM
|
||||||
|
)
|
||||||
|
|
||||||
|
add_custom_target(generate-eawebkit
|
||||||
|
COMMAND $<TARGET_FILE:rex::rexglue> codegen
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/manifests/eawebkit.toml"
|
||||||
|
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||||
|
COMMENT "Generating recompiled code for EAWebkit.xex"
|
||||||
|
VERBATIM
|
||||||
|
)
|
||||||
|
|
||||||
|
add_dependencies(generate-skate3 generate-eawebkit)
|
||||||
|
|
||||||
|
add_custom_target(generate-all
|
||||||
|
DEPENDS generate-skate3 generate-eawebkit
|
||||||
|
)
|
||||||
|
|
||||||
|
add_custom_target(skate3_codegen DEPENDS generate-skate3)
|
||||||
|
add_custom_target(eawebkit_codegen DEPENDS generate-eawebkit)
|
||||||
|
add_custom_target(all_codegen DEPENDS generate-all)
|
||||||
145
config/eawebkit_functions.toml
Normal file
145
config/eawebkit_functions.toml
Normal file
|
|
@ -0,0 +1,145 @@
|
||||||
|
# Function boundary overrides for EAWebkit codegen.
|
||||||
|
# Entries define address-taken functions and interior landing points needed by generated code.
|
||||||
|
|
||||||
|
[functions]
|
||||||
|
|
||||||
|
"0x881715C8" = { end = 0x88171F44 }
|
||||||
|
|
||||||
|
"0x880FA358" = {}
|
||||||
|
"0x88104580" = {}
|
||||||
|
"0x88104B70" = {}
|
||||||
|
"0x8810E318" = {}
|
||||||
|
"0x8810FBF8" = {}
|
||||||
|
"0x88115080" = {}
|
||||||
|
"0x881150B8" = {}
|
||||||
|
"0x881154C8" = {}
|
||||||
|
"0x881159C0" = {}
|
||||||
|
"0x88115B88" = {}
|
||||||
|
"0x88121060" = {}
|
||||||
|
"0x88121088" = {}
|
||||||
|
"0x88121090" = {}
|
||||||
|
"0x88121358" = {}
|
||||||
|
"0x8812D0D8" = {}
|
||||||
|
"0x8812D0E0" = {}
|
||||||
|
"0x88177670" = {}
|
||||||
|
"0x88177680" = {}
|
||||||
|
"0x88177690" = {}
|
||||||
|
"0x881891A0" = {}
|
||||||
|
"0x881FECA0" = {}
|
||||||
|
"0x882177A0" = {}
|
||||||
|
"0x88236C00" = {}
|
||||||
|
"0x8826F478" = {}
|
||||||
|
"0x882737C8" = {}
|
||||||
|
"0x88346DC8" = {}
|
||||||
|
"0x8834A9A0" = {}
|
||||||
|
"0x8834F540" = {}
|
||||||
|
"0x8835BF48" = {}
|
||||||
|
"0x88369810" = {}
|
||||||
|
"0x88375488" = {}
|
||||||
|
"0x88380BF8" = {}
|
||||||
|
"0x883A5128" = {}
|
||||||
|
"0x883A5178" = {}
|
||||||
|
"0x883EBBC0" = {}
|
||||||
|
"0x883EBBD0" = {}
|
||||||
|
"0x88404DA0" = {}
|
||||||
|
"0x8843C140" = {}
|
||||||
|
"0x88444098" = {}
|
||||||
|
"0x884440C0" = {}
|
||||||
|
"0x8844AA28" = {}
|
||||||
|
"0x8844AA30" = {}
|
||||||
|
"0x8844AA38" = {}
|
||||||
|
"0x8844AA40" = {}
|
||||||
|
"0x88462048" = {}
|
||||||
|
"0x884655D0" = {}
|
||||||
|
"0x88465610" = {}
|
||||||
|
"0x88465F20" = {}
|
||||||
|
"0x88476088" = { end = 0x8847608C, parent = 0x88476038 }
|
||||||
|
"0x88476138" = { end = 0x8847613C, parent = 0x88476038 }
|
||||||
|
"0x88476148" = { end = 0x8847614C, parent = 0x88476038 }
|
||||||
|
"0x884762D4" = { end = 0x884762D8, parent = 0x88476250 }
|
||||||
|
"0x884762E0" = { end = 0x884762E4, parent = 0x88476250 }
|
||||||
|
"0x88476400" = { end = 0x88476404, parent = 0x88476368 }
|
||||||
|
"0x88476508" = { end = 0x8847650C, parent = 0x88476368 }
|
||||||
|
"0x88476600" = { end = 0x88476604, parent = 0x88476558 }
|
||||||
|
"0x88476650" = { end = 0x88476654, parent = 0x88476558 }
|
||||||
|
"0x884776DC" = { end = 0x884776E0, parent = 0x884776C0 }
|
||||||
|
"0x8847778C" = { end = 0x88477790, parent = 0x884776C0 }
|
||||||
|
"0x88477E18" = { end = 0x88477E1C, parent = 0x88477D80 }
|
||||||
|
"0x88477E34" = { end = 0x88477E38, parent = 0x88477D80 }
|
||||||
|
"0x88477E04" = { end = 0x88477E08, parent = 0x88477D80 }
|
||||||
|
"0x884781B4" = { end = 0x884781B8, parent = 0x88478138 }
|
||||||
|
"0x884781CC" = { end = 0x884781D0, parent = 0x88478138 }
|
||||||
|
"0x884781A0" = { end = 0x884781A4, parent = 0x88478138 }
|
||||||
|
"0x88478384" = { end = 0x88478388, parent = 0x88478308 }
|
||||||
|
"0x88478398" = { end = 0x8847839C, parent = 0x88478308 }
|
||||||
|
"0x8847D5C8" = { end = 0x8847D5CC, parent = 0x8847D5A0 }
|
||||||
|
"0x8847D6DC" = { end = 0x8847D6E0, parent = 0x8847D5A0 }
|
||||||
|
"0x8847E504" = { end = 0x8847E508, parent = 0x8847E438 }
|
||||||
|
"0x8847E53C" = { end = 0x8847E540, parent = 0x8847E438 }
|
||||||
|
"0x8847E7B8" = { end = 0x8847E7BC, parent = 0x8847E788 }
|
||||||
|
"0x8847E7C8" = { end = 0x8847E7CC, parent = 0x8847E788 }
|
||||||
|
"0x8847E80C" = { end = 0x8847E810, parent = 0x8847E788 }
|
||||||
|
"0x8847E86C" = { end = 0x8847E870, parent = 0x8847E788 }
|
||||||
|
"0x8847E884" = { end = 0x8847E888, parent = 0x8847E788 }
|
||||||
|
"0x8847FBB8" = { end = 0x8847FBBC, parent = 0x8847FB90 }
|
||||||
|
"0x8847FD04" = { end = 0x8847FD08, parent = 0x8847FB90 }
|
||||||
|
"0x88481BAC" = { end = 0x88481BB0, parent = 0x88481AA8 }
|
||||||
|
"0x88481BF4" = { end = 0x88481BF8, parent = 0x88481AA8 }
|
||||||
|
"0x8848224C" = { end = 0x88482250, parent = 0x88482178 }
|
||||||
|
"0x88482298" = { end = 0x8848229C, parent = 0x88482178 }
|
||||||
|
"0x8848249C" = { end = 0x884824A0, parent = 0x884823C8 }
|
||||||
|
"0x884824E8" = { end = 0x884824EC, parent = 0x884823C8 }
|
||||||
|
"0x884827FC" = { end = 0x88482800, parent = 0x88482798 }
|
||||||
|
"0x88482808" = { end = 0x8848280C, parent = 0x88482798 }
|
||||||
|
"0x884847C4" = { end = 0x884847C8, parent = 0x88484798 }
|
||||||
|
"0x88484928" = { end = 0x8848492C, parent = 0x88484798 }
|
||||||
|
"0x88484EF4" = { end = 0x88484EF8, parent = 0x88484EC0 }
|
||||||
|
"0x88484F10" = { end = 0x88484F14, parent = 0x88484EC0 }
|
||||||
|
"0x88484F88" = { end = 0x88484F8C, parent = 0x88484F60 }
|
||||||
|
"0x88484F94" = { end = 0x88484F98, parent = 0x88484F60 }
|
||||||
|
"0x88487038" = { end = 0x8848703C, parent = 0x88486EA8 }
|
||||||
|
"0x884870C8" = { end = 0x884870CC, parent = 0x88486EA8 }
|
||||||
|
"0x884872F4" = { end = 0x884872F8, parent = 0x884872D0 }
|
||||||
|
"0x88487378" = { end = 0x8848737C, parent = 0x884872D0 }
|
||||||
|
"0x88487558" = { end = 0x8848755C, parent = 0x884874C8 }
|
||||||
|
"0x884875A4" = { end = 0x884875A8, parent = 0x884874C8 }
|
||||||
|
"0x88487774" = { end = 0x88487778, parent = 0x88487748 }
|
||||||
|
"0x8848777C" = { end = 0x88487780, parent = 0x88487748 }
|
||||||
|
"0x88487788" = { end = 0x8848778C, parent = 0x88487748 }
|
||||||
|
"0x884877F0" = { end = 0x884877F4, parent = 0x884877D0 }
|
||||||
|
"0x884877FC" = { end = 0x88487800, parent = 0x884877D0 }
|
||||||
|
"0x8848780C" = { end = 0x88487810, parent = 0x884877D0 }
|
||||||
|
"0x884882B8" = { end = 0x884882BC, parent = 0x88488290 }
|
||||||
|
"0x88488314" = { end = 0x88488318, parent = 0x88488290 }
|
||||||
|
"0x8848860C" = { end = 0x88488610, parent = 0x884885B8 }
|
||||||
|
"0x88488640" = { end = 0x88488644, parent = 0x884885B8 }
|
||||||
|
"0x8848872C" = { end = 0x88488730, parent = 0x884886E8 }
|
||||||
|
"0x8848873C" = { end = 0x88488740, parent = 0x884886E8 }
|
||||||
|
"0x88488758" = { end = 0x8848875C, parent = 0x884886E8 }
|
||||||
|
"0x88488794" = { end = 0x88488798, parent = 0x884886E8 }
|
||||||
|
"0x884887C8" = { end = 0x884887CC, parent = 0x884886E8 }
|
||||||
|
"0x884888E4" = { end = 0x884888E8, parent = 0x884886E8 }
|
||||||
|
"0x88488A1C" = { end = 0x88488A20, parent = 0x88488968 }
|
||||||
|
"0x88488A7C" = { end = 0x88488A80, parent = 0x88488968 }
|
||||||
|
"0x8848921C" = { end = 0x88489220, parent = 0x88489188 }
|
||||||
|
"0x8848923C" = { end = 0x88489240, parent = 0x88489188 }
|
||||||
|
"0x88489B34" = { end = 0x88489B38, parent = 0x88489A58 }
|
||||||
|
"0x88489B7C" = { end = 0x88489B80, parent = 0x88489A58 }
|
||||||
|
"0x88489FA0" = { end = 0x88489FA4, parent = 0x88489F40 }
|
||||||
|
"0x88489FB0" = { end = 0x88489FB4, parent = 0x88489F40 }
|
||||||
|
"0x8848B878" = { end = 0x8848B87C, parent = 0x8848B300 }
|
||||||
|
"0x8848F838" = { end = 0x8848F83C, parent = 0x8848F7D0 }
|
||||||
|
"0x8848F858" = { end = 0x8848F85C, parent = 0x8848F7D0 }
|
||||||
|
"0x8848F868" = { end = 0x8848F86C, parent = 0x8848F7D0 }
|
||||||
|
"0x8848FE38" = { end = 0x8848FE3C, parent = 0x8848FDA0 }
|
||||||
|
"0x884905D4" = { end = 0x884905D8, parent = 0x8848FDA0 }
|
||||||
|
"0x884906EC" = { end = 0x884906F0, parent = 0x88490668 }
|
||||||
|
"0x884908CC" = { end = 0x884908D0, parent = 0x88490668 }
|
||||||
|
"0x88490A54" = { end = 0x88490A58, parent = 0x88490960 }
|
||||||
|
"0x88490A7C" = { end = 0x88490A80, parent = 0x88490960 }
|
||||||
|
"0x88490FDC" = { end = 0x88490FE0, parent = 0x88490960 }
|
||||||
|
"0x88490FFC" = { end = 0x88491000, parent = 0x88490960 }
|
||||||
|
"0x8849100C" = { end = 0x88491010, parent = 0x88490960 }
|
||||||
|
"0x88491174" = { end = 0x88491178, parent = 0x88490960 }
|
||||||
|
"0x88491184" = { end = 0x88491188, parent = 0x88490960 }
|
||||||
|
"0x88491190" = { end = 0x88491194, parent = 0x88490960 }
|
||||||
1776
config/skate3_functions.toml
Normal file
1776
config/skate3_functions.toml
Normal file
File diff suppressed because it is too large
Load diff
12
manifests/eawebkit.toml.in
Normal file
12
manifests/eawebkit.toml.in
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
[project]
|
||||||
|
sdk_version = "0.8.1.19"
|
||||||
|
name = "eawebkit"
|
||||||
|
|
||||||
|
[entrypoint]
|
||||||
|
file_path = "@SKATE3_GAME_DATA_ROOT_CONFIG@/data/webkit/EAWebkit.xex"
|
||||||
|
out_directory_path = "@SKATE3_SOURCE_DIR_CONFIG@/generated/eawebkit"
|
||||||
|
includes = [
|
||||||
|
"@SKATE3_SOURCE_DIR_CONFIG@/config/eawebkit_functions.toml",
|
||||||
|
]
|
||||||
|
|
||||||
|
symbol_prefix = "eawebkit_"
|
||||||
10
manifests/skate3.toml.in
Normal file
10
manifests/skate3.toml.in
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
[project]
|
||||||
|
sdk_version = "0.8.1.19"
|
||||||
|
name = "skate3"
|
||||||
|
|
||||||
|
[entrypoint]
|
||||||
|
file_path = "@SKATE3_GAME_DATA_ROOT_CONFIG@/default.xex"
|
||||||
|
out_directory_path = "@SKATE3_SOURCE_DIR_CONFIG@/generated"
|
||||||
|
includes = [
|
||||||
|
"@SKATE3_SOURCE_DIR_CONFIG@/config/skate3_functions.toml",
|
||||||
|
]
|
||||||
16
src/exception_compat.cpp
Normal file
16
src/exception_compat.cpp
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
#include "skate3_init.h"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
extern "C" REX_FUNC(__imp__sub_82F44E40);
|
||||||
|
|
||||||
|
extern "C" REX_FUNC(sub_82F44E40) {
|
||||||
|
constexpr uint32_t kExceptionTrampoline = 0x83092CC0u;
|
||||||
|
const uint32_t trampoline = REX_LOAD_U32(kExceptionTrampoline);
|
||||||
|
if (trampoline == 0u) {
|
||||||
|
ctx.r3.u64 = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
__imp__sub_82F44E40(ctx, base);
|
||||||
|
}
|
||||||
34
src/main.cpp
Normal file
34
src/main.cpp
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
// skate3 - ReXGlue Recompiled Project
|
||||||
|
//
|
||||||
|
// This file is yours to edit. 'rexglue migrate' will NOT overwrite it.
|
||||||
|
|
||||||
|
#include "skate3_app_common.h"
|
||||||
|
#include <skate3_version.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
class Skate3PureApp : public Skate3BaseApp {
|
||||||
|
public:
|
||||||
|
using Skate3BaseApp::Skate3BaseApp;
|
||||||
|
|
||||||
|
std::string_view GetBuildTitle() const override {
|
||||||
|
return SKATE3_BUILD_TITLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string_view GetBuildStamp() const override {
|
||||||
|
return SKATE3_BUILD_STAMP;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetWindowTitle() const override {
|
||||||
|
return "Skate 3 " SKATE3_BUILD_TITLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::unique_ptr<rex::ui::WindowedApp> Create(
|
||||||
|
rex::ui::WindowedAppContext& ctx) {
|
||||||
|
return std::unique_ptr<Skate3PureApp>(
|
||||||
|
new Skate3PureApp(ctx, "skate3", skate3_PPCImageConfig));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
REX_DEFINE_APP(skate3, Skate3PureApp::Create)
|
||||||
22
src/skate3_app.manifest
Normal file
22
src/skate3_app.manifest
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" manifestVersion="1.0">
|
||||||
|
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||||
|
<application>
|
||||||
|
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
|
||||||
|
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
|
||||||
|
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
|
||||||
|
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
||||||
|
</application>
|
||||||
|
</compatibility>
|
||||||
|
<dependency>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*" />
|
||||||
|
</dependentAssembly>
|
||||||
|
</dependency>
|
||||||
|
<asmv3:application>
|
||||||
|
<asmv3:windowsSettings>
|
||||||
|
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">True/PM</dpiAware>
|
||||||
|
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2,PerMonitor</dpiAwareness>
|
||||||
|
</asmv3:windowsSettings>
|
||||||
|
</asmv3:application>
|
||||||
|
</assembly>
|
||||||
684
src/skate3_app_common.cpp
Normal file
684
src/skate3_app_common.cpp
Normal file
|
|
@ -0,0 +1,684 @@
|
||||||
|
#include "skate3_app_common.h"
|
||||||
|
|
||||||
|
#include "skate3_iso_installer.h"
|
||||||
|
#include "skate3_user_settings.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <chrono>
|
||||||
|
#include <cstring>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <ctime>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <memory>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#ifndef WIN32_LEAN_AND_MEAN
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#endif
|
||||||
|
#ifndef NOMINMAX
|
||||||
|
#define NOMINMAX
|
||||||
|
#endif
|
||||||
|
#include <Windows.h>
|
||||||
|
#elif defined(__linux__)
|
||||||
|
#include <spawn.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <rex/cvar.h>
|
||||||
|
#include <rex/filesystem.h>
|
||||||
|
#include <rex/filesystem/devices/host_path_device.h>
|
||||||
|
#include <rex/filesystem/vfs.h>
|
||||||
|
#include <rex/graphics/graphics_system.h>
|
||||||
|
#include <rex/input/input_system.h>
|
||||||
|
#include <rex/kernel/xam/module.h>
|
||||||
|
#include <rex/logging.h>
|
||||||
|
#include <rex/perf/counter.h>
|
||||||
|
#include <rex/ppc/context.h>
|
||||||
|
#include <rex/system/function_dispatcher.h>
|
||||||
|
#include <rex/system/kernel_state.h>
|
||||||
|
#include <rex/system/xam/user_profile.h>
|
||||||
|
#include <rex/system.h>
|
||||||
|
#include <rex/ui/keybinds.h>
|
||||||
|
#include <rex/ui/overlay/simple_settings_overlay.h>
|
||||||
|
|
||||||
|
#include <imgui.h>
|
||||||
|
#include <toml++/toml.hpp>
|
||||||
|
|
||||||
|
#if defined(__linux__)
|
||||||
|
extern char** environ;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern const rex::PPCImageInfo eawebkit_PPCImageConfig;
|
||||||
|
|
||||||
|
// Register multi-entry-function alternate entries that rex's analyzer can't
|
||||||
|
// lift via config because they overlap with auto-discovered parent functions.
|
||||||
|
extern "C" REX_FUNC(__restgprlr_19);
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
#if defined(__linux__)
|
||||||
|
std::vector<std::string> CurrentProcessArgumentsForRestart(
|
||||||
|
const std::filesystem::path& executable_path) {
|
||||||
|
std::vector<std::string> args;
|
||||||
|
std::ifstream cmdline("/proc/self/cmdline", std::ios::binary);
|
||||||
|
std::string arg;
|
||||||
|
char ch = 0;
|
||||||
|
while (cmdline.get(ch)) {
|
||||||
|
if (ch == '\0') {
|
||||||
|
args.push_back(std::move(arg));
|
||||||
|
arg.clear();
|
||||||
|
} else {
|
||||||
|
arg.push_back(ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!arg.empty()) {
|
||||||
|
args.push_back(std::move(arg));
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string executable = rex::path_to_utf8(executable_path);
|
||||||
|
if (args.empty()) {
|
||||||
|
args.push_back(executable);
|
||||||
|
} else {
|
||||||
|
args[0] = executable;
|
||||||
|
}
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
constexpr std::string_view kUserDirectoryName = "skate3";
|
||||||
|
constexpr std::string_view kSettingsFilename = "settings.toml";
|
||||||
|
|
||||||
|
std::filesystem::path DefaultDocumentsUserRoot() {
|
||||||
|
return rex::filesystem::GetUserFolder() / std::string(kUserDirectoryName);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::filesystem::path DefaultRoamingUserRoot() {
|
||||||
|
#if defined(_WIN32)
|
||||||
|
char* appdata = nullptr;
|
||||||
|
size_t appdata_size = 0;
|
||||||
|
if (_dupenv_s(&appdata, &appdata_size, "APPDATA") == 0 && appdata && *appdata) {
|
||||||
|
std::filesystem::path result = std::filesystem::path(appdata) / std::string(kUserDirectoryName);
|
||||||
|
std::free(appdata);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
std::free(appdata);
|
||||||
|
#endif
|
||||||
|
return DefaultDocumentsUserRoot();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::filesystem::path ResolveSkate3UserRoot(const rex::PathConfig& paths) {
|
||||||
|
const auto executable_root = rex::filesystem::GetExecutableFolder();
|
||||||
|
if (std::filesystem::exists(executable_root / "portable.txt")) {
|
||||||
|
return executable_root;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto old_default = DefaultDocumentsUserRoot();
|
||||||
|
if (!paths.user_data_root.empty() && paths.user_data_root != old_default) {
|
||||||
|
return paths.user_data_root;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DefaultRoamingUserRoot();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigureSkate3UserPaths(rex::PathConfig& paths, std::filesystem::path& settings_path,
|
||||||
|
std::filesystem::path& profiles_path) {
|
||||||
|
const auto old_user_root = DefaultDocumentsUserRoot();
|
||||||
|
const auto old_cache_root = old_user_root / "cache";
|
||||||
|
const auto original_cache_root = paths.cache_root;
|
||||||
|
const auto resolved_user_root = ResolveSkate3UserRoot(paths);
|
||||||
|
|
||||||
|
paths.user_data_root = resolved_user_root;
|
||||||
|
if (original_cache_root.empty() || original_cache_root == old_cache_root) {
|
||||||
|
paths.cache_root = resolved_user_root / "cache";
|
||||||
|
}
|
||||||
|
settings_path = resolved_user_root / std::string(kSettingsFilename);
|
||||||
|
profiles_path = skate3::ProfilesFilePath(resolved_user_root);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConfigContainsAnyKey(const toml::table& table, std::initializer_list<std::string_view> keys) {
|
||||||
|
for (std::string_view key : keys) {
|
||||||
|
if (table.contains(key)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DeveloperConfigHasResolutionScaleOverride(const std::filesystem::path& config_path) {
|
||||||
|
if (config_path.empty() || !std::filesystem::exists(config_path)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
auto config = toml::parse_file(config_path.string());
|
||||||
|
return ConfigContainsAnyKey(config, {"resolution_scale", "draw_resolution_scale_x",
|
||||||
|
"draw_resolution_scale_y"});
|
||||||
|
} catch (const toml::parse_error&) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr int kDefaultResolutionScale = 2;
|
||||||
|
|
||||||
|
void ApplyFirstRunVideoDefaults(const std::filesystem::path& settings_path,
|
||||||
|
const std::filesystem::path& developer_config_path) {
|
||||||
|
if (std::filesystem::exists(settings_path) ||
|
||||||
|
DeveloperConfigHasResolutionScaleOverride(developer_config_path) ||
|
||||||
|
rex::cvar::HasNonDefaultValue("resolution_scale") ||
|
||||||
|
rex::cvar::HasNonDefaultValue("draw_resolution_scale_x") ||
|
||||||
|
rex::cvar::HasNonDefaultValue("draw_resolution_scale_y")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto scale = std::to_string(kDefaultResolutionScale);
|
||||||
|
rex::cvar::SetFlagByName("resolution_scale", scale);
|
||||||
|
rex::cvar::SetFlagByName("draw_resolution_scale_x", scale);
|
||||||
|
rex::cvar::SetFlagByName("draw_resolution_scale_y", scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LoadAndNormalizeSimpleSettings(const std::filesystem::path& settings_path,
|
||||||
|
const std::filesystem::path& developer_config_path) {
|
||||||
|
if (std::filesystem::exists(settings_path)) {
|
||||||
|
rex::cvar::LoadConfig(settings_path);
|
||||||
|
} else {
|
||||||
|
ApplyFirstRunVideoDefaults(settings_path, developer_config_path);
|
||||||
|
}
|
||||||
|
rex::ui::EnsureSimpleSettingsConfig(settings_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* FirstExistingFontPath(std::initializer_list<const char*> paths) {
|
||||||
|
for (const char* path : paths) {
|
||||||
|
if (std::filesystem::exists(path)) {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
// sub_82EBAE4C: alternate entry into sub_82EBAE34 (37 vtable refs in .data).
|
||||||
|
// Body: lwz r3, 0x164(r31); addi r1, r31, 0x140; b __restgprlr_19
|
||||||
|
static void Sub82EBAE4CImpl(PPCContext& ctx, uint8_t* base) {
|
||||||
|
ctx.r3.u64 = REX_LOAD_U32(ctx.r31.u32 + 0x164);
|
||||||
|
ctx.r1.s64 = ctx.r31.s64 + 0x140;
|
||||||
|
__restgprlr_19(ctx, base);
|
||||||
|
}
|
||||||
|
|
||||||
|
Skate3BaseApp::~Skate3BaseApp() = default;
|
||||||
|
|
||||||
|
void Skate3BaseApp::OnConfigurePaths(rex::PathConfig& paths) {
|
||||||
|
ConfigureSkate3UserPaths(paths, user_settings_path_, profiles_path_);
|
||||||
|
config_path_ = paths.config_path;
|
||||||
|
LoadAndNormalizeSimpleSettings(user_settings_path_, config_path_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Skate3BaseApp::OnConfigureFonts(ImFontAtlas* atlas) {
|
||||||
|
if (!atlas) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
atlas->Clear();
|
||||||
|
ImFontConfig font_config;
|
||||||
|
font_config.OversampleH = 2;
|
||||||
|
font_config.OversampleV = 2;
|
||||||
|
font_config.PixelSnapH = false;
|
||||||
|
|
||||||
|
const char* base_font_path = FirstExistingFontPath({
|
||||||
|
#if defined(_WIN32)
|
||||||
|
"C:\\Windows\\Fonts\\Helvetica.ttf",
|
||||||
|
"C:\\Windows\\Fonts\\helvetica.ttf",
|
||||||
|
"C:\\Windows\\Fonts\\HelveticaNeue.ttf",
|
||||||
|
"C:\\Windows\\Fonts\\arial.ttf",
|
||||||
|
#else
|
||||||
|
"/usr/share/fonts/truetype/noto/NotoSans-Regular.ttf",
|
||||||
|
"/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf",
|
||||||
|
"/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
|
||||||
|
"/usr/local/share/fonts/NotoSans-Regular.ttf",
|
||||||
|
"/usr/local/share/fonts/LiberationSans-Regular.ttf",
|
||||||
|
#endif
|
||||||
|
});
|
||||||
|
|
||||||
|
if (base_font_path) {
|
||||||
|
atlas->AddFontFromFileTTF(base_font_path, 16.0f, &font_config,
|
||||||
|
atlas->GetGlyphRangesDefault());
|
||||||
|
} else {
|
||||||
|
atlas->AddFontDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
const char* jp_font_path = "C:\\Windows\\Fonts\\msgothic.ttc";
|
||||||
|
if (std::filesystem::exists(jp_font_path)) {
|
||||||
|
ImFontConfig jp_font_config;
|
||||||
|
jp_font_config.MergeMode = true;
|
||||||
|
jp_font_config.OversampleH = jp_font_config.OversampleV = 1;
|
||||||
|
jp_font_config.PixelSnapH = true;
|
||||||
|
jp_font_config.FontNo = 0;
|
||||||
|
atlas->AddFontFromFileTTF(jp_font_path, 16.0f, &jp_font_config,
|
||||||
|
atlas->GetGlyphRangesJapanese());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<rex::PathConfig> Skate3BaseApp::OnFinalizePaths(
|
||||||
|
const rex::PathConfig& defaults, std::function<void(rex::PathConfig)> resume) {
|
||||||
|
config_path_ = defaults.config_path;
|
||||||
|
user_settings_path_ = defaults.user_data_root / std::string(kSettingsFilename);
|
||||||
|
profiles_path_ = skate3::ProfilesFilePath(defaults.user_data_root);
|
||||||
|
|
||||||
|
auto profiles = skate3::LoadProfiles(profiles_path_);
|
||||||
|
const bool has_profiles_file = std::filesystem::exists(profiles_path_);
|
||||||
|
skate3::EnsureUsableProfileStore(profiles, "Player");
|
||||||
|
if (auto* profile = skate3::FindSelectedProfile(profiles)) {
|
||||||
|
skate3::ApplyProfileCvars(*profile);
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool has_config_file = std::filesystem::exists(defaults.config_path);
|
||||||
|
const bool has_game_path = std::filesystem::is_directory(defaults.game_data_root);
|
||||||
|
if (!has_profiles_file && has_config_file && has_game_path) {
|
||||||
|
skate3::SaveProfiles(profiles_path_, profiles);
|
||||||
|
}
|
||||||
|
auto runtime_paths = defaults;
|
||||||
|
if (runtime_paths.game_data_root.empty()) {
|
||||||
|
runtime_paths.game_data_root = config_path_.parent_path() / "game";
|
||||||
|
}
|
||||||
|
runtime_paths.config_path.clear();
|
||||||
|
if (!skate3::IsGameInstalled(runtime_paths.game_data_root)) {
|
||||||
|
REXLOG_INFO("Game files not found at {}; launching rexglue ISO installer",
|
||||||
|
runtime_paths.game_data_root.string());
|
||||||
|
rex::PathConfig installed_paths;
|
||||||
|
const bool installed = skate3::RunRexglueIsoInstallWizardBlocking(
|
||||||
|
app_context(), window(), imgui_drawer(), runtime_paths, installed_paths);
|
||||||
|
if (!installed) {
|
||||||
|
app_context().QuitFromUIThread();
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
return installed_paths;
|
||||||
|
}
|
||||||
|
return runtime_paths;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Skate3BaseApp::OnCreateDialogs(rex::ui::ImGuiDrawer* drawer) {
|
||||||
|
(void)drawer;
|
||||||
|
rex::ui::RegisterBind("bind_skate3_menu", "Escape", "Skate 3 settings", [this] {
|
||||||
|
ToggleSimpleSettings();
|
||||||
|
});
|
||||||
|
rex::ui::RegisterBind("bind_skate3_menu_alt", "F1", "Skate 3 settings alternate", [this] {
|
||||||
|
ToggleSimpleSettings();
|
||||||
|
});
|
||||||
|
rex::ui::RegisterBind("bind_skate3_save_draw_fingerprints", "F8",
|
||||||
|
"Save draw fingerprint log", [this] {
|
||||||
|
SaveDrawFingerprintLog();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Skate3BaseApp::OnPostSetup() {
|
||||||
|
ApplySelectedProfileToRuntime();
|
||||||
|
ApplyGameplayCursorMode();
|
||||||
|
|
||||||
|
if (auto* input_system = static_cast<rex::input::InputSystem*>(runtime()->input_system())) {
|
||||||
|
input_system->SetActiveCallback([this]() {
|
||||||
|
const bool settings_visible = simple_settings_dialog_ && simple_settings_dialog_->visible();
|
||||||
|
const bool xam_ui_active = rex::kernel::xam::xeXamIsUIActive();
|
||||||
|
return !settings_visible && !xam_ui_active;
|
||||||
|
});
|
||||||
|
input_system->SetMenuChordCallback([this]() {
|
||||||
|
app_context().CallInUIThreadDeferred([this]() { ToggleSimpleSettings(); });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std::getenv("SKATE3_DISABLE_BIG_ALIASES") == nullptr) {
|
||||||
|
InstallBigDeviceAliases();
|
||||||
|
}
|
||||||
|
InstallRecipeOverlay();
|
||||||
|
|
||||||
|
// Register multi-entry-function alternate entries.
|
||||||
|
runtime()->function_dispatcher()->SetFunction(0x82EBAE4C, &Sub82EBAE4CImpl);
|
||||||
|
|
||||||
|
auto* dispatcher = runtime()->function_dispatcher();
|
||||||
|
if (dispatcher->InitializeFunctionTable(eawebkit_PPCImageConfig.code_base,
|
||||||
|
eawebkit_PPCImageConfig.code_size,
|
||||||
|
eawebkit_PPCImageConfig.image_base,
|
||||||
|
eawebkit_PPCImageConfig.image_size)) {
|
||||||
|
for (int i = 0; eawebkit_PPCImageConfig.func_mappings[i].guest != 0; ++i) {
|
||||||
|
auto* host = eawebkit_PPCImageConfig.func_mappings[i].host;
|
||||||
|
if (host) {
|
||||||
|
dispatcher->SetFunction(
|
||||||
|
static_cast<uint32_t>(eawebkit_PPCImageConfig.func_mappings[i].guest),
|
||||||
|
host);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Skate3BaseApp::OnPostLaunchModule(rex::system::XThread* thread) {
|
||||||
|
(void)thread;
|
||||||
|
if (std::getenv("SKATE3_INVALIDATE_GPU_MEMORY_EVERY_FRAME") == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* graphics_system =
|
||||||
|
static_cast<rex::graphics::GraphicsSystem*>(runtime()->graphics_system());
|
||||||
|
if (!graphics_system) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
invalidate_gpu_memory_thread_running_.store(true, std::memory_order_release);
|
||||||
|
invalidate_gpu_memory_thread_ = std::thread([this, graphics_system]() {
|
||||||
|
while (invalidate_gpu_memory_thread_running_.load(
|
||||||
|
std::memory_order_acquire)) {
|
||||||
|
graphics_system->InvalidateGpuMemory();
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(16));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Skate3BaseApp::OnShutdown() {
|
||||||
|
rex::ui::UnregisterBind("bind_skate3_menu");
|
||||||
|
rex::ui::UnregisterBind("bind_skate3_menu_alt");
|
||||||
|
rex::ui::UnregisterBind("bind_skate3_save_draw_fingerprints");
|
||||||
|
ApplyGameplayCursorMode();
|
||||||
|
simple_settings_dialog_.reset();
|
||||||
|
StopGpuMemoryInvalidationThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Skate3BaseApp::OnGuestThreadExit(rex::system::XThread* thread) {
|
||||||
|
(void)thread;
|
||||||
|
StopGpuMemoryInvalidationThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Skate3BaseApp::StopGpuMemoryInvalidationThread() {
|
||||||
|
invalidate_gpu_memory_thread_running_.store(false, std::memory_order_release);
|
||||||
|
if (invalidate_gpu_memory_thread_.joinable()) {
|
||||||
|
invalidate_gpu_memory_thread_.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Skate3BaseApp::ToggleSimpleSettings() {
|
||||||
|
if (simple_settings_dialog_) {
|
||||||
|
if (simple_settings_dialog_->visible()) {
|
||||||
|
simple_settings_dialog_->Hide();
|
||||||
|
} else {
|
||||||
|
ApplySettingsCursorMode();
|
||||||
|
simple_settings_dialog_->Show();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto load_profiles = [this]() {
|
||||||
|
rex::ui::SimpleProfileState state;
|
||||||
|
auto store = skate3::LoadProfiles(profiles_path_);
|
||||||
|
skate3::EnsureUsableProfileStore(store, "Player");
|
||||||
|
state.selected_index = 0;
|
||||||
|
for (int i = 0; i < static_cast<int>(store.profiles.size()); ++i) {
|
||||||
|
const auto& profile = store.profiles[i];
|
||||||
|
state.profiles.push_back({profile.id, profile.gamertag, profile.signed_in});
|
||||||
|
if (profile.id == store.selected_profile) {
|
||||||
|
state.selected_index = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
};
|
||||||
|
auto save_profile = [this](int selected_index, std::string gamertag, bool signed_in) {
|
||||||
|
auto store = skate3::LoadProfiles(profiles_path_);
|
||||||
|
skate3::EnsureUsableProfileStore(store, "Player");
|
||||||
|
if (store.profiles.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
selected_index = std::clamp(selected_index, 0, static_cast<int>(store.profiles.size()) - 1);
|
||||||
|
auto& profile = store.profiles[selected_index];
|
||||||
|
if (!gamertag.empty()) {
|
||||||
|
profile.gamertag = std::move(gamertag);
|
||||||
|
}
|
||||||
|
profile.signed_in = signed_in;
|
||||||
|
if (!profile.signed_in) {
|
||||||
|
profile.live_signed_in = false;
|
||||||
|
}
|
||||||
|
store.selected_profile = profile.id;
|
||||||
|
skate3::SaveProfiles(profiles_path_, store);
|
||||||
|
skate3::ApplyProfileCvars(profile);
|
||||||
|
ApplySelectedProfileToRuntime();
|
||||||
|
};
|
||||||
|
auto close_settings = [this]() { ApplyGameplayCursorMode(); };
|
||||||
|
auto close_game = [this]() {
|
||||||
|
app_context().CallInUIThreadDeferred([this]() {
|
||||||
|
if (window()) {
|
||||||
|
window()->RequestClose();
|
||||||
|
} else {
|
||||||
|
app_context().QuitFromUIThread();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
auto restart_game = [this]() { RestartGame(); };
|
||||||
|
simple_settings_dialog_ =
|
||||||
|
std::make_unique<rex::ui::SimpleSettingsDialog>(
|
||||||
|
imgui_drawer(), user_settings_path_, std::move(load_profiles), std::move(save_profile),
|
||||||
|
std::move(close_settings), std::move(close_game), std::move(restart_game));
|
||||||
|
ApplySettingsCursorMode();
|
||||||
|
simple_settings_dialog_->Show();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Skate3BaseApp::ApplySettingsCursorMode() {
|
||||||
|
if (window()) {
|
||||||
|
window()->SetCursorVisibility(rex::ui::Window::CursorVisibility::kVisible);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Skate3BaseApp::ApplyGameplayCursorMode() {
|
||||||
|
if (window()) {
|
||||||
|
window()->SetCursorVisibility(rex::ui::Window::CursorVisibility::kAutoHidden);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Skate3BaseApp::RestartGame() {
|
||||||
|
app_context().CallInUIThreadDeferred([this]() {
|
||||||
|
#if defined(_WIN32)
|
||||||
|
wchar_t executable_path[MAX_PATH] = {};
|
||||||
|
if (!GetModuleFileNameW(nullptr, executable_path, MAX_PATH)) {
|
||||||
|
REXLOG_WARN("Restart requested, but the executable path could not be resolved");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring command_line = GetCommandLineW();
|
||||||
|
STARTUPINFOW startup_info{};
|
||||||
|
startup_info.cb = sizeof(startup_info);
|
||||||
|
PROCESS_INFORMATION process_info{};
|
||||||
|
if (!CreateProcessW(executable_path, command_line.data(), nullptr, nullptr, FALSE, 0, nullptr,
|
||||||
|
nullptr, &startup_info, &process_info)) {
|
||||||
|
REXLOG_WARN("Restart requested, but launching a new process failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
CloseHandle(process_info.hThread);
|
||||||
|
CloseHandle(process_info.hProcess);
|
||||||
|
#elif defined(__linux__)
|
||||||
|
const auto executable_path = rex::filesystem::GetExecutablePath();
|
||||||
|
if (executable_path.empty()) {
|
||||||
|
REXLOG_WARN("Restart requested, but the executable path could not be resolved");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto args = CurrentProcessArgumentsForRestart(executable_path);
|
||||||
|
std::vector<char*> argv;
|
||||||
|
argv.reserve(args.size() + 1);
|
||||||
|
for (auto& arg : args) {
|
||||||
|
argv.push_back(arg.data());
|
||||||
|
}
|
||||||
|
argv.push_back(nullptr);
|
||||||
|
|
||||||
|
const std::string executable = rex::path_to_utf8(executable_path);
|
||||||
|
pid_t child_pid = 0;
|
||||||
|
const int spawn_result =
|
||||||
|
posix_spawn(&child_pid, executable.c_str(), nullptr, nullptr, argv.data(), environ);
|
||||||
|
if (spawn_result != 0) {
|
||||||
|
REXLOG_WARN("Restart requested, but launching a new process failed: {}",
|
||||||
|
std::strerror(spawn_result));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
REXLOG_WARN("Restart requested, but automatic restart is not implemented on this platform");
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (window()) {
|
||||||
|
window()->RequestClose();
|
||||||
|
} else {
|
||||||
|
app_context().QuitFromUIThread();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Skate3BaseApp::SaveDrawFingerprintLog() {
|
||||||
|
#ifndef REXGLUE_ENABLE_PERF_COUNTERS
|
||||||
|
REXLOG_WARN("Perf capture is unavailable because perf counters are disabled in this build");
|
||||||
|
return;
|
||||||
|
#else
|
||||||
|
auto now = std::chrono::system_clock::now();
|
||||||
|
std::time_t now_time = std::chrono::system_clock::to_time_t(now);
|
||||||
|
std::tm local_time{};
|
||||||
|
#if defined(_WIN32)
|
||||||
|
localtime_s(&local_time, &now_time);
|
||||||
|
#else
|
||||||
|
localtime_r(&now_time, &local_time);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::ostringstream counters_filename;
|
||||||
|
counters_filename << "perf_capture_counters_" << std::put_time(&local_time, "%Y%m%d_%H%M%S")
|
||||||
|
<< ".csv";
|
||||||
|
std::ostringstream filename;
|
||||||
|
filename << "perf_capture_draw_fingerprints_" << std::put_time(&local_time, "%Y%m%d_%H%M%S")
|
||||||
|
<< ".csv";
|
||||||
|
|
||||||
|
std::error_code ec;
|
||||||
|
std::filesystem::create_directories(cache_root(), ec);
|
||||||
|
const auto counters_path = cache_root() / counters_filename.str();
|
||||||
|
const auto path = cache_root() / filename.str();
|
||||||
|
if (rex::perf::StartCapture(counters_path, path)) {
|
||||||
|
REXLOG_INFO("Started perf capture; counter log will be saved to {}",
|
||||||
|
counters_path.string());
|
||||||
|
REXLOG_INFO("Started perf capture; draw fingerprint log will be saved to {}",
|
||||||
|
path.string());
|
||||||
|
} else {
|
||||||
|
REXLOG_WARN("Perf capture is already running");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void Skate3BaseApp::ApplySelectedProfileToRuntime() {
|
||||||
|
if (!runtime() || !runtime()->kernel_state() || !runtime()->kernel_state()->user_profile()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto profiles = skate3::LoadProfiles(profiles_path_);
|
||||||
|
if (auto* profile = skate3::FindSelectedProfile(profiles)) {
|
||||||
|
skate3::ApplyProfileCvars(*profile);
|
||||||
|
runtime()->kernel_state()->user_profile()->SetIdentity(profile->xuid, profile->gamertag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Skate3BaseApp::IsRecipeNameChar(char c) {
|
||||||
|
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
|
||||||
|
(c >= '0' && c <= '9') || c == '_' || c == '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
std::set<std::string> Skate3BaseApp::DiscoverRecipeAliases(
|
||||||
|
const std::filesystem::path& content_root) {
|
||||||
|
static constexpr size_t kScanBytes = 8 * 1024 * 1024;
|
||||||
|
static constexpr std::string_view kPrefix = "data/content/recipe/";
|
||||||
|
|
||||||
|
std::set<std::string> aliases;
|
||||||
|
std::error_code ec;
|
||||||
|
for (const auto& entry : std::filesystem::directory_iterator(content_root, ec)) {
|
||||||
|
if (ec || !entry.is_regular_file() || entry.path().extension() != ".big") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ifstream stream(entry.path(), std::ios::binary);
|
||||||
|
if (!stream) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string buffer;
|
||||||
|
buffer.resize(kScanBytes);
|
||||||
|
stream.read(buffer.data(), static_cast<std::streamsize>(buffer.size()));
|
||||||
|
buffer.resize(static_cast<size_t>(stream.gcount()));
|
||||||
|
|
||||||
|
size_t pos = 0;
|
||||||
|
while ((pos = buffer.find(kPrefix, pos)) != std::string::npos) {
|
||||||
|
size_t name_start = pos + kPrefix.size();
|
||||||
|
size_t name_end = name_start;
|
||||||
|
while (name_end < buffer.size() && IsRecipeNameChar(buffer[name_end])) {
|
||||||
|
++name_end;
|
||||||
|
}
|
||||||
|
if (name_end > name_start) {
|
||||||
|
aliases.insert(buffer.substr(name_start, name_end - name_start));
|
||||||
|
}
|
||||||
|
pos = name_end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return aliases;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Skate3BaseApp::InstallRecipeOverlay() {
|
||||||
|
if (recipe_overlay_installed_ || !runtime() || !runtime()->file_system()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto content_root = game_data_root() / "data" / "content";
|
||||||
|
if (!std::filesystem::exists(content_root)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto aliases = DiscoverRecipeAliases(content_root);
|
||||||
|
if (aliases.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto overlay_root = cache_root() / "vfs_recipe_aliases";
|
||||||
|
std::error_code ec;
|
||||||
|
std::filesystem::create_directories(overlay_root, ec);
|
||||||
|
if (ec) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t created = 0;
|
||||||
|
for (const auto& alias : aliases) {
|
||||||
|
std::filesystem::create_directories(overlay_root / alias, ec);
|
||||||
|
if (!ec) {
|
||||||
|
++created;
|
||||||
|
}
|
||||||
|
ec.clear();
|
||||||
|
}
|
||||||
|
if (!created) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto device = std::make_unique<rex::filesystem::HostPathDevice>(
|
||||||
|
"skate3recipe:", overlay_root, true);
|
||||||
|
if (!device->Initialize()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
runtime()->file_system()->RegisterDevice(std::move(device));
|
||||||
|
runtime()->file_system()->RegisterSymbolicLink(
|
||||||
|
"\\Device\\Harddisk0\\Partition1\\data\\content\\recipe",
|
||||||
|
"skate3recipe:");
|
||||||
|
recipe_overlay_installed_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Skate3BaseApp::InstallBigDeviceAliases() {
|
||||||
|
if (big_device_aliases_installed_ || !runtime() || !runtime()->file_system()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skate 3 uses these file-server style schemes for world content probes.
|
||||||
|
// Route them to the normal title mount so existing disc files resolve.
|
||||||
|
runtime()->file_system()->RegisterSymbolicLink(
|
||||||
|
"big:", "\\Device\\Harddisk0\\Partition1");
|
||||||
|
runtime()->file_system()->RegisterSymbolicLink(
|
||||||
|
"dlcbig:", "\\Device\\Harddisk0\\Partition1");
|
||||||
|
big_device_aliases_installed_ = true;
|
||||||
|
}
|
||||||
61
src/skate3_app_common.h
Normal file
61
src/skate3_app_common.h
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "generated/skate3_init.h"
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
#include <set>
|
||||||
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include <rex/rex_app.h>
|
||||||
|
#include <rex/ui/overlay/simple_settings_overlay.h>
|
||||||
|
|
||||||
|
namespace rex::ui {
|
||||||
|
class ImGuiDrawer;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Skate3BaseApp : public rex::ReXApp {
|
||||||
|
public:
|
||||||
|
using rex::ReXApp::ReXApp;
|
||||||
|
~Skate3BaseApp() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::optional<rex::PathConfig> OnFinalizePaths(
|
||||||
|
const rex::PathConfig& defaults,
|
||||||
|
std::function<void(rex::PathConfig)> resume) override;
|
||||||
|
void OnConfigurePaths(rex::PathConfig& paths) override;
|
||||||
|
void OnConfigureFonts(ImFontAtlas* atlas) override;
|
||||||
|
void OnCreateDialogs(rex::ui::ImGuiDrawer* drawer) override;
|
||||||
|
void OnPostSetup() override;
|
||||||
|
void OnPostLaunchModule(rex::system::XThread* thread) override;
|
||||||
|
void OnShutdown() override;
|
||||||
|
void OnGuestThreadExit(rex::system::XThread* thread) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void StopGpuMemoryInvalidationThread();
|
||||||
|
void InstallRecipeOverlay();
|
||||||
|
void InstallBigDeviceAliases();
|
||||||
|
void ToggleSimpleSettings();
|
||||||
|
void ApplySettingsCursorMode();
|
||||||
|
void ApplyGameplayCursorMode();
|
||||||
|
void RestartGame();
|
||||||
|
void SaveDrawFingerprintLog();
|
||||||
|
void ApplySelectedProfileToRuntime();
|
||||||
|
|
||||||
|
static bool IsRecipeNameChar(char c);
|
||||||
|
static std::set<std::string> DiscoverRecipeAliases(
|
||||||
|
const std::filesystem::path& content_root);
|
||||||
|
|
||||||
|
std::filesystem::path config_path_;
|
||||||
|
std::filesystem::path user_settings_path_;
|
||||||
|
std::filesystem::path profiles_path_;
|
||||||
|
std::unique_ptr<rex::ui::SimpleSettingsDialog> simple_settings_dialog_;
|
||||||
|
bool recipe_overlay_installed_ = false;
|
||||||
|
bool big_device_aliases_installed_ = false;
|
||||||
|
std::atomic<bool> invalidate_gpu_memory_thread_running_{false};
|
||||||
|
std::thread invalidate_gpu_memory_thread_;
|
||||||
|
};
|
||||||
Loading…
Add table
Reference in a new issue