add Skate 3 demo path
This commit is contained in:
parent
1caef13677
commit
c790817d56
6 changed files with 283 additions and 1 deletions
|
|
@ -183,6 +183,7 @@ include(cmake/CodegenTargets.cmake)
|
|||
set(SKATE3_COMMON_SOURCES
|
||||
src/exception_compat.cpp
|
||||
src/skate3_app_common.cpp
|
||||
src/skate3_demo_path.cpp
|
||||
src/skate3_fov.cpp
|
||||
src/skate3_iso_installer.cpp
|
||||
src/skate3_title_update_installer.cpp
|
||||
|
|
|
|||
|
|
@ -86,3 +86,45 @@ endforeach()
|
|||
if(NOT _fov_patched)
|
||||
message(FATAL_ERROR "Failed to apply Skate 3 generated projection FOV patch; projection FOV anchor not found")
|
||||
endif()
|
||||
|
||||
set(_demo_path_movie_patched FALSE)
|
||||
foreach(_file IN LISTS _skate3_recomp_files)
|
||||
file(READ "${_file}" _contents)
|
||||
if(_contents MATCHES "ShouldForceIntroMovieComplete")
|
||||
set(_demo_path_movie_patched TRUE)
|
||||
break()
|
||||
endif()
|
||||
|
||||
set(_demo_path_movie_site
|
||||
" // bl 0x825d60c8
|
||||
ctx.lr = 0x825E05A0;
|
||||
sub_825D60C8(ctx, base);")
|
||||
if(NOT _contents MATCHES "ctx\\.lr = 0x825E05A0;")
|
||||
continue()
|
||||
endif()
|
||||
if(NOT _contents MATCHES "DEFINE_REX_FUNC\\(sub_825E0510\\)")
|
||||
continue()
|
||||
endif()
|
||||
string(FIND "${_contents}" "${_demo_path_movie_site}" _demo_path_movie_anchor)
|
||||
if(_demo_path_movie_anchor EQUAL -1)
|
||||
continue()
|
||||
endif()
|
||||
|
||||
set(_demo_path_movie_patch
|
||||
" if (skate3::demo_path::ShouldForceIntroMovieComplete()) {
|
||||
ctx.r3.u64 = 0;
|
||||
} else {
|
||||
// bl 0x825d60c8
|
||||
ctx.lr = 0x825E05A0;
|
||||
sub_825D60C8(ctx, base);
|
||||
}")
|
||||
string(REPLACE "${_demo_path_movie_site}" "${_demo_path_movie_patch}" _contents "${_contents}")
|
||||
_skate3_add_include(_contents "skate3_demo_path.h")
|
||||
file(WRITE "${_file}" "${_contents}")
|
||||
set(_demo_path_movie_patched TRUE)
|
||||
message(STATUS "Applied Skate 3 demo path intro movie patch in ${_file}")
|
||||
break()
|
||||
endforeach()
|
||||
if(NOT _demo_path_movie_patched)
|
||||
message(FATAL_ERROR "Failed to apply Skate 3 demo path intro movie patch; FEMoviePlayer::Update anchor not found")
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "skate3_app_common.h"
|
||||
|
||||
#include "skate3_demo_path.h"
|
||||
#include "skate3_fov.h"
|
||||
#include "skate3_iso_installer.h"
|
||||
#include "skate3_title_update_installer.h"
|
||||
|
|
@ -121,6 +122,16 @@ REXCVAR_DEFINE_DOUBLE(skate3_ultrawide_target_aspect, 0.0, "Skate 3",
|
|||
|
||||
namespace {
|
||||
|
||||
void ApplyDemoPathProfileOverride() {
|
||||
if (!rex::cvar::Query<bool>("skate3_demo_path") &&
|
||||
!rex::cvar::Query<bool>("skate3_demo_path_probe")) {
|
||||
return;
|
||||
}
|
||||
|
||||
rex::cvar::SetFlagByName("user_profile_signed_in", "false");
|
||||
rex::cvar::SetFlagByName("user_live_signed_in", "false");
|
||||
}
|
||||
|
||||
#if defined(__linux__) || defined(__APPLE__)
|
||||
std::vector<std::string> CurrentProcessArgumentsForRestart(
|
||||
const std::filesystem::path& executable_path) {
|
||||
|
|
@ -599,6 +610,7 @@ std::optional<rex::PathConfig> Skate3BaseApp::OnFinalizePaths(
|
|||
skate3::EnsureUsableProfileStore(profiles, "Player");
|
||||
if (auto* profile = skate3::FindSelectedProfile(profiles)) {
|
||||
skate3::ApplyProfileCvars(*profile);
|
||||
ApplyDemoPathProfileOverride();
|
||||
}
|
||||
|
||||
const bool has_config_file = std::filesystem::exists(defaults.config_path);
|
||||
|
|
@ -727,6 +739,7 @@ void Skate3BaseApp::OnPostSetup() {
|
|||
#endif
|
||||
|
||||
auto* dispatcher = runtime()->function_dispatcher();
|
||||
skate3::demo_path::InstallHooks(dispatcher);
|
||||
if (dispatcher->InitializeFunctionTable(eawebkit_PPCImageConfig.code_base,
|
||||
eawebkit_PPCImageConfig.code_size,
|
||||
eawebkit_PPCImageConfig.image_base,
|
||||
|
|
@ -797,6 +810,7 @@ void Skate3BaseApp::ToggleSimpleSettings() {
|
|||
store.selected_profile = profile.id;
|
||||
skate3::SaveProfiles(profiles_path_, store);
|
||||
skate3::ApplyProfileCvars(profile);
|
||||
ApplyDemoPathProfileOverride();
|
||||
ApplySelectedProfileToRuntime();
|
||||
};
|
||||
auto close_settings = [this]() { ApplyGameplayCursorMode(); };
|
||||
|
|
@ -984,6 +998,7 @@ void Skate3BaseApp::ApplySelectedProfileToRuntime() {
|
|||
auto profiles = skate3::LoadProfiles(profiles_path_);
|
||||
if (auto* profile = skate3::FindSelectedProfile(profiles)) {
|
||||
skate3::ApplyProfileCvars(*profile);
|
||||
ApplyDemoPathProfileOverride();
|
||||
runtime()->kernel_state()->user_profile()->SetIdentity(profile->xuid, profile->gamertag);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
212
src/skate3_demo_path.cpp
Normal file
212
src/skate3_demo_path.cpp
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
#include "skate3_demo_path.h"
|
||||
|
||||
#include "generated/skate3_init.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
|
||||
#include <rex/cvar.h>
|
||||
#include <rex/input/input.h>
|
||||
#include <rex/kernel/xam/input_injection.h>
|
||||
#include <rex/logging.h>
|
||||
#include <rex/ppc/context.h>
|
||||
#include <rex/system/function_dispatcher.h>
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
REXCVAR_DEFINE_BOOL(skate3_demo_path, false, "Skate 3",
|
||||
"Probe and automate the boot path to gameplay");
|
||||
REXCVAR_DEFINE_BOOL(skate3_demo_path_probe, false, "Skate 3",
|
||||
"Log Skate 3 boot/frontend states used by the demo path");
|
||||
|
||||
namespace skate3::demo_path {
|
||||
namespace {
|
||||
|
||||
constexpr uint32_t kFrontEndStatePressStart = 24;
|
||||
constexpr uint32_t kFrontEndStateLanguageSelect = 47;
|
||||
|
||||
std::atomic<uint32_t> g_last_requested_state{0};
|
||||
std::atomic<bool> g_seen_language_update{false};
|
||||
std::atomic<uint32_t> g_last_language_select_event{std::numeric_limits<uint32_t>::max()};
|
||||
std::atomic<uint32_t> g_last_press_start_event{std::numeric_limits<uint32_t>::max()};
|
||||
std::atomic<uint32_t> g_automation_stage{0};
|
||||
std::atomic<bool> g_skip_intro_movie{false};
|
||||
std::atomic<bool> g_logged_intro_movie_skip{false};
|
||||
std::atomic<bool> g_f10_poll_was_down{false};
|
||||
|
||||
bool ProbeEnabled() {
|
||||
return rex::cvar::Query<bool>("skate3_demo_path") ||
|
||||
rex::cvar::Query<bool>("skate3_demo_path_probe");
|
||||
}
|
||||
|
||||
bool AutomationEnabled() {
|
||||
return rex::cvar::Query<bool>("skate3_demo_path");
|
||||
}
|
||||
|
||||
const char* KnownFrontEndStateName(uint32_t state_id) {
|
||||
switch (state_id) {
|
||||
case kFrontEndStatePressStart:
|
||||
return "press-start";
|
||||
case kFrontEndStateLanguageSelect:
|
||||
return "language-select";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
void LogBootFlowEventChange(std::atomic<uint32_t>& last_event, const char* name, uint32_t event,
|
||||
uint32_t state_this) {
|
||||
if (!ProbeEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t previous = last_event.load(std::memory_order_relaxed);
|
||||
while (previous != event) {
|
||||
if (last_event.compare_exchange_weak(previous, event, std::memory_order_relaxed)) {
|
||||
REXLOG_INFO("Skate 3 demo path: {} event={} this=0x{:08X}", name, event, state_this);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QueueLanguageAcceptIfNeeded() {
|
||||
if (!AutomationEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t expected = 0;
|
||||
if (!g_automation_stage.compare_exchange_strong(expected, 1, std::memory_order_relaxed)) {
|
||||
return;
|
||||
}
|
||||
|
||||
rex::kernel::xam::QueueSyntheticInput(rex::input::X_INPUT_GAMEPAD_A, 8);
|
||||
REXLOG_INFO("Skate 3 demo path: queued language-select A pulse");
|
||||
}
|
||||
|
||||
void EnableTitleStartAutoTapIfNeeded(const char* reason) {
|
||||
if (!AutomationEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t stage = g_automation_stage.load(std::memory_order_relaxed);
|
||||
while (stage < 2) {
|
||||
if (g_automation_stage.compare_exchange_weak(stage, 2, std::memory_order_relaxed)) {
|
||||
rex::kernel::xam::SetSyntheticAutoTap(rex::input::X_INPUT_GAMEPAD_START, true);
|
||||
REXLOG_INFO("Skate 3 demo path: enabled press-start auto-tap ({})", reason);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EnableIntroMovieSkipIfNeeded() {
|
||||
if (!AutomationEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool expected = false;
|
||||
if (g_skip_intro_movie.compare_exchange_strong(expected, true, std::memory_order_relaxed)) {
|
||||
REXLOG_INFO("Skate 3 demo path: enabled intro movie completion override");
|
||||
}
|
||||
}
|
||||
|
||||
void PollF10Marker() {
|
||||
#if defined(_WIN32)
|
||||
if (!ProbeEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bool f10_down = (GetAsyncKeyState(VK_F10) & 0x8000) != 0;
|
||||
const bool was_down = g_f10_poll_was_down.exchange(f10_down, std::memory_order_relaxed);
|
||||
if (f10_down && !was_down) {
|
||||
REXLOG_WARN("Skate 3 demo path: polled F10 milestone marker");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
extern "C" REX_FUNC(Skate3DemoPath_SetFrontEndStateHook) {
|
||||
const uint32_t manager = ctx.r3.u32;
|
||||
const uint32_t state_id = ctx.r4.u32;
|
||||
const uint32_t mode = ctx.r5.u32;
|
||||
const uint32_t caller_lr = ctx.lr;
|
||||
|
||||
if (ProbeEnabled()) {
|
||||
g_last_requested_state.store(state_id, std::memory_order_relaxed);
|
||||
REXLOG_INFO(
|
||||
"Skate 3 demo path: FE SetState state={} ({}) mode={} manager=0x{:08X} lr=0x{:08X}",
|
||||
state_id, KnownFrontEndStateName(state_id), mode, manager, caller_lr);
|
||||
}
|
||||
|
||||
sub_82D0AFA0(ctx, base);
|
||||
}
|
||||
|
||||
extern "C" REX_FUNC(Skate3DemoPath_LanguageSelectStateHook) {
|
||||
PollF10Marker();
|
||||
LogBootFlowEventChange(g_last_language_select_event, "BootFlow LanguageSelectState",
|
||||
ctx.r4.u32, ctx.r3.u32);
|
||||
if (ctx.r4.u32 == 4) {
|
||||
EnableIntroMovieSkipIfNeeded();
|
||||
}
|
||||
|
||||
sub_826FDD70(ctx, base);
|
||||
}
|
||||
|
||||
extern "C" REX_FUNC(Skate3DemoPath_ShowPressStartModeHook) {
|
||||
PollF10Marker();
|
||||
LogBootFlowEventChange(g_last_press_start_event, "BootFlow ShowPressStartMode", ctx.r4.u32,
|
||||
ctx.r3.u32);
|
||||
if (ctx.r4.u32 == 1) {
|
||||
g_skip_intro_movie.store(false, std::memory_order_relaxed);
|
||||
EnableTitleStartAutoTapIfNeeded("press-start state");
|
||||
}
|
||||
|
||||
sub_826FE1D8(ctx, base);
|
||||
}
|
||||
|
||||
extern "C" REX_FUNC(Skate3DemoPath_LanguageSelectUpdateHook) {
|
||||
PollF10Marker();
|
||||
if (ProbeEnabled()) {
|
||||
bool expected = false;
|
||||
if (g_seen_language_update.compare_exchange_strong(expected, true,
|
||||
std::memory_order_relaxed)) {
|
||||
REXLOG_INFO(
|
||||
"Skate 3 demo path: FrontEndState_LanguageSelect::OnUpdate first seen "
|
||||
"dt={} this=0x{:08X}",
|
||||
ctx.r4.u32, ctx.r3.u32);
|
||||
QueueLanguageAcceptIfNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
sub_82639400(ctx, base);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void InstallHooks(rex::runtime::FunctionDispatcher* dispatcher) {
|
||||
if (!dispatcher || !ProbeEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatcher->SetFunction(0x82D0AFA0, &Skate3DemoPath_SetFrontEndStateHook);
|
||||
dispatcher->SetFunction(0x826FDD70, &Skate3DemoPath_LanguageSelectStateHook);
|
||||
dispatcher->SetFunction(0x826FE1D8, &Skate3DemoPath_ShowPressStartModeHook);
|
||||
dispatcher->SetFunction(0x82639400, &Skate3DemoPath_LanguageSelectUpdateHook);
|
||||
REXLOG_INFO("Skate 3 demo path: frontend probe hooks installed");
|
||||
}
|
||||
|
||||
bool ShouldForceIntroMovieComplete() {
|
||||
if (!AutomationEnabled() || !g_skip_intro_movie.load(std::memory_order_relaxed)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool expected = false;
|
||||
if (g_logged_intro_movie_skip.compare_exchange_strong(expected, true,
|
||||
std::memory_order_relaxed)) {
|
||||
REXLOG_INFO("Skate 3 demo path: forcing frontend intro movie complete");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace skate3::demo_path
|
||||
12
src/skate3_demo_path.h
Normal file
12
src/skate3_demo_path.h
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
namespace rex::runtime {
|
||||
class FunctionDispatcher;
|
||||
}
|
||||
|
||||
namespace skate3::demo_path {
|
||||
|
||||
void InstallHooks(rex::runtime::FunctionDispatcher* dispatcher);
|
||||
bool ShouldForceIntroMovieComplete();
|
||||
|
||||
} // namespace skate3::demo_path
|
||||
2
third_party/rexglue-sdk
vendored
2
third_party/rexglue-sdk
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit 4e468082dce6f5cf32cad0ce2622abd6468e1219
|
||||
Subproject commit 8ce31c280a92a041474e95db1a6958c5b1158537
|
||||
Loading…
Add table
Reference in a new issue