Explorar o código

Limit loading cinematic suppression to the native 1AU activity

Millie hai 3 días
pai
achega
cc76d94a1f

+ 5 - 1
MISSION_EMBER_POST_OPUS.md

@@ -6,7 +6,7 @@ Read `OpusHandoff.md` and retained Claude's latest beam polarity (normal open, s
 
 The Omega contributor supplied `LoadingCinematics_Suppressed`, then RVA `0xC24490`. In the captured native image this is a no-argument boolean wrapper. Its travel/spaceflight callers skip cinematic setup and waiting when it returns true (D46900, D474D0, EE50A0 and EF44C0 inspected).
 
-`client.suppress_loading_cinematics` now forces that predicate true. It defaults false in source and is enabled in the installed settings. Setting it false restores the original predicate. It applies to loading cinematics generally, rather than being scoped to a particular mission. The scripted type-6 movie controller is not detoured. Verify that the erroneous Earth fly-in disappears while the authored 1AU introduction and ending movies still play.
+`client.suppress_loading_cinematics` now forces that predicate true only when the native loading flow names 1AU (definition 38F926B2). It defaults false in source and is enabled in the installed settings. Setting it false restores the original predicate. The scope is re-evaluated on every call through the same native activity reader the loading flow uses (C294B0). The SDK maps its index to the definition hash. Orbit, another activity, an absent SDK or an unresolved activity uses the original predicate; retained host sessions do not control suppression. The scripted type-6 movie controller is not detoured. Verify that the erroneous Earth fly-in disappears while the authored 1AU introduction and ending movies still play.
 
 The hook resolves the unique EF44C0 caller signature, follows its call at +30 and checks the target's boolean-wrapper bytes. Offline signature validation found exactly one caller and resolved C24490. The short predicate signature alone matches two functions and is deliberately not used for lookup.
 
@@ -33,3 +33,7 @@ Installed 19 files, including the settings change, with complete prior-file back
 Backup: `/home/millie/Documents/Sunrise-builds/mission-ember/build/post-opus-install-backup-20260906-150504`.
 
 Installed DLL SHA-256: `be4ea67c9399c4f08c41eac25f9ed2f27152dff9876b449e8d0d60e7fc9a0139`.
+
+## Scope correction
+
+Loading suppression is now limited to 1AU using the native loading activity reader above. The corrected Release DLL built successfully, and offline signature/call validation passed. Installation was deferred because the game was running; the installed DLL still has the earlier global override until this build is copied after the game closes. The scope-only installer is `/tmp/install-ember-loading-scope.py` and preserves settings, saves and scripts.

+ 33 - 1
Sunrise/src/client/hooks/bootflow/loading_cinematics.cpp

@@ -1,8 +1,10 @@
+#include <cstdint>
 #include <cstring>
 #include <string_view>
 
 #include "../../../core/logging/log.h"
 #include "../../../core/settings/settings.h"
+#include "../../../state/activity_sdk/runtime.h"
 #include "internal.h"
 
 namespace sunrise::client::hooks::bootflow {
@@ -13,11 +15,38 @@ constexpr std::string_view kCallerText =
     "48 89 5C 24 ? 57 48 83 EC ? 48 8B D9 E8 ? ? ? ? 80 3D ? ? ? ? 00 "
     "48 8B F8 75 ? E8 ? ? ? ? 84 C0 75 ? E8 ? ? ? ? 84 C0 75 ?";
 constexpr auto kCaller = signature<signature_length(kCallerText)>(kCallerText);
+// C294B0 reads the activity index from the native group activity property. The
+// loading flow itself uses this reader at E2EC30 before testing suppression.
+constexpr std::string_view kActivityReaderText =
+    "48 89 5C 24 ? 55 48 8D AC 24 ? ? ? ? 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? "
+    "48 33 C4 48 89 85 ? ? ? ? 48 8B D9 E8 ? ? ? ? 4C 8B C0 33 D2 8B C2 "
+    "4D 85 C0 74 ? 49 63 40 10 48 69 C8 A0 C8 01 00";
+constexpr auto kActivityReader = signature<signature_length(kActivityReaderText)>(kActivityReaderText);
+constexpr std::uint32_t kEmberDefinition = 0x38F926B2U;
+using ActivityReader = void(__fastcall*)(std::uint16_t*);
+ActivityReader g_activityReader{};
 using Predicate = bool(__fastcall*)();
 hooking::detour::Handle g_handle{};
 
+bool entering_ember() noexcept {
+    if (g_activityReader == nullptr) return false;
+    std::uint16_t index = 0xFFFF;
+    g_activityReader(&index);
+    if (index == 0xFFFF) return false;
+    const auto catalog = state::activity_sdk::snapshot();
+    if (catalog == nullptr) return false;
+    for (const auto& activity : catalog->activities()) {
+        if (activity.activityIndex == index) {
+            return activity.definitionHash == kEmberDefinition;
+        }
+    }
+    return false;
+}
+
 bool __fastcall loading_cinematics_suppressed() noexcept {
-    if (core::settings::get().client.suppressLoadingCinematics) {
+    // Read each time: retained host sessions and saved character activity values
+    // can still name Ember after the client has selected orbit or another mission.
+    if (core::settings::get().client.suppressLoadingCinematics && entering_ember()) {
         return true;
     }
     const auto original = reinterpret_cast<Predicate>(g_handle.original);
@@ -27,6 +56,9 @@ bool __fastcall loading_cinematics_suppressed() noexcept {
 
 StageResult stage_loading_cinematics(hooking::detour::Spec& spec) noexcept {
     if (g_handle.attached) return StageResult::attached;
+    g_activityReader = reinterpret_cast<ActivityReader>(
+        scan_main_image_unique(kActivityReader, "loading_cinematics_activity"));
+    if (g_activityReader == nullptr) return StageResult::unavailable;
     const auto* caller = scan_main_image_unique(kCaller, "loading_cinematics_hold");
     if (caller == nullptr) return StageResult::unavailable;
     auto* target = resolve_relative(caller + 31, caller + 35);

+ 1 - 1
Sunrise/src/core/settings/client/definition.h

@@ -32,7 +32,7 @@ struct Settings {
      * suppresses the orbit-side entry cinematic.
      */
     bool skipOrbitCinematicWait{false};
-    /** Force LoadingCinematics_Suppressed before travel; authored mission movies stay native. */
+    /** Force LoadingCinematics_Suppressed only for 1AU; other activities use the native predicate. */
     bool suppressLoadingCinematics{false};
     /**
      * Pins the participation record to the replicated snapshot at `comp + 496`.