From 547fccbed3c456210078cdeeacd1cf545a55d91f Mon Sep 17 00:00:00 2001 From: Ragmoa Date: Sun, 9 Aug 2026 16:51:55 +0200 Subject: [PATCH 1/3] Bypass galestorm tutorial --- src/okami-apclient/eventfix/eventfix.cpp | 2 ++ src/okami-apclient/eventfix/gale_shrine.cpp | 37 +++++++++++++++++++++ src/okami-apclient/eventfix/gale_shrine.hpp | 14 ++++++++ src/okami-apclient/sources.cmake | 1 + 4 files changed, 54 insertions(+) create mode 100644 src/okami-apclient/eventfix/gale_shrine.cpp create mode 100644 src/okami-apclient/eventfix/gale_shrine.hpp diff --git a/src/okami-apclient/eventfix/eventfix.cpp b/src/okami-apclient/eventfix/eventfix.cpp index e5966b9..87f032d 100644 --- a/src/okami-apclient/eventfix/eventfix.cpp +++ b/src/okami-apclient/eventfix/eventfix.cpp @@ -6,6 +6,7 @@ #include "cave_of_nagi.hpp" #include "common.hpp" +#include "gale_shrine.hpp" #include "kamiki_village.hpp" #include "registry.hpp" @@ -48,6 +49,7 @@ void initialize() int installed = 0; installAll(cave_of_nagi::getBypasses(), installed); installAll(kamiki_village::getBypasses(), installed); + installAll(gale_shrine::getBypasses(), installed); wolf::logInfo("[eventfix] installed %d bypass hooks", installed); } diff --git a/src/okami-apclient/eventfix/gale_shrine.cpp b/src/okami-apclient/eventfix/gale_shrine.cpp new file mode 100644 index 0000000..8e00633 --- /dev/null +++ b/src/okami-apclient/eventfix/gale_shrine.cpp @@ -0,0 +1,37 @@ +#include "gale_shrine.hpp" + +#include +#include + +#include "common.hpp" + +namespace eventfix::gale_shrine +{ + +namespace +{ + +void __fastcall stubGalestormTutoBypass() +{ + eventfix::clearCutsceneModeBits(); + eventfix::grantBrush(okami::BrushOverlay::galestorm); + // Set this flag to load the "healed" state on next reload + eventfix::setStateBit(0x80023); + // Set these to mark tutorial as completed + eventfix::setStateBit(0x80026); + eventfix::setStateBit(0x80033); + wolf::logInfo("[eventfix] Galestorm tutorial removed; Granted bursh power"); +} + +constexpr EventBypass kBypasses[] = { + {"Galestrom tutorail lock", 0x4EC0C0, stubGalestormTutoBypass}, +}; + +} // namespace + +std::span getBypasses() +{ + return kBypasses; +} + +} // namespace eventfix::gale_shrine diff --git a/src/okami-apclient/eventfix/gale_shrine.hpp b/src/okami-apclient/eventfix/gale_shrine.hpp new file mode 100644 index 0000000..7cc4c87 --- /dev/null +++ b/src/okami-apclient/eventfix/gale_shrine.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include + +#include "registry.hpp" + +namespace eventfix::gale_shrine +{ + +// Bypass records for Gale Shrine forced-tutorial softlock. See +// docs/event-triggers-runtime.md for the runtime model and chain. +std::span getBypasses(); + +} // namespace eventfix::gale_shrine diff --git a/src/okami-apclient/sources.cmake b/src/okami-apclient/sources.cmake index 65b693e..72635b4 100644 --- a/src/okami-apclient/sources.cmake +++ b/src/okami-apclient/sources.cmake @@ -16,6 +16,7 @@ set(okami-apclient_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/eventfix/cave_of_nagi.cpp ${CMAKE_CURRENT_SOURCE_DIR}/eventfix/common.cpp ${CMAKE_CURRENT_SOURCE_DIR}/eventfix/eventfix.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/eventfix/gale_shrine.cpp ${CMAKE_CURRENT_SOURCE_DIR}/eventfix/kamiki_village.cpp ${CMAKE_CURRENT_SOURCE_DIR}/gamestate_accessors.cpp ${CMAKE_CURRENT_SOURCE_DIR}/itempatch.cpp From 478fe4693ed2129c83238c3a0fafca4059c76f11 Mon Sep 17 00:00:00 2001 From: Ragmoa Date: Sun, 9 Aug 2026 17:09:21 +0200 Subject: [PATCH 2/3] Add galestorm bypass doc --- docs/event-triggers-runtime.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/docs/event-triggers-runtime.md b/docs/event-triggers-runtime.md index f7d5851..600e38e 100644 --- a/docs/event-triggers-runtime.md +++ b/docs/event-triggers-runtime.md @@ -224,6 +224,28 @@ Confirmed offsets and dispatch order for the Sakuya descent + Water Lily grant + The bypass installed by `src/okami-apclient/eventfix/kamiki_village.cpp` replaces `FUN_1804c64a0` with a stub that calls `clearCutsceneModeBits()`, `grantBrush(BrushOverlay::water_lily)`, and `transitionStageSubState(0xe)`. The natural chain's per-map state writes (e.g. `KamikiVillage` worldStateBits 11 / 149 / 163) are set elsewhere (trigger volumes, post-bloom scripts); the bypass does not interact with them. +## Gale Shrine galestorm tutorial bypass + +The handler for the tutorial is FUN_1804E9CC0, + +```cpp + if (((DAT_180b6b2ac >> 0x1e & 1) == 0) && ((*(uint *)(WORLD_STATE_POINTER + 0x45c) & 0x100) == 0)) + { + cVar2 = FUN_1803f3380(PTR_DAT_1807a8cb0,5,0,1); + if (cVar2 != '\0') { + set_world_state_bit(0x80017); //set flag "stuck in platform" + puVar1 = PTR_DAT_1807a8cb0; + enter_cutsence_mode(PTR_DAT_1807a8cb0); + uVar3 = schedule_callback(puVar1 + 0x20,FUN_1804ec0c0,0xffffffff); + register_callback(puVar1,uVar3); + return; + } + } +``` +We replace the FUN_1804ec0c0 with a stub that exits cutscene mode, grants the galestorm check to the player, and sets some flags to mark the tutorial as complete. The door stays closed, but it doesn't have collision as you're never supposed to be able to move while it's closed, so you can just cross it. + + + ## Stage architecture (one level above TICK) The CoN TICK has zero static call sites in main.dll. Its only xref is its `.pdata` exception-unwind entry. Despite that, scripts and TICKs in this engine are *not* loaded from external files: there is no separate script bytecode language. Scripts are compiled C++ callbacks baked into main.dll, scheduled by ID via `FUN_1803f35f0(ctx, script_id, ...)` and similar. So the TICK gets installed at runtime through some indirection that we haven't fully traced statically (likely a stage-id -> function-pointer table populated during stage init), but the answer lives somewhere inside main.dll, not in an on-disk script file. The on-disk room files (`data_pc/stN/rXXX.bin`) carry SCA trigger volumes, MSD strings, and per-room asset data, not callback pointers. That said, the *parallel* machinery (the stage descriptor object the TICK eventually drives) is fully visible in the binary. From d362d38a5105275fb8bc32b931effde2c253f251 Mon Sep 17 00:00:00 2001 From: Ragmoa Date: Sun, 9 Aug 2026 17:12:07 +0200 Subject: [PATCH 3/3] Add galestorm bypass doc (2) --- docs/event-triggers-runtime.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/event-triggers-runtime.md b/docs/event-triggers-runtime.md index 600e38e..3f86082 100644 --- a/docs/event-triggers-runtime.md +++ b/docs/event-triggers-runtime.md @@ -245,7 +245,6 @@ The handler for the tutorial is FUN_1804E9CC0, We replace the FUN_1804ec0c0 with a stub that exits cutscene mode, grants the galestorm check to the player, and sets some flags to mark the tutorial as complete. The door stays closed, but it doesn't have collision as you're never supposed to be able to move while it's closed, so you can just cross it. - ## Stage architecture (one level above TICK) The CoN TICK has zero static call sites in main.dll. Its only xref is its `.pdata` exception-unwind entry. Despite that, scripts and TICKs in this engine are *not* loaded from external files: there is no separate script bytecode language. Scripts are compiled C++ callbacks baked into main.dll, scheduled by ID via `FUN_1803f35f0(ctx, script_id, ...)` and similar. So the TICK gets installed at runtime through some indirection that we haven't fully traced statically (likely a stage-id -> function-pointer table populated during stage init), but the answer lives somewhere inside main.dll, not in an on-disk script file. The on-disk room files (`data_pc/stN/rXXX.bin`) carry SCA trigger volumes, MSD strings, and per-room asset data, not callback pointers. That said, the *parallel* machinery (the stage descriptor object the TICK eventually drives) is fully visible in the binary.