Skip to content

Commit 1b9dd0f

Browse files
marthofdoomclaude
andcommitted
DIAG v22 (EQSND): guard-REJECT descriptor vtable census
v21 census was clean of anything enchant/magic — only UI clicks + physics settling. And AddArmorEnchantment/AddWeaponEnchantment (36165/36166) make NO sound call, so MEO's enchant creation isn't the emitter. Leading theory: the black-screen hum is built at a BuildSound site from a descriptor that is NOT a BGSSoundDescriptorForm (raw BGSStandardSoundDef, or a runtime/temp/magic-apply descriptor), which the form-vtable guard silently skips (return 0, no log) — so it's built but invisible. v22: when the form guard rejects a descriptor, still SEH-safely read the descriptor's OWN vtable (no form deref) and log [diag-rej] descVt=+RVA + site. A burst of one vtable at one site during the black-screen phase = the hum; I resolve the vtable's RTTI class offline and design the gate. Form builds still log [diag-bld]. v21 Play census disabled (done: only UI/physics, no replay). All SEH-safe, call-site write_call. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 26fe914 commit 1b9dd0f

1 file changed

Lines changed: 36 additions & 8 deletions

File tree

native/plugin.cpp

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5998,6 +5998,7 @@ namespace sndhook {
59985998
static std::uintptr_t g_sndrVt1 = 0, g_sndrVt2 = 0; // BGSSoundDescriptorForm vtables
59995999
static BuildSoundFn* g_realBuildSound = nullptr; // real 67666 entry (unhooked)
60006000
static std::atomic<int> g_probeLog{ 0 };
6001+
static std::atomic<int> g_rejLog{ 0 }; // v22: guard-rejected descriptor census
60016002

60026003
// POD-only, SEH-guarded. Returns the owning SNDR FormID iff a_desc is the
60036004
// BSISoundDescriptor subobject (form+0x20) of a BGSSoundDescriptorForm; else 0.
@@ -6017,22 +6018,49 @@ namespace sndhook {
60176018
return 0;
60186019
}
60196020

6021+
// v22: SEH-safe read of the descriptor's OWN vtable (no form deref). Lets us
6022+
// type a non-BGSSoundDescriptorForm descriptor (e.g. a raw BGSStandardSoundDef
6023+
// or a runtime/temp/magic-apply descriptor) that the form guard rejects — the
6024+
// leading suspect for the black-screen hum, since it'd be built but never logged.
6025+
static std::uintptr_t ReadDescVtableSafe(void* a_desc) noexcept {
6026+
if (!a_desc) {
6027+
return 0;
6028+
}
6029+
__try {
6030+
return *reinterpret_cast<std::uintptr_t*>(a_desc);
6031+
} __except (EXCEPTION_EXECUTE_HANDLER) {
6032+
return 0;
6033+
}
6034+
}
6035+
60206036
static bool BuildSoundProbe(RE::BSAudioManager* a_self, RE::BSSoundHandle* a_handle,
60216037
RE::BSISoundDescriptor* a_desc, std::uint32_t a_flags) {
60226038
// v21: log EVERY SNDR-form build (not just the 4 vanilla). The load hum plays
60236039
// while the screen is still BLACK = early actor/magic restore, before any 3D —
60246040
// so it's a NON-shader build we've been filtering out. sndr!=0 means the guard
60256041
// matched a real BGSSoundDescriptorForm (non-form descriptors like physics
60266042
// impact return 0 and are skipped — no flood, no fault).
6027-
const RE::FormID sndr = ReadEnchantSndrSafe(a_desc);
6043+
const std::uintptr_t base = REL::Module::get().base();
6044+
const std::uintptr_t siteRva = reinterpret_cast<std::uintptr_t>(_ReturnAddress()) - base - 5;
6045+
const RE::FormID sndr = ReadEnchantSndrSafe(a_desc);
60286046
if (sndr != 0) {
6029-
const std::uintptr_t ra = reinterpret_cast<std::uintptr_t>(_ReturnAddress());
6030-
const std::uintptr_t siteRva = ra - REL::Module::get().base() - 5; // the E8 call site
6031-
const int n = g_probeLog.fetch_add(1, std::memory_order_relaxed) + 1;
6047+
const int n = g_probeLog.fetch_add(1, std::memory_order_relaxed) + 1;
60326048
if (n <= 900) {
60336049
spdlog::info("[diag-bld #{}] SNDR={:08X} site=+0x{:X} flags=0x{:X}",
60346050
n, sndr, siteRva, a_flags);
60356051
}
6052+
} else {
6053+
// v22: descriptor is NOT a BGSSoundDescriptorForm — the form guard rejected
6054+
// it. Log its own vtable RVA + site so we can identify the class (a burst
6055+
// of one vtable at one site during the black-screen phase = the hum).
6056+
const std::uintptr_t vt = ReadDescVtableSafe(a_desc);
6057+
if (vt) {
6058+
const int m = g_rejLog.fetch_add(1, std::memory_order_relaxed) + 1;
6059+
if (m <= 900) {
6060+
spdlog::info("[diag-rej #{}] descVt=+0x{:X} site=+0x{:X} flags=0x{:X}",
6061+
m, vt - base, siteRva, a_flags);
6062+
}
6063+
}
60366064
}
60376065
return g_realBuildSound(a_self, a_handle, a_desc, a_flags);
60386066
}
@@ -6093,8 +6121,8 @@ namespace sndhook {
60936121
tramp.write_call<5>(site, BuildSoundProbe);
60946122
++ok;
60956123
}
6096-
spdlog::info("[snd] v21 all-SNDR BuildSound census: {} sites hooked, {} skipped "
6097-
"(log-only; logs EVERY SNDR build); sndrVt=0x{:X}/0x{:X}",
6124+
spdlog::info("[snd] v22 BuildSound census: {} sites hooked, {} skipped "
6125+
"([diag-bld]=form builds, [diag-rej]=non-form descriptor vtables); sndrVt=0x{:X}/0x{:X}",
60986126
ok, skip, g_sndrVt1, g_sndrVt2);
60996127
}
61006128

@@ -6144,8 +6172,8 @@ SKSEPluginLoad(const SKSE::LoadInterface* skse) {
61446172
SetupLog();
61456173
menuhook::Install(); // must be written before the renderer initializes
61466174
sndhook::Install(); // [snd] enchant-hum gate (Init+0x113 OwnedController mute)
6147-
sndhook::ProbeInstall(); // [snd] v21: ALL-SNDR census at the 39 non-Init BuildSound sites
6148-
sndhook::PlayProbeInstall(); // [snd] v21: valid-handle census at the 66 BSSoundHandle::Play sites
6175+
sndhook::ProbeInstall(); // [snd] v22: all-SNDR + guard-REJECT vtable census at 39 BuildSound sites
6176+
// sndhook::PlayProbeInstall(); // v21 Play census done (only UI/physics, no enchant replay) — disabled
61496177

61506178
const auto gameVersion = REL::Module::get().version();
61516179
spdlog::info("MEO native v1.0.6 loading; runtime {}", gameVersion.string());

0 commit comments

Comments
 (0)