Current reconstruction handoff
September 5, 2026 ยท View on GitHub
This file records only the current durable state. Historical investigations
belong in focused notes such as RUNECL_FUNCTION_EXACT_NOTES.md; live counts
come from the ledgers, not this prose.
Current reconstruction status
Authored source covers 1,107 / 1,107 authored functions; the accepted exact
ledger covers 1,106 / 1,107. The sole authored near match is
ReplayManager::PlaybackExtendedInputAndFps @ 0x004526C0. Live counts come
from scripts/analysis/report-reconstruction-status.py; config/claims.csv
remains header-only.
The authored structural semantic and protocol/readability phases are complete
on main. They preserve two independent oracles: VC7 focused/cold exact
replay for target code and the applicable modern Windows/Linux build/runtime
checks for portable behavior. The portable Linux package workflow runs on
push, pull_request, and workflow_dispatch.
The current protocol closure goes beyond the imported TH06 readability
baseline on the comparable interpreter surfaces. TH08 now names all 184 ECL
opcodes, all 101 ECL operand selectors (0x2710..0x2774), all 35 Background
stream opcodes, and all 17 ECL timeline opcodes. The local TH06 reference has
25 named ECL variables and six named stage opcodes, while its timeline
dispatcher retains 11 numeric cases. TH08 additionally names the complete
Background interpolation/camera-motion and ECL easing domains, replay frame
event bits, and the stable UI/gameplay state protocols encountered in this
audit. These are evidence-bounded names, not names inferred merely from a
numeric value.
Only 74 numeric case labels remain in production/probe source: Player option
array indices and damage quantities, per-file Bullet ANM script IDs whose
visual roles remain ambiguous, and GUI life-count quantities. They are data
or quantities rather than unnamed protocol domains and are deliberately not
given speculative enum names. scripts/check-semantic-protocols.py now
guards the complete named value sets and their dispatch/writer use, including
the secondary streams and closed UI/gameplay states that the structural debt
router cannot see.
Focused VC7 comparison passed the four ECL operand resolvers, complete
Background update, complete ECL timeline runner, and every affected-object
batch. The final single-job non-reuse replay cold-built all 75 configured
objects and passed 1,106 / 1,106 exact with zero failures. A fresh normal
VC7 production image linked, the complete Linux i386 image rebuilt and linked,
and the fixed-layout verifier passed. The sole authored-but-unaccepted
ReplayManager::PlaybackExtendedInputAndFps @ 0x004526C0 remains unchanged and
outside this semantic checkpoint.
The first accepted source batch is the ScreenEffect +0x18/+0x1C/+0x20
variant-parameter family. A TU-local typed overlay records fade color, shake
amplitude, pulse repeat/color, and three-phase shake-envelope views while
preserving both the generic RegisterChain initialization ABI and the shared
header byte-for-byte. Focused VC7 replay passed ScreenEffect.obj 16 / 16
and its canonical objdiff path 5 / 5; the normal VC7 image linked, the
complete i386 container build linked, and the fixed-layout verifier passed.
The required cold aggregate diagnostic initially exposed a reproducible branch-baseline blocker at 1,017 / 1,105. The follow-up bounded maintenance batch has now closed it. It restored target-observed direct-global and source- expression shapes where the portable-owner pass had changed VC7 extent/codegen, introduced target/modern storage bridges where the two linkers intentionally differ, and migrated only relocation entries whose masked function bytes were otherwise identical. All 79 final relocation-only failures had zero non-relocation byte differences; all 338 changed/new relocation entries inferred the existing target bases without conflict and had mapping/global-ledger evidence.
The current attested result is a non-reuse cold build of all 75 configured
comparison objects followed by 1,105 / 1,105 exact accepted units with no
failures. A normal VC7 production link passes. The complete i386 Linux
container build and fixed-layout verifier also pass. This closes the aggregate
blocker without changing authored progress or weakening the comparator; see the
post-port reconciliation entry in docs/SEMANTIC_RECONSTRUCTION.md for the
evidence split and reusable compatibility pattern.
The next accepted source batch recovers the Player deathbomb/Bomb lifecycle.
It replaces the raw Player + 0xFDC..+0x1014 active/type/duration/timer and
calculation/draw callback views, plus the +0xE2A68/+0xE2A6C/+0xE2B28
deathbomb window, Bomb-input lock, and effect pointer, with asserted typed
fields. TH08 target users cover Die, the Bomb update/draw paths, the dying
transition, initialization, shot/collision gates, and Spellcard::EndSpell;
TH06 only corroborates the stable active/duration/timer/calc/draw roles. The
individual callback-slot meanings and retained opaque fields remain unknown.
Focused replay passed 249 / 249, a new non-reuse cold replay passed 1,105
/ 1,105, the normal VC7 link passed, and the complete i386 Linux build/layout
verifier passed. No match manifest, authored/exact ledger, global identity, or
runtime state operation changed.
The next accepted batch closes the Laser lifecycle family across
BulletManager::RemoveAllBullets, DespawnBullets, SpawnLaserPattern,
OnUpdate, OnDraw, and seven Laser-control opcodes in the complete exact
EclManager::RunEcl. Raw +0x548..+0x599 views now use the asserted Laser
owner for position, beam geometry, speed, timers, hitbox windows, active state,
and the starting/active/despawning state machine. The +0x599 byte is named
hideCapDuringStartup from its RunEcl write and draw-side gate; flag-bit
meanings, the color convention, and the final two bytes remain unknown.
Focused replay passed BulletManager 24 / 24 and RunEcl 1 / 1; a new
non-reuse cold replay passed 1,105 / 1,105, the normal VC7 link passed, and
the complete i386 Linux build/layout verifier passed. No match manifest,
ledger, global identity, initialization order, or state operation changed.
The latest accepted batch closes the core Bullet lifecycle across spawn,
transform dispatch, removal/despawn, update/collision/culling, six draw
buckets, rendering, sprite-template setup, and nine exact ECL extension
handlers. Raw Bullet + 0xD44..+0x10B4 and manager-tail views now use asserted
owners for kinematics, timers, state, transform masks, graze/collision gates,
draw links, and pool bookkeeping. The transform-specific BulletExState
payloads, ANM VM internals, Bullet + 0xDBC, template +0xD40, and the
gameplay role of the proven-Float3 member at +0xD5C remain explicitly
unknown. Focused production/canonical replay passed 70 / 70; a final
non-reuse cold replay passed 1,105 / 1,105, the normal VC7 image linked,
and the final complete i386 Linux build/layout verifier passed. The semantic
router falls from 294 to 97 candidates in BulletManager.cpp and 25 to 18 in
EclExIns.cpp; these are review deltas, not progress percentages. No match
manifest, ledger, global identity, state operation, or rendering sequence
changed.
The next accepted batch closes BulletExState[7], the transform-specific
runtime family at Bullet + 0xF80..+0x10B3. The dispatcher, update path, and
nine handlers now use asserted state slots and named timer, acceleration,
direction-change, bounce, wait, and wrap fields; the transform flag namespace
also names spawn modes, runtime actions, spawn sound, and cancel immunity.
The seven-element nontrivial array remains intact because its VC7 vector
construction is target-visible. Generic tagged-record operands and the
deceleration slot's unconsumed Float3::z role remain explicitly neutral.
Focused BulletManager.obj replay passed 37 / 37; a required non-reuse cold
replay passed 1,105 / 1,105, the normal VC7 image linked, and the complete
i386 Linux build/layout verifier passed. The semantic router for
BulletManager.cpp falls from 97 to 29 candidates. No match manifest, ledger,
layout, construction order, or state operation changed. The adjacent bullet
spawn/ECL descriptor family is the next bounded milestone-closing batch.
The following accepted batch closes that adjacent bullet/laser spawn and ECL
descriptor family. Enemy + 0x2E24/+0x3070 are now asserted
bulletSpawnDescriptor and laserSpawnDescriptor owners; ECL opcodes 109,
111, and 113..115 use typed descriptor, transform-record, sound, aim, and laser
operand fields, while the exact shot dispatcher at 0x00422720 uses the same
bullet owner. The nine target-observed aim modes and player-human/youkai shot
conditions are named; tagged transform operands and descriptor unknown1FA
remain deliberately neutral. Focused replay across the five affected objects
passed 102 / 102, a required non-reuse cold replay passed 1,105 / 1,105,
the normal VC7 image linked, and the complete i386 Linux build/layout verifier
passed. No match manifest, ledger, ABI, construction order, or state operation
changed.
This formally closes the first semantic milestone, core bullet-gameplay loop semantic closure: Player deathbomb/Bomb, Laser lifecycle, Bullet core lifecycle, Bullet transform runtime state, and the adjacent spawn/ECL descriptor family are all typed and dual-oracle locked. The claim is limited to those bounded families and does not imply whole-program semantic completion. A useful next milestone is Enemy/ECL orchestration closure, starting from the still-raw Enemy motion/rank/phase fields adjacent to these descriptors rather than reopening the now-closed bullet loop.
The first accepted batch toward Enemy/ECL orchestration closure types the
Enemy bullet-rank and repeated-shot scheduling family. The six
Enemy + 0x2DEC..+0x2DFA rank endpoints now form an asserted
EnemyBulletRankInfluence; the +0x3034 delayed-shot snapshot remains an
explicit 0x2C-byte ECL protocol buffer, followed by named
shootIntervalFrames @ +0x3060 and shootIntervalTimer @ +0x3064 fields.
Producers, consumers, and reset paths across RunEcl, the exact shot dispatcher,
Enemy phase transitions, Spellcard reset, and EnemyManager update share those
owners. Focused replay passed 71 / 71, a non-reuse cold replay passed
1,105 / 1,105, the normal VC7 image linked, and the complete i386 Linux
build/layout verifier passed. The next coherent family is the adjacent Enemy
combat identity/life/score/boss-timer state at +0x2DFC..+0x2E20, followed by
its phase threshold/callback owners.
The second Enemy/ECL orchestration batch closes that adjacent combat-state
range. Enemy + 0x2DFC..+0x2E20 now exposes current/maximum life, a neutral
phase-starting-life value, death score, pool index, per-Enemy Player-shot hit
accumulator, Boss timer, and ANM display color. All spawn, ECL, phase, damage,
death, Player, Spellcard, operand-resolver, and EX callback users share the
asserted owner; phaseStartingLife remains explicitly inference-limited
because the target proves its writes but no current authored reader.
Focused replay passed 163 / 163, a non-reuse cold replay passed 1,105 /
1,105, the normal VC7 image linked, and the complete i386 Linux build/layout
verifier passed. The next coherent family is the Boss life/timer threshold and
callback table at Enemy + 0x3358..+0x3384, together with the adjacent ECL
subroutine identifiers and phase-gauge state that consume it.
The third Enemy/ECL orchestration batch closes the Boss phase callback and
child-ECL ownership range at Enemy + 0x3354..+0x3393. The shared owner now
names last applied damage, four life thresholds and their parallel dword ECL
subroutine identifiers, the timer threshold/subroutine pair, linked-child
count, and four owned child ECL block pointers. The dword callback IDs retain
their exact write width while call sites explicitly consume their low signed
16 bits; each 0x24B0 child block remains byte-oriented because only its
allocation protocol and partial internal offsets are proven. All opcode,
RunEcl, PopEclContext, phase/death cleanup, Spellcard, GUI, resolver, spawn,
and update consumers share the asserted fields. Focused replay passed 110 /
110, target-pinned packets for both transitions and cleanup replayed exact,
a non-reuse cold replay passed 1,105 / 1,105, the normal VC7 image linked,
and the complete i386 Linux build/layout verifier passed. The next coherent
family is active ECL context/subroutine state around +0x2CA0..+0x2D30, then
the motion vectors and phase/control flags that consume it.
The fourth Enemy/ECL orchestration batch closes the interpreter/context state.
The constructor-bearing primary context at Enemy + 0x07F8, its sixteen-entry
call stack at +0x0A20, active context/stack pointers, local and per-Enemy ECL
variable families, call parameters, callback/interpolation state, primary and
active call depths, death callback, 32-entry subroutine table, and pending
subroutine index now share asserted owners. The constructor-bearing storage
uses the same EnemyEclContext and EnemyEclInterpolationSlot types as the
interpreter instead of duplicate imported UnkStruct shells. The +0x2CEC word, context +0x21C
dword, callback argument payload, and stage-specific meanings of extra ECL
variables remain explicitly unknown. Focused replay passed 124 / 124,
target-pinned CallSubOnEnemy/PopEclContext packets replayed exact, a non-reuse
cold replay passed 1,105 / 1,105, the normal VC7 image linked, and the
complete i386 Linux build/layout verifier passed. The next coherent family is
the Enemy motion controller at +0x2D34..+0x2DEB, then its phase/control flags
and boundary/presentation state.
The fifth Enemy/ECL orchestration batch closes the continuous motion
controller at Enemy + 0x2D34..+0x2DEB. Logical, offset, previous,
last-frame-displacement, and world positions; velocity; both hitboxes; linear
and polar movement parameters; parent linkage; shot offset; and timed
interpolation state now share asserted owners across ECL, Enemy, Player,
Effect, and Spellcard users. The two anonymous core functions are tracked as
Enemy::UpdateMovement @ 0x00422C40 and
Enemy::IntegrateVelocity @ 0x0042DEB0. ECL opcode 169 retains one
named/layout-asserted offset constant
because a direct member expression changes six VC7 x87 bytes; the complete
RunEcl function remains exact. Focused replay passed 287 / 287, three
target-pinned motion packets replayed exact, a non-reuse cold replay passed
1,105 / 1,105, the normal VC7 image linked, and the complete i386 Linux
build/layout verifier passed. The next coherent family is the control flags
at +0x3324/+0x3328 and the adjacent boundary/presentation state.
The sixth Enemy/ECL orchestration batch closes the adjacent control and
presentation range at Enemy + 0x3304..+0x3353. Item-drop policy/counts,
death animations, Boss slot, damage flash, both flag dwords, animation
direction/draw group/difficulty override, the six-script animation table,
movement bounds, and minimum Player shot distance now share asserted owners
across ECL, Enemy, Player, Bomb, Spellcard, Effect, and timeline users. Named
shift constants preserve target-observed VC7 shift-and-test source shape; a
mask-test trial changed 26 units and was rejected. Twelve important Enemy
methods now carry behavior-backed names. Focused replay passed 328 / 328,
three target-pinned packets replayed exact, a non-reuse cold replay passed
1,105 / 1,105, the normal VC7 image linked, and the complete i386 Linux
build/layout verifier passed. Retained padding, timer3318, neutral flag
labels, and unobserved bits remain explicit unknowns. The next coherent
family is the trail/effect/death tail at +0x534C..+0x53CC, followed by the
EnemyManager pool/list owners needed to close the Enemy/ECL orchestration
milestone.
The seventh Enemy/ECL orchestration batch closes the Enemy trail and attached-
effect tail. The 96 position/velocity/angle samples, trail flags and three
length/stride controls, damage-reduction timer, 24 attached Effect pointers,
Effect count/target distance, alignment Effect, and phase-end seconds now
share asserted owners across RunEcl, update, draw, collision, culling, linked-
child, phase/death, and score-drop paths. The shared Effect radius, angle,
active, and release-requested fields are also asserted. The anonymous
0x1C record constructor is tracked as EnemyTrailSample::EnemyTrailSample.
Focused replay passed 93 / 93, four target-pinned packets replayed exact,
a non-reuse cold replay passed 1,105 / 1,105, the normal VC7 image linked,
and the complete i386 Linux build/layout verifier passed. The one retained
padding byte, unused trail bits, generic cross-effect vector roles, and a
producer-only phase-end value remain evidence-limited. The EnemyManager
pool/list and global update-state fields are the final coherent family before
declaring Enemy/ECL orchestration closure.
The eighth Enemy/ECL orchestration batch closes that final manager family.
The spawn template, 481-element Enemy array (480 active pool entries plus the
target-visible failure sentinel), eight Boss slots, drop scheduler, active
count, opcode-163 state, sixteen timelines, manager timer, four draw-list
heads, two Enemy ANM banks, spawn-failure state, four timeline event slots,
and timeline-spawn suppression now share asserted EnemyManager owners. The
old g_EclEnemyTableF54CC0 and two drop-counter globals were overlapping
analysis aliases inside g_EnemyManager, not separate production storage;
their declarations, Linux aliases, ledgers, and relocation identities are
retired. Important helpers are tracked as UpdateSubrank,
KillAllNonBossEnemies, and HasBoss. Focused replay passed 316 / 316,
seven target-pinned packets replayed exact, a non-reuse cold replay passed
1,105 / 1,105, the normal VC7 image linked, and the complete i386 Linux
build/layout verifier passed. This formally closes the bounded Enemy/ECL
orchestration semantic milestone. The next high-value milestone is Player
core state together with its adjacent PlayerBomb protocol; whole-program
semantic completion remains open.
The first accepted Player-core batch closes movement, collision/graze/item
bounds, option focus transitions, and the 20-frame shooting cycle. The Player
owner now exposes its 16-position movement history, three AABB pairs and half-
sizes, time-scaled velocity, axis multipliers, current speeds, movement
direction enum, focus/extreme-gauge effects, typed option callbacks, and
shotTimer. The linked SHT header now names the target-proven initial Bomb,
deathbomb, hitbox/graze/autocollect/item/point-line, and normal/focused speed
fields. Strict replay caught and corrected the point-line displacement from
the initial +0x20 hypothesis to target +0x1C; the accepted layout keeps
+0x20 unknown. Eleven important helpers now carry behavior-backed collision,
graze, movement, angle, and shooting names. Focused replay passed 242 / 242,
eleven target-pinned packets replayed exact, a non-reuse cold replay passed
1,105 / 1,105, the normal VC7 image linked, and the complete i386 Linux
build/layout verifier passed. position2 @ +0x2C0, unconsumed vector
components, several gauge timers, PlayerShot internals, and most PlayerBomb
work-item protocols remain open. The next coherent batch is PlayerShot runtime
and SHT descriptors, followed by the adjacent PlayerBomb work-item callbacks.
The second accepted Player-core batch closes that PlayerShot/SHT family. The
SHT owner now exposes its power-level rows and 0x38-byte shot descriptors,
including schedule, geometry, damage/type/animation/sound metadata and four
typed callbacks. PlayerShot remains 0x484 bytes while naming its VM,
position history, hitbox/velocity/speed/angle, timer/state/type, persistent
timeline metadata, tint, callbacks, and originating descriptor. Player owns
the persistent descriptor slots, shot-hit effect counter, and base shot angle.
Twenty-one helpers now carry behavior-backed initialization, scheduled spawn,
homing/falling/persistent/trail, update/draw, hit-behavior, and damage names;
CalcDamageToEnemy @ 0x00451670 matches TH07 only as corroboration and remains
accepted against TH08 target bytes. Focused Player plus EnemyManagerUpdate
replay passes 52 / 52, including all 22 directly edited units. The shared
header replay then passed 1,105 / 1,105 accepted units from a single-job
cold VC7 build; the normal VC7 image linked, and the complete i386 Linux build
and fixed-layout verifier passed. Remaining neutral fields include SHT
header +0x20/+0x34, shot +0x448/+0x46D/+0x471, and the wider PlayerBomb
work-item protocol. PlayerBomb is the next coherent Player-core family.
The third accepted Player-core batch closes the PlayerBomb callback protocol.
The 24-row table is now g_PlayerBombCallbacksByShotType, with typed
primary/secondary/deathbomb/special variants and paired update/draw rows. The
16 Shift-JIS cut-in strings directly name the Fantasy Orb, Quadruple Barrier,
Master Spark/Artful Sacrifice, Killing Doll/Red Nightless Castle, and
Present-World Slash/Ghastly Dream families plus their deathbomb forms. The
0x16F0 work item now exposes its state timer, motion scalar/vector state,
path points, eight VMs, effect VM, timer, and damage/cancellation regions with
assertions through +0x16EC. Timer predicates are now HasTicked,
JustReached, and IsPeriodic. Focused replay passes PlayerBomb 59 / 59,
Player 77 / 77, EnemyManager 39 / 39, and ECL extensions 33 / 33.
The single-job cold VC7 replay passes 1,105 / 1,105, the normal VC7 image
links, and the complete i386 Linux build and fixed-layout verifier pass.
PlayerBombState +0xC remains unknown; the next coherent batch is shared
Effect/ANM interpolation state.
The fourth accepted Player-core batch closes that shared Effect/ANM family.
Effect remains exactly 0x360 bytes and now exposes its leading VM, nine
constructor-bearing vectors, radius/angle and strip geometry, pool slot,
secondary radial state, timer, callbacks, signed lifecycle/draw bytes, vertex
storage, and draw-list link. EffectManager now asserts the 654-row pool,
five individually constructed sentinels, five draw tails, tamper counter, and
two ANM banks through its exact 0x8B05C extent. Player/PlayerBomb/ECL/Enemy
users share those owners. The four interpolation setters are behavior-named,
and their timer/mode/initial/final fields replace open-coded offsets; mode 5 is
recorded as EaseOutCubic and mode 4 as EaseOut. Focused replay passes
Player/PlayerBomb 136 / 136 and ECL/EnemyManagerUpdate/EffectManager
86 / 86. The single-job cold VC7 replay passes 1,105 / 1,105, the
normal VC7 image links, and the complete i386 Linux build and fixed-layout
verifier pass. Constructor evidence requires Float3 + f32 at Effect
+0x304, five separate sentinel members, and signed lifecycle bytes; the two
fixed-slot spawners retain their target-shaped sizeof/offsetof expression.
Generic per-effect vectors and callback signatures remain evidence-limited.
The next coherent family is remaining Player core state, then Background.
The fifth accepted Player-core batch closes that remaining state/controller
family. The two 192-entry collision pools are now asserted
damageRegions/cancelRegions of PlayerCollisionRegion; their four
allocators distinguish rectangular/circular damage and cancellation regions,
and every PlayerBomb/Enemy consumer shares the same type. The Player state
machine now names Bomb/deathbomb update, death and respawn, respawn animation,
invulnerability, collision-region lifetime, and gauge-position updates. State
3 is PLAYER_STATE_INVULNERABLE, matching its timer, flashing, and collision
suppression behavior rather than the former DEAD label.
The option controller now has a uniform PlayerOptionState * callback ABI.
Homing helpers use typed position/velocity/timer/VM members, the draw callback
uses the embedded VM directly, and seven table callbacks carry conservative
behavior names for homing, Bomb anchoring, orbit, facing, and paired-option
motion. AnmVmBase exposes the scale/color/flag word used by Player, and the
six run/stage active/extreme-gauge frame counters in GameManager are named
across setup, Player, and GUI consumers. Focused replay across the seven
directly affected production objects passes 258 / 258. A non-reuse
single-job cold VC7 replay passes 1,105 / 1,105, the normal VC7 image
links, and the complete i386 Linux build plus fixed-layout verifier passes.
The Player source/header/Bomb router is down to one raw-member candidate, the
separate Background tint byte. The next high-value family is the Background
camera/stage object model; whole-program semantic completion remains open.
The first accepted Background batch closes the camera, stage-object,
stage-script, fog, spell-background, and tint model. Background retains its
exact 0x6600 extent while naming the loaded stage buffer and serialized
records, object/quad tables, stage position and script state, text and Effect
owners, five BackgroundCamera records, interpolation lanes, spell VM bank,
culling/tint state, and special-effect points. Important helpers are now
InterpolateCameraVector, CubicHermiteInterpolate, AccumulateTint,
UpdateStageObjectVms, StartSpellBackground, and StopSpellBackground.
ECL opcode 147's former g_EclGlobal004EA290 identity is proven to be
g_Background.pendingStageScriptLabel @ +0x6260; the overlapping VC7/Linux
storage alias is retired and RunEcl remains 26,638 / 26,638 exact. The
later replay-runtime batch also closes the former stage-7 absolute view as
g_GameManager.currentStage == STAGE6B, with its target owner/addend recorded
as a COFF relocation. Focused replay passes Background 29 / 29 and the selected affected
consumer objects 487 / 487. A non-reuse single-job cold VC7 replay passes
1,105 / 1,105, the normal VC7 image links, and the complete i386 Linux
build plus fixed-layout verifier passes. The whole-source router now reports
207 raw-member, 82 absolute-address, 432 anonymous-identifier, and 54 opaque-
storage candidates; Background itself has zero raw-member candidates. These
are routing observations, not completion percentages. GameManager and its
GUI/setup consumers are the next high-value coherent family.
The first accepted GameManager-core batch recovers the runtime/setup state
used by OnUpdate @ 0x00439BC7, GameplaySetupThread @ 0x0043ABD7, replay,
Player, GUI, and stage teardown. The typed owner now names gameplay setup and
startup states, frame skipping, run/stage playtime, humanity-rate counters,
stage-at-start and clear-mask state, replay pause recording, next Supervisor
state, character-list index, and the stage RNG seed. Setup writes now use
asserted ZunGlobals and GameConfiguration members; in particular the
target's +0x80/+0x84/+0x88 bomb triplet is respectively bombs remaining,
bombs used, and bombs used in the current stage. The score.dat/CATK/CLRD/PSCR
initializer at 0x0043BBE1 is now InitializeScoreData in source and every
mapping/comparison ledger. Packed flag masks retain one ownership-aware word
view where VC7's emitted mask operations are target-visible.
Focused GameManager replay passes 42 / 42, selected affected consumers
pass 493 / 493, a new single-job non-reuse cold replay passes 1,105 /
1,105, the normal VC7 production image links, and the complete i386 Linux
build/layout verifier passes. Evidence-limited bytes at
+0x3DBB6/+0x3DBB7/+0x3DDBE, plus unresolved fields at
+0x3DDCC/+0x3DDD2/+0x3DE0C, remain explicitly unknown. The whole-source
router now reports 201 raw-member, 82 absolute-address, 310 anonymous-
identifier, and 54 opaque-storage candidates. GUI's boss/status/message
state is the next coherent owner.
The adjacent authored-exact side investigation resolves
TitleScreen::RegisterChain @ 0x0047146D. GensokyoClub commit 1b630bb
supplied the decisive natural source-shape hypothesis:
ZUN_NEW(TitleScreen, "TitleInf") instead of raw new TitleScreen(). In the
retail build the inline registry wrapper returns the allocation directly, but
the macro expression changes VC7's hidden-new/EH local lifetime and restores
the target 0x40 frame. The dedicated comparator replays the complete 281
/ 281 bytes and all 20 relocations, including local EH metadata. The
"TitleInf" debug registry label is compiled out and therefore remains
upstream provenance, not a target-observed string claim. A new single-job
non-reuse cold replay passes 1,106 / 1,106, the normal VC7 image links,
and the complete i386 Linux build/layout verifier passes. Authored exact is
now 1,106 / 1,107 functions and 459,396 / 459,757 bytes; only
ReplayManager::PlaybackExtendedInputAndFps @ 0x004526C0 remains unaccepted.
The first accepted GUI-core batch closes its message, Boss HUD, stage-element,
stage-clear, popup, capture, transition, and clock ownership. GuiImpl is
asserted through its exact 0x230B8 extent; the message state is correctly
bounded at 0x1570, leaving the following stage-clear state/total dwords as
their own fields. The +0x22DEC stage-clear record, all important VM banks,
Boss-life target/display interpolation, formatted-popup mode, and the three
Supervisor stage-resource flags now have shared typed owners. The clear-stage
bonus global at 0x004C7158 is corrected from a pointer array to the target-
observed integer array g_GuiStageClearBonuses.
Twenty-five high-use GUI and helper symbols now carry behavior-backed names,
including StartMessage, UpdateStageElements, DrawStageElements,
DrawStageClearScreen, ShowBonusScore, ShowPopupText,
ShowSpellcardBonus, SetBossLifeBarTarget, SetBossTimerSeconds,
CopyCurrentStageEnemyNameTexture, CaptureArcade, and the clock controls.
All mapping/implemented/reccmp/match and relocation identities moved with the
source. Focused GUI replay passes 41 / 41; selected affected callers pass
224 / 224; a single-job non-reuse cold build of all 75 comparison objects
passes 1,106 / 1,106. The normal VC7 image links, and the complete i386
Linux build plus fixed-layout verifier passes. Gui.cpp now has zero raw-
member candidates; the whole-source router is 116 raw-member, 82 absolute,
280 anonymous, and 49 opaque candidates. These counts are routing aids only.
The adjacent GUI message-protocol batch is now accepted. The complete 0..22
opcode set has behavior names, and the variable-size record/file format now
uses asserted typed payloads and pointers while retaining its real byte-stream
advance and in-place offset fixups. Target evidence proves the signed-word,
unsigned-byte, dword, portrait, wait/music, and encrypted-text accesses;
GensokyoClub's current declarations are corroboration only. The XOR helper at
0x004353EC is now DecryptGuiMessageText in source and all ledgers.
Focused GUI replay passes 41 / 41; RunMsg is exact over 5,597 authored
bytes and its full 5,689-byte code-plus-table extent. A single-job
non-reuse cold build passes 1,106 / 1,106, the normal VC7 image links, and
the complete i386 Linux build plus fixed-layout verifier passes.
The Spellcard effect/reward lifecycle is now accepted across Init,
StartSpell, EndSpell, OnUpdateImpl, and the ECL-facing
SetStoredVector. Spellcard +0xF4/+0xF8 are typed Effect * owners; their
ANM position/RGB/scale interpolation banks, sprite/visibility state, effect
position/tracking vector, ring geometry, lifetime timer, and active state
replace every raw effect offset in Spellcard.cpp. Target-visible float
field transfers remain explicit dword bit copies. Focused Spellcard replay
passes 29 / 29, focused EclManager replay 10 / 10, and a required
single-job non-reuse cold replay passes 1,106 / 1,106. The normal VC7
image links, and the complete i386 Linux build plus fixed-layout verifier
passes. Spellcard.cpp is now zero-candidate in all router categories; the
unproven Spellcard::unknown_044 range remains neutral. The whole-source
router is 87 raw-member, 82 absolute, 271 anonymous, and 49 opaque candidates.
The Enemy update/render batch is now accepted. The complete
EnemyManager::OnUpdate @ 0x0042C660 uses the shared Enemy, Player,
GameManager, ReplayManager, VM, sprite, and draw-list owners instead of its
private raw overlay family; EnemyManager::OnDrawImpl @ 0x0042E140 consumes
the same typed VM and Enemy::nextInDrawGroup fields. Target replay also
caught two important false hypotheses: Player +0x000 is the Player state
machine rather than character type, and loaded-sprite +0x30/+0x34 are the
pixel height/width fields. The phase-capture helper at 0x0042BC50 now uses
Spellcard flags and bonusProgress directly while retaining its ABI.
Focused replay matches OnUpdate 6,198 / 6,198, canonical EnemyManager
23 / 23 including OnDrawImpl 2,504 / 2,504, and the production subset
16 / 16. The required single-job non-reuse cold replay passes 1,106 /
1,106, the normal VC7 image links, and the complete i386 Linux build plus
fixed-layout verifier passes. EnemyManagerUpdate.cpp is zero-candidate in
all router categories. The later replay-runtime batch proves former absolute
0x018B8A24 as g_Player.damageAccumulatorThreshold @ +0xE2B2C from its
Player compare/subtract consumers and Enemy initialization. The whole-source
router is now 60 raw-member, 82 absolute, 255 anonymous, and 49 opaque.
The Ascii presentation batch is now accepted. Boss-marker state/interrupts,
the shared VM update, frame timer, retry initial-Bomb read, and Supervisor
transition countdown now have behavior-backed owners and names. The
AsciiManager +0x16F04/+0x16F08/+0x16F0C tail is the Mystia night-blindness
radius, integer alpha, and center VM protocol, with
ConfigureNightBlindness @ 0x00423390 publishing its ECL parameters.
GensokyoClub's current names corroborated this interpretation; target access
patterns and strict replay accepted it, while the target-observed integer
alpha was retained instead of upstream's ZunColor view.
The former globals at 0x004E3D24/0x004E3D28 are retired as overlapping
aliases inside g_AsciiManager. The former
g_AsciiManagerDemoAnm0577EB4 is likewise retired: 0x00577EB4 is exactly
g_EffectManager.effectAnm @ +0x8B054, now shared directly by Bullet,
PlayerBomb, Spellcard, ECL extensions, and Ascii rendering. Their source,
relocation, global-ledger, and Linux layout identities moved together.
The rebuilt affected-object selection passes 353 / 353, with the Ascii object at 63 / 63 and a separately regenerated canonical selection at 72 / 72. A required single-job non-reuse cold replay passes 1,106 / 1,106, the normal VC7 image links, and the complete i386 Linux build plus fixed-layout verifier passes. Only five Ascii candidates remain: the unidentified script-9 VM, reset-only bool, and unused popup tail field. The whole-source router is now 60 raw-member, 82 absolute, 218 anonymous, and 48 opaque candidates.
The GameManager setup/runtime batch is now accepted. The target's nineteen
0x14-byte spell-practice music records at 0x004C7670, nine three-song stage
contexts at 0x004C7240, and the STD header's +0x90/+0x290 song-name/path
arrays now have shared typed owners. The helpers at 0x00439916/0x00439961
are GameManager::ShouldPauseMusicInSpellPractice and
GetSongNameSpriteIdx; setup error/resource addresses are source literals
with target-replayed relocations. GensokyoClub supplied strong table/name
hypotheses, but canonical TH08 data and VC7 replay remain the acceptance
evidence.
The byte at 0x0160F534 is now correctly owned as
g_GameManager.scriptedUpdateFreeze @ +0x2C. The ECL setter at 0x00425070
and all Bullet, Enemy, GUI, Player, Spellcard, and ScreenEffect consumers use
that owner; eight manifest relocations now require g_GameManager + 0x2C, and
the false standalone global/storage/Linux alias is gone. The same batch names
the Supervisor viewport, recorded replay FPS, loading-VM and worker-thread
state; active Bullet count; Background sky-fog color; RNG saved seed;
stage-clear replay gate; anti-tamper heap-jitter allocation; and restart play
count. Unused/reset-only GameManager tail fields remain neutral.
Focused replay passed EclExIns 33 / 33, Background 24 / 24, GameManager 33 / 33, the broad affected selection 333 / 333, SpellCard 29 / 29, and the final setup/Supervisor/replay/title selection 251 / 251. The normal VC7 image and probes link, and a required single-job cold replay of all 75 comparison objects passes 1,106 / 1,106 exact. The complete i386 Linux container build links and its fixed-layout verifier passes after removing the duplicate initialization/alias shims. No isolated runtime smoke exists for spell-practice music or scripted whole-update freeze, so none is claimed. The semantic router now reports 60 raw-member, 18 absolute, 194 anonymous, and 45 opaque candidates; these remain routing counts only.
The Gensokyo TitleScreen::RegisterChain side hypothesis is conclusively
confirmed in this cold environment: the existing ZUN_NEW(TitleScreen, "TitleInf") source replays all 281 / 281 bytes and 20 relocations at
0x0047146D, including its 0x40 target frame. It is already committed in
the earlier GameManager/title checkpoint; no new merge is required.
The Bullet transform/runtime-owner batch is now accepted. Absolute
0x017CE8E0 is g_Supervisor.framerateMultiplier @ +0x188, not an
independent ECL global: all 43 existing relocations and 11 newly manifested
raw-address uses replay with the aggregate owner/addend, and the duplicate
storage, global row, and Linux linker alias are gone. Bullet transform
helpers at 0x0042F5F0, 0x0042FFC0, 0x00430E10, and
0x00432170 through 0x00432AA0 now carry lifecycle/action names; their ANM,
ReplayManager, Effect, Player bomb-state, and ECL-instruction accesses use
asserted fields. Focused replay passes 70 / 70, the required cold replay
passes 1,106 / 1,106 exact, the normal VC7 image links, and the complete
i386 Linux build plus fixed-layout verifier passes. The remaining
Bullet +0xDBC and template +0xD40 observations stay neutral.
The Replay/Supervisor runtime batch is now accepted. Replay stage payloads and live state distinguish captured spellcards, input bytes, playback and recording cursors/end tables, FPS samples, per-frame RNG seed, and event flags. Supervisor now exposes startup-worker result, FPS-display suppression, and recording/playback FPS warnings. Effect initialization/update callbacks, quality selection, pool slots, Spellcard time limits, ANM flags, and Item collection bounds use their asserted owners.
All safely attributable absolute views are closed: Background stage 6B,
GameManager gameplay-frame reads, Player singleton Bomb/input reads,
g_Player.damageAccumulatorThreshold, and ECL opcode 148's nested CATK maximum
bonus now use aggregate members. Their COFF owner/addend relocations are
explicit in the manifests; signed target comparisons remain signed. Broad
focused replay passes 319 / 319, a required single-job cold replay passes
1,106 / 1,106 exact, the normal VC7 image links, and the complete i386
Linux build plus fixed-layout verifier passes. The router now reports 5 raw,
0 absolute, 166 anonymous, and 44 opaque candidates. The five raw candidates
are deliberately retained evidence gaps in Bullet/template and SHT item
collection state. MIDI and SoundPlayer are the next dense semantic owners.
The MIDI/SoundPlayer protocol batch is now accepted. MIDI track and output state distinguish data/running-status cursors, event ticks, loop points, tempo epochs, elapsed ticks/milliseconds, pending long messages, controller/meta events, device/transposition state, and fade handling. SoundPlayer command records use typed preload/load/stop/release/fade/pause/unpause/volume opcodes; their arguments, paths, per-effect queue counts, preloaded BGM storage, loaded slot, and streaming-file base offset now have shared owners across GameManager, Supervisor, SoundPlayer, and zwave. Fields with only reset or no authored consumer remain explicitly neutral.
Focused replay across the five affected objects passes 169 / 169 exact,
including MidiOutput::ProcessMsg at 1,871 / 1,871 bytes and
SoundPlayer::ProcessQueues at 2,358 / 2,358 bytes. A required single-job
cold replay passes 1,106 / 1,106 exact, the normal VC7 image links, and the
complete i386 Linux build plus fixed-layout verifier passes. The router now
reports 5 raw, 0 absolute, 121 anonymous, and 44 opaque candidates. The next
batch should continue from behavior-backed UI/result or remaining manager
state; do not invent meanings for the residual reset-only audio words.
The Replay file-envelope/callback batch is now focused-accepted. Generic
callback names now state whether a callback captures frame sync,
records input/FPS, plays ordinary or extended input/FPS, or controls playback
frame advancement. Recording/playback stage setup and manager teardown have
matching lifecycle names, and their chain members share those roles. Header
+0x06/+0x07/+0x15 now expose extended-record selection, the appended USER
section marker, and the byte-obfuscation key; the random header/payload bytes
remain deliberately role-neutral.
Focused ReplayManager replay passes 17 / 17 exact, including RegisterChain
777 / 777, recording setup 1,315 / 1,315, playback setup 787 / 787,
and all accepted callback bodies. The sole authored backlog entry is still
ReplayManager::PlaybackExtendedInputAndFps @ 0x004526C0: its behaviorally
reconstructed six-byte-record source emits 362 bytes versus the 361-byte target
because of the documented VC7 register-allocation phase. It is not added to
the exact ledger. The required single-job cold replay passes 1,106 / 1,106
exact, the normal VC7 image links, and the complete i386 Linux build plus
fixed-layout verifier passes. The whole-source router is now 5 raw, 0
absolute, 116 anonymous, and 44 opaque candidates.
The ScreenEffect lifecycle/mode batch is now accepted. The formerly numeric
mode family now distinguishes playfield pulses, full/playfield loading-overlay
holds, and amplitude-envelope shake. The pulse, draw, envelope, initialization,
deletion, and release functions all have behavior-based names; +0x10 is the
shared overlay alpha and +0x24 is the loading-overlay release request. The
three variant words retain neutral storage names underneath their already
typed TU-local views, and unused +0x0C remains unknown.
ScreenEffect's two focused selections pass 16 / 16 and 5 / 5 exact;
the affected ECL/Player/Supervisor production selection passes 132 / 132.
A required single-job cold replay passes 1,106 / 1,106 exact, the normal
VC7 image links, and the complete i386 Linux build plus fixed-layout verifier
passes. ScreenEffect.cpp has zero router candidates; the whole-source router
is now 5 raw, 0 absolute, 113 anonymous, and 44 opaque candidates.
The Effect factory/draw/trail batch is now accepted. Factory names distinguish
the primary 0x200-entry ring, secondary [0x200, 0x280) pool, and fixed slots
at slotIndex + 0x280, including their velocity-taking variants. Bullet and
Background callers now name their draw-layer ownership; stage-origin
compensation, stage-position adjustment, resource load/release, and the
animation-end predicate use behavior-backed names. Directional-offset and
radial-trail callbacks now expose their exact initialization, synchronization,
timed/fading, anchored, and alternate-layer roles.
The complete EffectManager selection passes 52 / 52 exact. A required
single-job cold replay passes 1,106 / 1,106 exact, the normal VC7 image
links, and the complete i386 Linux build plus fixed-layout verifier passes.
Six stage-specific particle callbacks remain address-named because their exact
bodies prove motion and camera relations but not stable visual identities. The
whole-source router remains at 5 raw, 0 absolute, 113 anonymous, and 44 opaque
candidates; EffectManager.cpp itself has zero candidates.
The accepted ANM render batch names the central 2D, camera-facing, and 3D quad
projection/draw paths at 0x00463470..0x004640E0, the horizontal/vertical
texture-strip initializers, and the buffered sprite-quad queue. It also closes
AnmVm::IsStopped @ 0x004396F8 and the effect-table callback
UpdatePulsingRadialTrail @ 0x0040EB50. Target dataflow proves the projection,
UV-axis, submission, and pulsing-strip roles; unused ANM storage remains
unknown. All nine central functions and both VM helpers pass focused exact
comparison, the required cold replay passes 1,106 / 1,106 exact, the normal
VC7 image links, and the complete i386 Linux build plus fixed-layout verifier
passes. The old QueueSpriteQuad ledger ABI was also corrected from two byte
arguments to its target-proven VM/vertex pointer types.
The complete ECL EX callback table is now behavior-readable. Twenty-nine
remaining address names at 0x004233D0..0x00425390 now expose the narrow,
medium, and wide bullet-warp barriers, bouncing-enemy motion, rotating laser
hitboxes, child-orbit formation, Reisen bullet phases, screen effects, spell
state publishers, marked-bullet triggers, scaled bullet time, and conditional
item spawning. All 33 table functions were first replayed through target-
pinned typed packets and then pass the focused EclExIns.obj selection
33 / 33 exact. The shared Bullet flags name 0x100000 only as an ECL EX
trigger marker; instruction-header bytes and artistic identities remain
unknown. The required cold replay passes 1,106 / 1,106 exact, the normal
VC7 image links, and the complete i386 Linux build plus fixed-layout verifier
passes.
The accepted Spellcard control batch closes fifteen remaining address-named helpers and their shared flag/presentation ownership. Capture validity, timer-callback transition, Bomb damage, pending/captured reward state, effect tracking, bonus updates, alternate effect style, and bonus-presentation suppression now use named shifts and masks while preserving the target-visible shift tests and bitfield assignments. Twelve offset-derived VMs now identify the player/enemy portraits, name/frame presentation, layered enemy name, and bonus digit/frame roles; two draw-only VMs and flag bit 4 remain neutral.
Important helpers now include HidePlayerSpellPresentation @ 0x00416130,
HideEnemySpellPresentation @ 0x00416160, DeactivateWithoutCleanup @ 0x00416AF0, AddBonusProgress @ 0x00416B10, IsCaptureValid @ 0x0041FD90,
PrepareSpellcardForTimerCallback @ 0x0042BC50,
IsBombDamageEnabled @ 0x0042DFF0, and the Bomb/death capture invalidators at
0x0044CBA0/0x0044D150. The timer helper now has its proven Spellcard *
fastcall receiver rather than void *. Fifteen target-pinned packets and the
affected eleven-object selection pass 192 / 192 exact; the typed fastcall
helper separately passes 50 / 50. The required cold replay passes 1,106
/ 1,106 exact, the normal VC7 image links, and the complete i386 Linux build
plus fixed-layout verifier passes. The remaining evidence gaps are flag bit 4,
unknown_044, and the two draw-only VMs.
The accepted Ending/score-record batch makes the complete ending-script path
behavior-readable. ReadScriptParameter @ 0x00428890,
UpdateAndDrawFade @ 0x00428910, RunEndingScript @ 0x00428B80, and
LoadEndingScript @ 0x004295E0 now name their actual protocol, while the
Ending owner exposes its script buffer/cursor, ending ANM/VMs, line/page wait
timers, skip-lock countdowns, configured line delays, and next text-VM index.
canSkipChainedEnding @ +0x2A5C is set from the already-unlocked route in
AddedCallback @ 0x00429980 and copied into the active skip gate after opcode
@F loads another ending script.
The persisted score chapter header now exposes chapterSize and the second
size word that every authored producer writes equally as chapterSizeCopy;
CATK historyChecksum @ +0x0E names the byte checksum
seeded by the spell name and accumulated over both history arrays. CLRD
pendingEndingSkip @ +0x20 is deliberately conservative: target-wide .text
inspection finds only the non-clear Ending read followed by an immediate clear,
and no TH08 1.00d producer. Ending +0x2A98 and the seven remaining ScoreDat
fields stay neutral because current evidence does not prove their roles.
Focused replay passes Ending 11 / 11 exact and the six directly affected ScoreDat/SpellCard/ResultScreen/GameManager/Supervisor/TitleScreen objects 172 / 172 exact. The required final single-job cold replay passes 1,106 / 1,106 exact, the normal VC7 image links, and the complete i386 Linux build plus fixed-layout verifier passes. The four-file Ending/ScoreDat router falls from 14 anonymous identifiers plus one opaque range to seven anonymous identifiers plus the retained opaque range; the whole-source router is now 5 raw-member, 0 absolute-address, 106 anonymous-identifier, and 44 opaque-storage candidates. These remain routing counts, not a completion percentage.
The accepted ResultScreen/high-score batch closes the current-run score marker,
PSCR serialization gate, score-list operations, menu phase timer, shared name
keyboard selection, saved-name presence, spell-card exit, and displayed-time
refresh state. Target writes/reads prove that Th8k::runtimeMarker @ +0x09
marks the current run only in memory and is cleared immediately before HSCR
serialization. Pscr::shouldSerialize @ +0x175 gates copying the full PSCR
chapter, but its nonzero gameplay producer remains unknown. ResultScreen
offset assertions pin statePhase @ +0x10, statePhaseTimer @ +0x18,
keyboardSelection @ +0x2C, hasSavedLastName @ +0x50,
isExitingSpellcardResults @ +0x54, and
lastDisplayedTotalSeconds @ +0x19C.
ScoreDat::InsertScore @ 0x0045A500 and the ResultScreen wrapper now state the
descending-list insertion/rank behavior; both release helpers explicitly name
that they free score-list nodes. Eight post-rename typed packets replay exact.
Fresh focused replay passes ResultScreen 30 / 30 exact and ScoreDat 13 /
13 exact. The required final single-job cold replay passes 1,106 / 1,106
exact, the normal VC7 image links, and the complete i386 Linux build plus
fixed-layout verifier passes. Four-file anonymous candidates fall from 15 to
12 and the whole-source router is now 5 raw, 0 absolute, 103 anonymous, and 44
opaque. Hscr +0x166 and the reset-only/unused ResultScreen fields remain
unknown rather than receiving speculative names.
The residual raw-member batch closes all five remaining router-selected raw
offset expressions. ItemManager::OnUpdate @ 0x00440500 now reads the
asserted PlayerRawShtFile::itemMovementSpeed @ +0x34 from the primary or
secondary SHT according to Player::focusMode; target dataflow uses it as
the ordinary/scattered item movement and gravity step, distinct from
autocollection speed. SpawnSingleBullet @ 0x0042F5F0 addresses the remaining
Bullet +0xDBC and template +0xD40 bytes through asserted direct members.
Target-wide inspection still finds only one initialization and one copy with
no consumer, so both Bullet names remain explicitly unknown.
Post-change typed packets for both complete functions replay exact. Fresh focused replay passes ItemManager 19 / 19 exact and BulletManager 24 / 24 exact. The required final single-job cold replay passes 1,106 / 1,106 exact, the normal VC7 image links, and the complete i386 Linux build plus fixed-layout verifier passes. Whole-source raw-member and absolute-address candidates are now both 0. Replacing two opaque Bullet bytes with direct neutral members intentionally migrates repeated occurrences to the anonymous category, so the remaining router totals are 110 anonymous and 42 opaque; no semantic meaning is claimed for those bytes.
The address-name/ECL-type closure is now accepted. EclRawHeader::timelineCount
names the word consumed by the exact ECL load and accessor path. The loading
completion transition, Enemy attachment-chain teardown, wrapped-angle
interpolator, and random-biased ECL move helper now have behavior-backed names.
Ten Player Bomb callbacks expose their radial geometry and lifecycle roles,
with the portable callback bridge updated to the same symbols. Duplicate
EnemyUnkStruct2/3 shells are gone: the primary context, sixteen-entry call
stack, and target constructors now directly share EnemyEclContext and
EnemyEclInterpolationSlot.
Target-pinned packets and focused comparison replay every renamed function exact, including the 1,336-byte Enemy chain helper, 630-byte movement helper, and all ten Bomb callbacks. The required single-job cold replay passes 1,106 / 1,106 exact, the normal VC7 image links, and the complete i386 Linux build plus fixed-layout verifier passes. The final router totals are 0 raw-member, 0 absolute-address, 107 anonymous-identifier, and 41 opaque-storage candidates.
The follow-up production/probe/modern audit found and closed a gap outside the
router's categories: TitleScreen.cpp still contained the repository's only
two game-source STUB bodies while their complete accepted implementations
lived in TitleReplayMenuProbe.cpp. UnlockLastWordSpellCards @ 0x0046CBBB,
ConvertToFullWidthDigits @ 0x0046D763, and FormatSpellCardInfo @ 0x0046D7F9
now have shared source owners consumed by both production and the exact probe.
The production Title owner also restores the full-width number buffer, 18
Last Word comment records, and six difficulty strings. The target-pinned
verify-title-spell-card-data.py check passes 18 / 18 records and 6 / 6
strings exact.
The same batch names the ECL interpolation slot's callback index and four float parameters, the Title spell-info reveal countdown, and the GameManager practice-mode bit used by score initialization. Four modern-runtime literal target-data writes now use mapped Linux-local ABI aliases. Focused replay passes all 11 / 11 Title probe units, RunEcl 26,638 / 26,638, and score initialization 552 / 552. The required non-reuse cold replay passes 1,106 / 1,106 exact, the normal VC7 image links, and the complete i386 Linux build plus fixed-layout verifier passes.
This closes the current whole-source semantic-readability milestone more
strictly: game production source contains no STUB marker, and scans including
probes and modern sources contain 0 raw-member and 0 absolute-address
candidates. They retain 100 anonymous and 41 opaque candidates; production
alone retains 99 and 41. Those entries were reviewed as reset-only,
unconsumed, padding, or reserved storage and remain neutral. The six
stage-specific Effect callbacks also remain address-named because target
bodies do not prove stable visual identities. Future semantic work must start
from new producer/consumer or runtime evidence, not from heuristic category
size. Re-audit the evidence boundary with:
python3 scripts/analysis/report-semantic-debt.py --include-probes --include-modern
python3 scripts/analysis/verify-title-spell-card-data.py
A final production-symbol audit also corrects twelve ledger-only namespace
identities. Eleven PlayerShot free callbacks and InitializeScoreData were
already emitted in Player.obj/GameManager.obj as th08::..., but four
canonical CSVs still used unqualified names. Qualifying those rows removes all
twelve false "not present in detour input objects" warnings. Focused replay
remains Player 51 / 51 exact and GameManager 33 / 33 exact; no source,
address, layout, match-unit symbol, or accepted count changed. The remaining
20 detour warnings are limited to the generator's unsupported operator and
compiler-generated deleting-destructor spellings.
The scheduler follow-up names the complete calc/draw priority protocol instead
of leaving engine ordering as literals 0..21. Every normal owner
registration now identifies its execution/draw layer, including Replay's four
separate calc roles and the paired Background, Enemy, Player, and Ascii draw
layers. Normal gameplay ScreenEffects identify draw priority 21; the special
Supervisor-only priority 1 path stays literal pending stronger evidence.
ChainElem +0x18 is now the narrowly evidenced releaseTarget, with size and
offset assertions. TH08 target instructions show it initialized to self,
filled with live nodes in ReleaseSingleChain @ 0x0043CC60, and read back for
the release pass.
GensokyoClub's priority table was used only as a naming hypothesis; each name
was checked against the current exact registrant and immediate value. Focused
Global replay passes 44 / 44 exact. Because Global.hpp is shared, a new
single-job cold build of all 75 comparison objects passes 1,106 / 1,106
exact. The normal VC7 image links, and the complete i386 Linux container
build plus fixed-layout verifier passes. Router totals remain 0 / 0 / 100 /
41 with probes and modern source, or 0 / 0 / 99 / 41 for production.
Three live Supervisor flag roles are also closed. Bit 1 is
lockableBackbuffer: InitD3DRendering @ 0x004424C0 sets the corresponding
D3D presentation flag and the bit, while all consumers gate pause/retry menu
captures. Bit 6 is dummyMidiTimerEnabled: the startup-info branch controls
it and the startup thread uses it only to create/start that timer. Bit 8 is
scoreBackupPending: it guards one startup score.dat backup rotation and is
then cleared. The four-byte flags word and its Supervisor + 0x1A4 offset are
asserted; bit 5 remains unknown because no writer has been observed.
A stale focused PCH first rejected the new names, as expected for this shared header. The proof run discarded it: a fresh single-job build of all 75 objects passes 1,106 / 1,106 exact. Rebuilt AsciiManager/main/Supervisor diagnostic replay passes 134 / 134 exact, the normal VC7 image links, and the complete i386 Linux container build plus fixed-layout verifier passes.
The ResultScreen entry protocol is now explicit at every caller. Mode 0 opens
the standalone result browser, mode 1 handles the post-game result flow, and
mode 2 performs synchronous score-data persistence without installing chains.
RegisterChain @ 0x004582A0 keeps its original u32 ABI while named enum
values replace all raw mode arguments and comparisons. The function passes
395 / 395 exact; the required cold 75-object replay passes 1,106 / 1,106
exact, the normal VC7 image links, and the complete i386 Linux container
build plus fixed-layout verifier passes.
TitleScreen's two registration arguments are also explicit, with a narrower
claim: standard entry passes 0 and replay-completion return passes 1, but
RegisterChain @ 0x0047146D only homes and never reads the i32 argument.
TitleScreenRegistrationReason documents those caller reasons without
claiming different callee behavior. Focused replay passes Title 281 / 281
exact and Supervisor 1,831 / 1,831 exact. The required cold 75-object
replay passes 1,106 / 1,106 exact, the normal VC7 image links, and the
complete i386 Linux container build plus fixed-layout verifier passes.
The PBG archive loader now names its encoded entry count, encoded table offset,
and encoded table decompressed size, and preserves the per-entry fourth word as
neutral unconsumedMetadata because retail copies but never reads it. Header
and entry sizes/offsets are asserted. Focused PbgArchive.obj replay passes
15 / 15 exact. The required cold 75-object replay passes 1,106 / 1,106
exact, the normal VC7 image links, and the complete i386 Linux container build
plus fixed-layout verifier passes.
The Enemy/ECL attachment and laser-slot family is now typed end to end.
Enemy + 0x04/+0x08 expose the doubly linked attachment chain used by ECL
insertion, traversal, and unlinking; +0x3280 is a 32-entry Laser pointer
array and +0x3300 its selected slot. ECL Laser controls and primary/secondary
ANM VM users now use their asserted owners instead of raw byte views. Opcode
169's position view remains intentionally offset-shaped to preserve six
target VC7 x87 bytes, and child ECL blocks remain byte-oriented beyond their
three proven internal offsets. Focused replay passes 74 / 74 exact, the
parent-chain cleanup packet passes 1,336 / 1,336 bytes exact, and the
required cold 75-object replay passes 1,106 / 1,106 exact. The normal VC7
image links; the complete i386 Linux build and fixed-layout verifier pass.
The semantic router now reports 0 / 0 / 100 / 39 including probes and
modern source, or 0 / 0 / 99 / 39 for production; these are candidate
counts, not semantic completion totals.
Supervisor's BGM playback interface now exposes its actual score-data
protocol. Target PlayMusic/PlayAudio instructions treat the second argument
as an integer index into g_GameManager.plst.bgmUnlocked, not as a pointer;
the owner offsets GameManager::plst @ +0x3D804 and
Plst::bgmUnlocked @ +0x208 are asserted. Production/probe callers no longer
cast song numbers to char *, replay/demo gates use named flags, and the exact
manifest records the corrected PlayMusic symbol plus the target-equivalent
g_GameManager + 0x3DA0C relocations. Focused replay passes 189 / 189
exact, including PlayMusic 218 / 218 and PlayAudio 352 / 352; the
required cold 75-object replay passes 1,106 / 1,106 exact. The normal VC7
image links, and the complete i386 Linux build plus fixed-layout verifier pass.
The residual Item/ANM owner pass replaces byte views of Player shot/item
timers and option mode, GameManager character, the Item sprite z-write bit,
and AnmManager's texture factor with existing named fields. Assertions pin
Player::timeOrbGaugeChangeSuppressionTimer @ +0xE2ADC and
AnmManager::currentTextureFactor @ +0x24B8. A direct Float3 .y trial was
rejected because retail Item OnDraw calls Float3::operator float *() twice;
the conversion-shaped element accesses remain documented to preserve its
451-byte extent. Focused AnmManager/ItemManager replay passes 106 / 106
exact, and the required cold 75-object replay passes 1,106 / 1,106 exact.
The normal VC7 image links; the complete i386 Linux build and verifier pass.
The Enemy chain-helper ownership follow-up removes the final fake helper class
from this family. HasParentChain @ 0x0041F000, HasAttachedEnemy @ 0x0041FD20, and CountParentChain @ 0x0041FD40 are now real Enemy members:
their complete target bodies consume only the asserted attachment fields, and
all Enemy/ECL/GUI callers pass an Enemy *. Their target-neighborhood
placement in EclManager is retained as TU evidence rather than being confused
with class ownership. Both ECL operand resolvers also use typed Enemy chain
links and Player::position.x/y/z; unused raw field macros and a private vector
shell are gone. The temporary resolver/detach adapter described at this
checkpoint was removed by the later Enemy/ECL owner migration.
Focused replay across the seven affected objects passes 127 / 127 exact; the helper bodies pass 53 / 53, 28 / 28, and 70 / 70, and the 1,336-byte cleanup caller remains exact. The required non-reuse cold replay passes 1,106 / 1,106 exact, the normal VC7 image links, and the complete i386 Linux container build plus fixed-layout verifier passes. Mapping and accepted ledgers now record the real Enemy identities without changing any address, size, status, or count.
The Player focus/option/gauge pass closes three gameplay timer roles and the
shared option state machine. focusMode @ +0x03 names unfocused, focused, and
the initialization sentinel. gaugeShiftDelayTimer @ +0xE2AD0,
timeOrbGaugeChangeSuppressionTimer @ +0xE2ADC, and
shootingGaugeChangeRampTimer @ +0xE2AE8 now express the complete observed
shooting/idle gauge ramp and time-orb suppression protocols across Player,
Enemy attachment rewards, and Item collection. The option dwords at
+0x2C8/+0x2CC expose the common inactive/initializing/active/exiting
lifecycle and the homing-specific behavior state. Production, Bomb, and
probe switches use named enums; three unaccessed alignment ranges are marked
as padding, while unconsumed storage remains neutral.
Focused replay across the five affected production objects passes 200 / 200
exact, including UpdateMovementAndOptions 4,769 / 4,769, every option
callback, and Enemy cleanup 1,336 / 1,336; the PlayerOption probe compiles.
The required non-reuse cold replay passes 1,106 / 1,106 exact, the normal
VC7 image links, and the complete i386 Linux build plus fixed-layout verifier
passes. No ledger identity/count changed.
Whole-executable TU/layout work below remains deferred, not invalidated.
Active playable-port branch
port/modern-windows-linux is the independent playable reconstruction lane.
It does not replace the VC7 exact build or change authored/library ledgers.
Playable-port state on 2026-08-24:
- CMake compiles and links the complete production-authored source set with the
32-bit MinGW toolchain into
build/modern-windows/th08-modern.exe; - the output is an i386 Windows GUI PE and has no MinGW support-DLL dependency, but the user reports that the current native BAT/startup path does not launch a usable application. Prior smoke observations are insufficient to call the Windows product verified; Windows remains in progress;
--data-dir <directory>is the intended Windows interface for a Unicode directory containingth08.datandthbgm.dat, but it must be revalidated as part of the native startup fix;- the playable lane restores target-proven global ownership for the active
GameManagerstate and playfield bounds, uses relocatable function symbols for Player option/shot/bomb callback tables, and connects the completeEnemyManager::OnUpdateimplementation to the calculation chain; - the MinGW bring-up build temporarily uses the non-redistributable SDK
d3dx8d.dll. A distributable Windows build must replace the remaining D3DX calls; - CMake also builds
th08-modernas a native i386 ELF using repository-owned SDL2/OpenGL compatibility backends.scripts/build-modern-linux.shis the default host multilib entry; the i386 container is optional CI/build isolation rather than a runtime dependency; - native i386 ELF execution under WSLg has completed a full Lunatic story endurance pass, with player/enemy/bullet simulation, score awards, Boss bars, Bomb animations, focus marker, Stage 2 fog, Japanese dialogue, keyboard input, sound effects, streamed WAV BGM, and the ending transition active. The endurance run used an external GDB command that suppressed life decrement only; that test aid is ignored and never shipped. No fatal signal occurred. A real Linux desktop with hardware OpenGL remains additional release coverage;
th08-layout.ldnow aliases target-owned manager, callback-chain, and known aggregate-field views to their original addresses. This prevents duplicate spell/ECL callbacks across reloads, which had manifested as negative spell bonuses, saturated scores, missing scripted visuals, and later-stage exits;- the OpenGL backend uses a texture-backed FBO plus a stable dialogue-entry
snapshot, explicit eye-space linear fog, and independent RGB/alpha
texture-stage combiners. A Stage 4-to-5 transition can still tile the dynamic
player-name texture across the frame/HUD; it is documented as a deferred
Linux renderer issue rather than a release blocker. Fatal Linux signals
write a symbolizable
modern-crash.txt; MIDI remains a follow-up; scripts/setup-modern-linux.sh <data-directory>is the one-command Debian/Ubuntu setup/build/run entry, whilescripts/play-modern-linux.shperforms the normal incremental build and launch. Both accept only the original data directory and requireth08.datplusthbgm.dat;.github/workflows/portable-linux.ymlbuilds and verifies the i386 ELF and uploads a tar archive with a standalone data-directory launcher. No original executable or archives enter the artifact. The project-owned Touhou Lab icon is copied beside the ELF and loaded as the SDL window icon;- after the endurance pass, a fresh normal container build was linked without GDB or runtime patches, passed the ELF/layout/dependency/package checks, and was user-smoke-tested successfully under WSLg. The CI portable package was subsequently user-tested in a Kali Linux x86-64 GUI VM with native filesystem data, low memory, and no 3D acceleration. Normal life decrement remains active in this build;
- the Kali artifact's clean-data-directory exit was reproduced locally with
the exact CI package: status 139, fault address
0xffffffff, and a stack fromFindCloseintoSupervisor::StartupThread. The report was written underbackup/modern-crash.txtbecause the authored score-rotation code had already changed directories. The LinuxFindClosebackend now returnsFALSEfor null,INVALID_HANDLE_VALUE, or wrong-kind handles instead of deleting an invalid pointer. A clean two-DAT regression with an empty backup directory stayed alive, created the first score backup, and continued through the title assets.th08.exeis not opened; an existingth08.cfgis only useful for avoiding slow fullscreen calibration on unaccelerated VMs.
Reproduce the build with:
cmake -S . -B build/modern-windows -G Ninja \
-DCMAKE_TOOLCHAIN_FILE=cmake/mingw32-toolchain.cmake \
-DCMAKE_BUILD_TYPE=Debug
cmake --build build/modern-windows --parallel 1
Run the executable without copying or linking the original archives:
th08-modern.exe --data-dir "D:\path\to\the\original\TH08\directory"
Build and run the native Linux target with:
scripts/setup-modern-linux.sh "/path/to/the/original/TH08 directory"
After first-time dependency setup, use
scripts/play-modern-linux.sh "/path/to/the/original/TH08 directory".
The optional isolated build is scripts/build-modern-linux-container.sh and
outputs build/modern-linux-container/th08-modern.
See docs/LINUX_PORTING.md for the platform boundary, environment overrides,
validation matrix, and reusable lessons from the Linux reconstruction.
The selected directory becomes the runtime working directory, matching the
original game's relative-file behavior for configuration, score, replay, and
BGM files. Keep the executable, SDK DLL, generated test data, and runtime
screenshots under build/ or outside the repository; never commit the original
archives.
Current status
As cold-built and replayed on 2026-08-26 against the original Japanese TH08 1.00d target:
- authored source: 1,107 / 1,107 functions, 459,757 / 459,757 bytes;
- strict authored exact: 1,106 / 1,107 functions, 459,396 / 459,757 bytes;
- library inventory: 1,119 classified functions; all 1,119 / 1,119 now have mapping sizes totaling 217,165 bytes; the independent library exact ledger currently accepts 260 functions / 52,955 body bytes;
config/claims.csvis header-only;- a cold normal VC7 build links
build/th08.exesuccessfully; - a cold objdiff build followed by full replay passes 1,106 / 1,106 accepted units;
- the completed production-layout passes cover the Ascii target clusters,
Player bomb/main regions, misplaced
main.cpphelpers, Anm render helpers, Global math helpers, SoundPlayer helpers/tail, and GameManager helpers/main region.Player.obj,GameManager.obj,Global.obj, andSoundPlayer.objnow each have zero target-order inversions. Canonical owners record the section-defined production copies rather than stale probe/objdiff donors; - the whole-image comparator is in place. After the first evidence-backed link contract repairs, the rebuild has the target's DLL descriptor order, no debug directory, and the same two resource paths and resource section extent. The placeholder icon payload intentionally remains different;
- repository-owned target-independent CI passes.
Regenerate or verify these values with:
python3 scripts/analysis/report-reconstruction-status.py --summary
python3 scripts/validate-tracking.py --require-target
python3 scripts/analysis/verify-exact-units.py --all --json \
> build/accepted-unit-replay.json
python3 scripts/progress.py --check
python3 scripts/ci.py
verify-exact-units.py --all performs its own single-job cold build. Do not add
--reuse-build when attesting aggregate totals.
Authored exact backlog
One authored function remains unaccepted. Do not count it as exact or restart broad brute-force matrices without a new target-backed hypothesis.
ReplayManager::PlaybackExtendedInputAndFps @ 0x004526C0, 361 bytes: the natural source object is one byte longer and the residual is a register-allocation phase difference. Its adjacent callback family is exact. The ordinary pointer/cast/increment/local-order variants are exhausted and recorded inBUILD_MATCHING.md; resume only from a new allocator/TU hypothesis.TitleScreen::RegisterChain @ 0x0047146Dis no longer backlog. The GensokyoClubZUN_NEW(TitleScreen, "TitleInf")hypothesis restores the target0x40frame and replays the full 281-byte body plus all 20 relocations exactly.
The source for the remaining function is present and behaviorally
reconstructed. Exact coverage
will increase only when the canonical comparator returns exact for a natural,
evidence-backed C++ form.
The other fourteen cold regressions have been repaired and accepted. Four
TitleScreen functions required production-source restoration. Seven other
functions were already byte-exact but had stale relocation identities in
match-units.toml. Supervisor::IsFogDisabled required restoring its original
header-inline definition: VC7 then emits the target 0x1A COMDAT in
main.obj, while the calling AsciiManager function remains exact. The
AnmVm::SetZRotation and GameManager::CollectExtend then required bounded
production-source owner/value-flow fixes. The 2026-08-20 single-job cold replay
verifies all 1,105 / 1,105 accepted units.
See BUILD_MATCHING.md before changing a shared inline/out-of-line contract.
Library foundation (paused except for whole-link blockers)
The seven previously missing library extents are repaired. The canonical selector now reports no missing-size rows:
python3 scripts/analysis/report-reconstruction-status.py \
--category library --state missing-size --sort address
The six D3DX rows were reconciled against the target CPU dispatch tables and
the SHA-pinned VC7 PlatformSDK prerelease D3DX8.LIB
(0d4a2b642485dcaa7671926a9a1a545c656d5eb73f160fe971b3deebf0b516b5).
Their archive identities are SSE/SSE2 Vec3, Plane, Vec4, and Quaternion
normalize members; target body extents stop before archive alignment padding.
operator delete @ 0x004A43CF is the five-byte VC7 /MT LIBCMT.LIB
delete.obj tail jump to _free. These boundary/provenance facts now feed the separate accepted library ledger; they are not authored claims.
Inventory integrity is now reconciled. Four imported extents were stale and
were shortened at target-proven return/next-function boundaries (0x0049E3CD,
0x004A51FD, _strchr @ 0x004A5BE0, and 0x004A6890). The remaining nine
overlaps are VC7 CRT nested SEH cleanup funclets and are explicitly represented
in config/mapping-overlaps.csv; validate-tracking.py rejects stale exception
rows and reports any new unclassified overlap.
Separate library acceptance infrastructure is now in place:
library-provenance.tomlpins archive identity and SHA-256;library-match-units.tomlrecords target body/compare extents, archive member, COFF symbol, and every relocation target base;compare-library.pyverifies archive hash/member identity and replays DIR32/REL32 relocations before comparing the complete target range;library-matches.csvis the independent accepted ledger;validate-library.pyis the public CI gate, with--require-archivesfor local hash attestation;library-progress.pygeneratesLIBRARY_PROGRESS.md.
Library acceptance now has 260 / 260 configured units and 52,955 body bytes
after canonical zero-difference replay. The accepted set contains six SSE/SSE2
D3DX normalize helpers plus seven exact x3d_quat.obj quaternion helpers (including the 0xA59-byte x3d_D3DXQuaternionSquadSetup with 66 replayed relocations) and six exact x3d_matx.obj matrix helpers (including 0x1198/0x1154-byte transformation implementations), VC7 /MT operator delete, seven CRT string/memory
helpers (including strict shared-section strchr/strcpy/strcat), six
isolated compiler-runtime arithmetic helpers (_ftol2, _chkstk, _aulldiv,
_allmul, _ftol, _aullshr), and the LIBCMT malloc.obj allocation wrappers
_nh_malloc @ 0x004A423D / malloc @ 0x004A4269 with explicit DIR32/REL32 replay, plus the complete currently mapped heapinit.obj/sbheap.obj core accepted so far: _heap_init, __sbh_heap_init, __sbh_find_block, __sbh_free_block, __sbh_alloc_new_region, __sbh_alloc_new_group, the 735-byte __sbh_resize_block, and the 764-byte __sbh_alloc_block. The next accepted family also covers eight trnsctrl.obj C++ EH helpers, including _UnwindNestedFrames, __CxxFrameHandler, _GetRangeOfTrysToCheck, _CreateFrameInfo, IsExceptionObjectToBeDestroyed, _FindAndUnlinkFrame, _CallCatchBlock2, and _CallSETranslator. Shared-section exsup.obj coverage now also accepts _global_unwind2, _local_unwind2, _abnormal_termination, _NLG_Notify1, _NLG_Notify, plus the isolated __EH_prolog. The EH dependency chain now also accepts _getptd, __InternalCxxFrameHandler, _CallSettingFrame@12, and _inconsistency; the latter exposed and fixed a stale 0x26 mapping extent, with hooks.obj and target control flow proving the full 0x2D body. __FrameUnwindToState is accepted with a 0xAD main-body extent and a 0xCE comparison extent so its compiler-owned cleanup alternate-entry/funclet bytes are replayed without double-counting the separately mapped 0x004AA267 funclet. Auxless whole-section COMDAT support is now explicit/fail-closed, and FindHandler @ 0x004AA72E is accepted as a 0x1E2-byte static code COMDAT with a unique offset-zero function owner and 13 replayed REL32 relocations. tidtable.obj thread-runtime coverage now also accepts _mtterm @ 0x004AA049 and _mtinit @ 0x004AA0D8 with their TLS/lock/calloc relocation graph replayed explicitly. mlock.obj coverage now accepts _mtinitlocks, _mtdeletelocks, _unlock, _mtinitlocknum, and _lock, including the dynamic critical-section table and lazy lock initialization paths. Its immediate dependencies now also accept _errno, _amsg_exit, and __crtInitCritSecAndSpinCount; the latter two required target-proven extent repairs from 0x22 to 0x25 and 0x67 to 0x8B respectively. The message/critical-section leaf layer now additionally accepts __crtInitCritSecNoSpinCount@8, _FF_MSGBANNER, and _NMSG_WRITE; the 0x177-byte message writer replays 30 relocations including CRT string helpers, Win32 I/O, and the security-cookie check. Its immediate code dependencies now also accept strncpy, __crtMessageBoxA, and the 14-byte seccook.obj security-check-cookie helper at 0x004AB43C; that last row remains conservatively named FUN_004ab43c until a repository-wide fastcall-decoration naming rule is fixed. _report_failure is now accepted with its full 0x31-byte auxless COMDAT extent, and _calloc with its full 0xBB-byte VC7 function-definition extent; calloc deliberately retains the legal nested cleanup-funclet overlap rather than truncating the parent epilogue. trnsctrl.obj also exposed a missing CatchGuardHandler @ 0x004A44A1 inventory row and an undersized TranslatorGuardHandler @ 0x004A44C5; both auxless COMDATs are now accepted at 0x24/0x71 bytes. frame.obj coverage now also accepts TypeMatch, the repaired 0x45-byte __DestructExceptionObject, CatchIt, and FindHandlerForForeignException. _JumpToContinuation now replays a 0x30-byte COFF section while counting its 0x2B main body, and the linked 0x004A4412 helper is accepted through the existing CallMemberFunction1 row; archive Function0/1/2 symbols are identical 7-byte COMDAT aliases of that linked copy. CallCatchBlock and BuildCatchObject are now accepted with 0x97/0x170 body extents while replaying their complete 0x1A7/0x17C auxless COMDAT sections, which include separately mapped EH funclets/tails. Their direct support layer now also accepts AdjustPointer, ValidateRead, corrected ValidateWrite, ValidateExecute, and the repaired full 0x33D-byte memmove; FUN_004AA427 is now accepted through the explicit tail-local-funclet schema, pinned to $L19653 and its CallCatchBlock parent section rather than forced through the normal function schema. The same schema now accepts FUN_004AA267 via $L19568 at the tail of __FrameUnwindToState; terminate @ 0x004AA9B2 is also repaired from 0x2E to the full 0x35-byte hooks.obj function and accepted with two __getptd calls plus the final _abort @ 0x004B05BD tail jump. The locale/thread pair now accepts __updatetlocinfo_lk @ 0x004A9F4C, __updatetlocinfo @ 0x004AA00E with a 0x32 body/0x3B comparison extent, and its 9-byte FUN_004AA040 tail-local cleanup; _abort @ 0x004B05BD is also accepted as a 0x17 body with the final int3 included only in its 0x18-byte comparison extent. This chain now continues through __freetlocinfo @ 0x004A9E82; _abort dependencies _exit @ 0x004A6B6A and _raise @ 0x004B17F2 are accepted as well, with _raise repaired from 0x164 to its full 0x179-byte parent extent while retaining the legal nested 0x004B192D cleanup funclet. The locale teardown chain now also accepts __free_lc_time @ 0x004AFA38, __free_lconv_num @ 0x004AFBC8, and __free_lconv_mon @ 0x004AFC27, closing the direct heap-release dependencies of __freetlocinfo. Signal/ctype coverage accepts static siglookup @ 0x004B17C4 and the isdigit/isxdigit/isspace leaf family. Do not accept signal @ 0x004B196B: winsig.obj::_signal has a 0x1A9-byte function-definition section, but replay at that target start has hundreds of non-relocation byte differences; the current row remains an unresolved inventory/provenance item rather than an exact claim. Time/locale formatting coverage accepts _Strftime_mt, _Strftime, strftime, localtime, and time; the archive-proven VC7 symbol corrects the imported _time32 mapping name to _time. Formatting/small-CRT coverage also accepts vsprintf, sprintf, sscanf, atol, rand, and purecall from their pinned LIBCMT members; the stdio wrappers replay their __output/__flsbuf or strlen/__input relocations explicitly rather than masking them. Time-runtime dependencies now also accept __tzset, _isindst, and gmtime; termination coverage accepts _onexit_lk, _onexit, its separately mapped 6-byte tail cleanup funclet, and atexit. The internal timezone lane now also accepts _tzset_lk and _isindst_lk as auxless static tzset.obj COMDATs, replaying 58 and 30 relocations respectively; _isindst_lk exposes _cvtdate @ 0x004AADC8 as the next direct dependency. That dependency is now accepted as the full 0x1B8-byte static _cvtdate COMDAT; _tzset_lk also closes through exact _getenv_lk @ 0x004B05D5. The environment conversion chain now continues through exact _mbsnbicoll @ 0x004B1FC8 and archive-proven __wtomb_environ @ 0x004B2016, each with four explicit relocations. String/heap support now accepts the repaired 0x88-byte strcmp plus _msize as a 0x6A main body with a 0x76 comparison extent and a separately accepted 9-byte cleanup tail. Startup/termination coverage now also accepts __crtExitProcess, _lockexit, _unlockexit, _cinit, doexit, public exit, _cexit, and the previously missing _c_exit @ 0x004A6B8A; __crtExitProcess counts 0x2F body bytes while replaying the final int3 in its 0x30-byte COFF extent. initsect.obj coverage now repairs _RTC_Initialize @ 0x004ACBFE from 0x3D to 0x44 and adds the previously missing _RTC_Terminate @ 0x004ACC42, both exact 0x44-byte SEH iterator functions. File-I/O wrapper coverage now repairs _lseek, _write, and _read from imported 0xA0 extents to their full 0xAB VC7 function-definition extents. Each parent still contains a separately mapped nested cleanup funclet, but control flow resumes in the parent afterward; the overlap exceptions remain valid and the child does not define the parent boundary. Stdio support also accepts write_char, write_multi_char, write_string, input::__inc, and archive-proven _ungetc_lk; the latter replaces the imported FID_conflict:_ungetc label after exact ungetc.obj replay. File-handle support now also accepts _lseek_lk, _write_lk, _read_lk, _lock_fhandle, and _unlock_fhandle, closing the direct lock/worker dependencies of the repaired _lseek/_write/_read wrappers. Error/handle support now also accepts _doserrno, _dosmaperr, and _get_osfhandle, replacing the anonymous doserrno row with its archive-proven identity. Stdio/MBC conversion coverage now also accepts _getbuf, _filbuf, wctomb, mbtowc, __wctomb_mt, and __mbtowc_mt, closing the direct buffer and multibyte conversion dependencies of the accepted output/input code. File/stdio lock internals now also accept _free_osfhnd, _lseeki64_lk, _flush, _fflush_lk, _fclose_lk, and _close_lk, providing archive-proven worker identities for the remaining public wrapper lane. _isatty is also accepted from isatty.obj, closing the simple terminal-test branch used by formatted and file output paths. Public-wrapper coverage now adds the previously missing fclose @ 0x004B2609 and repairs _commit @ 0x004B265A / _close @ 0x004B2E0D to their full 0xBC / 0x9B VC7 extents while preserving their nested cleanup overlaps. fflush.obj inventory repair now also adds static flsall @ 0x004B1C69 (0xD5) and _flushall @ 0x004B1D3E (0x9), which exactly fill the unmapped gap after _fflush_lk and before ___ansicp. X87 common.obj coverage now accepts nine bounded functions in one shared 0x18C code section, including the previously missing _check_overflow_exit @ 0x004A7F35; each match pins its section offset instead of pretending the archive member contains nine independent sections. Continue pinning provenance/match units for
remaining VC7 CRT/runtime, standard-library, D3DX, and compiler-runtime inventory
only when a bounded whole-image difference identifies the member as a dependency.
Keep library progress separate from authored totals; do not resume broad archive
scanning merely to increase its percentage.
Keep authored implemented.csv, matches.csv, and authored percentages
unchanged. There is intentionally no whole-library scanner until archive
identity, relocation policy, COMDAT/padding rules, and failure modes are encoded.
Current milestone: whole-executable reconstruction
The normal executable links, so whole-image differences now select the work. Run the canonical cold baseline with:
python3 scripts/build.py --fresh
python3 scripts/compare-whole-image.py --json \
> build/whole-image-report.json
The 2026-08-20 baseline after restoring the target-proven ICF link contract is:
- file size: target 840,704, rebuild 817,152 bytes, so the rebuild is
23,552 bytes (
0x5C00) smaller; - entry point: target
0x004A619E, rebuild0x00498EAB; .textvirtual/raw: target0xB1B78 / 0xB1C00, rebuild0xA8C4F / 0xA8E00; the raw-code deficit is0x8E00;.rdatavirtual/raw: target0x11284 / 0x11400, rebuild0x14FDC / 0x15000;.data1raw bytes and extent are exact;.dataand the rest of the image are not exact;.rsrcvirtual/raw extents are both0xD60 / 0xE00, and its two paths and group-icon leaf agree. The 3,240-byte icon leaf retains repository-owned placeholder artwork, so its payload is intentionally different;- both images have no debug-directory entries;
- DLL descriptor order agrees exactly. The target imports 155 symbols and the
rebuild imports 172: there are no target-only imports and 17 rebuild-only
KERNEL32symbols. Nine are referenced by D3DXcd3dxfile.obj/cd3dxresource.obj; the remaining set is referenced by six linked VC7 CRT members recorded inKNOWLEDGE_BASE.md. The configured prereleased3dx8tex.objis now directly attested by exact auxless-COMDAT units forD3DXLoadSurfaceFromFileInMemory @ 0x0047AAF3(0xB8, four REL32 relocations) andD3DXCreateTextureFromFileInMemoryEx @ 0x0047B72E(0x40, one REL32). Both have zero non-relocation differences. An isolatedmssdk/lib/d3dx8.liblink kept all 17 extra imports, reduced located anchors to 930 with 86 missing, and shrank.textraw size to0x96C00, so do not replace the pinned prerelease archive. The live D3DX import issue is member retention/splitting under/OPT:NOREF, not a production source API mismatch; - 952 accepted address anchors are found in the linker map and 66 accepted units
lack a unique map candidate. Twelve located anchors are ICF-folded aliases;
they remain exact evidence but are excluded from TU-layout ranking because a
shared linked RVA does not identify the contributing object's placement.
Thirty-five production objects have accepted anchors, 34 retain at least one
non-folded layout anchor, and the only folded-only production object is
utils.obj. Anchor-derived current-object order still differs from target. The largest genuine intra-object drift is nowBulletManager.objat 104,240 bytes, followed byEnemyManager.objat 89,792 bytes;AnmManager.objfalls from the former 359,792-byte apparent span to only 160 bytes once its folded vertex-constructor alias is excluded.
The first bounded target translation-unit/object-partition recovery is now
complete. The old AsciiManager.obj placed seven exact functions totaling
0x220 bytes between InitializeVms and RegisterChain, although the target
places them in four distant clusters: 0x00406FD0, 0x00422BB0,
0x0042F2D0, and 0x004398FF. Their definitions now live in
AsciiManagerGauge.cpp, AsciiManagerBossMarker.cpp,
AsciiManagerScale.cpp, and AsciiManagerGuiMode.cpp, compiled under the same
/Od PCH profile. Canonical focused replay passed all 45 / 45 accepted
units across the donor and recipients; the subsequent full cold replay passed
1,105 / 1,105.
This split reduced AsciiManager.obj from 189 inversions / 3 target-order
runs / 225,802 bytes of drift span to 0 inversions / 1 run / 2,800 bytes of
drift span. Its first 21 anchors, through PauseMenu::OnUpdate @ 0x004037B0,
now have zero linked-address drift. The extracted clusters each have uniform
internal drift; their final placement depends on later splits in neighboring
GameManager, EnemyManager, and Gui ownership and is not yet exact.
The executable layout proves that these bodies cannot remain in the early
AsciiManager object; it does not yet prove that all four clusters correspond
one-for-one to standalone original source files.
The second bounded investigation separated a genuine Player TU boundary from
ordinary lexical disorder. The callback family at
0x0040BC20..0x004142C0 was merged into the same source file as the main
Player region at 0x00449CA0..0x00451D50, despite the large target gap and
many intervening subsystems. Its real definitions now live in
PlayerBomb.cpp, under the same /Od PCH profile, and all 56 accepted
early-range units have canonical ownership in PlayerBomb.obj. The main
target neighborhood is otherwise substantially continuous, so its remaining
seven runs were definition-order drift rather than evidence for six more
standalone TUs. Player.cpp is now ordered by its mapped target functions;
two forward declarations preserve natural C++ visibility without adding
definitions or shims.
Focused donor/recipient replay passed all 116 / 116 accepted units. The
subsequent full single-job cold replay passed 1,105 / 1,105. The original
merged Player.obj had 286 inversions / 8 runs / 275,088 bytes of drift
span. The repaired Player.obj has 0 / 1 / 7,760, and
PlayerBomb.obj has 0 / 1 / 544. The name PlayerBomb.cpp is a
repository description of the proven callback cluster, not a claim about the
original filename.
Continue target translation-unit recovery one production object at a time. Generate and rank the detailed anchors rather than manually scanning the whole report:
python3 scripts/compare-whole-image.py --json --include-anchor-details \
> build/whole-image-anchors.json
python3 scripts/analysis/report-tu-partition-candidates.py \
build/whole-image-anchors.json
The third bounded pass repaired a different ownership error rather than
inventing another source-file split. Fourteen small GameManager/ZunTimer
helpers had explicit definitions appended to main.cpp, even though target
neighborhoods and production undefined references place twelve with the early
AsciiManager consumer cluster, ZunTimer::operator--(int) @ 0x00418110
beside SpellCard, and GameManager::SetClockTime @ 0x00453C60 beside replay
loading. Restoring their natural header-inline bodies makes VC7 emit exact
COMDATs from AsciiManager.obj, SpellCard.obj, and ReplayManager.obj under
the actual consumer profiles. Focused donor/recipient replay passed 133 /
133 accepted units, and the subsequent full single-job cold replay passed
1,105 / 1,105.
This reduced main.obj from 277 inversions / 5 runs / 316,960 bytes of drift
span to 16 / 2 / 146,336. All 25 anchors in the retained main region are
continuous; the remaining reset is solely the exact but currently uncalled
ZunTimer::operator+= @ 0x0041FDF0. It remains explicitly defined in
main.cpp until a target-backed production consumer or owner is recovered;
do not assign it to an unrelated ECL/SpellCard TU just to improve the metric.
The current Ascii helper cluster retains a bounded lexical/emission-order issue
(59 anchors / 154 inversions / 7 runs / 9,600 span) but no exact-code
regression. The later natural emissions of
Float3::Float3(float,float,float) @ 0x00404720 and eight Sound fade/pause
COMDATs all have target-neighbor evidence; the pairwise inversion count grows
with the number of anchors and is not directly comparable to the earlier
45-anchor measurement.
The fourth bounded pass addressed AnmManager.obj. Seven exact helper bodies
were mixed into the donor despite target-neighbor production consumers:
AnmVmBase::Initialize and SetTextureCaptureParams route to the early
Ascii cluster, SetZRotation to PlayerBomb, IsStopped to Gui, and
SetCameraMode/Draw2DAndFlush/GetAnm to Background. Six can remain
natural consumer-emitted header bodies. Initialize must instead be an
explicit /Od definition in AsciiManager.cpp: exposing its body globally
changed five accepted /Os /Ob1 Title functions during cold replay. The
expanded seven-object focused replay passed 294 / 294 accepted units and
the final full cold replay passed 1,105 / 1,105.
The retained 0x00462270..0x00464EC0 Anm render cluster was continuous, so its
definitions were reordered by target function address instead of split into
invented TUs. SpriteHasTexture @ 0x004622C0 deliberately remains
header-inline: changing it to out-of-line made the Anm metric perfect but
broke exact TitleScreen::OnDraw @ 0x0047087F, whose target contains the
inlined body and no call relocation. The accepted result reduces
AnmManager.obj from 195 inversions / 10 runs / 400,208 bytes of drift
span to 20 / 2 / 359,840. All 20 residual inversions are that required
deferred COMDAT placement; the main/render definitions themselves are in
target order.
The fifth bounded pass repaired Global.obj. Six exact shared math bodies were
appended between a substantially continuous Global main region and its final
global helpers. Target neighborhoods plus production undefined references
place Float3::operator+, operator-, operator*, and operator/= in
Background.cpp, and VectorAngle plus Float3::operator/ in
PlayerBomb.cpp. Their explicit definitions now sit at the corresponding
target-local lexical positions without making the bodies globally visible.
Chain::RunDrawChain and Chain::ReleaseSingleChain remain in the continuous
Global TU but are lexically ordered as in the target.
Moving the six callers also stopped Global.obj from implicitly emitting
Float3::Float3(float,float,float) @ 0x00404720. The target constructor is
adjacent to PauseMenu::OnDraw, and AsciiManager.obj naturally emits the
same exact COMDAT, so its canonical match-unit owner now records that
section-defined production copy. Focused donor/recipient replay passed 191 /
191, and the subsequent full single-job cold replay passed 1,105 /
1,105. Global.obj improved from 194 inversions / 4 runs / 220,816 bytes
of drift span to 0 / 1 / 96.
The sixth bounded pass repaired SoundPlayer.obj. Eight exact fade/pause
helpers appended to the Sound donor had target addresses
0x00406AC0..0x00406C50, directly between the already recovered Anm and
GameManager Ascii-emitted clusters. AsciiManager.obj was the only production
object with undefined references to the five outer SoundPlayer wrappers. Once
the wrapper bodies in SoundPlayer.hpp and their three nested stream-helper
bodies in zwave.hpp were restored inline, VC7 emitted the exact target order:
each SoundPlayer wrapper immediately followed by its CStreamingSound helper,
then Pause and UnPause. Focused Ascii/Sound replay passed 86 / 86 and
the required full cold replay passed 1,105 / 1,105.
The remaining Sound tail at 0x0045E2D0..0x0045E3F0 was continuous but its
three deferred header accessors appeared after the explicit constructor/free/
fade functions. All accessor callers are in the same /Od Sound TU and retain
target-proven call boundaries. Making CWaveFile::GetFormat,
CSoundManager::GetDirectSound, and CSound::GetWaveFile explicit definitions
immediately before the constructor restored the complete target lexical order;
focused replay passed 25 / 25, followed by another 1,105 / 1,105 cold
aggregate replay. SoundPlayer.obj improved from 181 inversions / 4 runs /
358,864 bytes of drift span to 0 / 1 / 0.
The seventh bounded pass repaired GameManager.obj, which initially had 37
anchors / 174 inversions / 9 runs / 291,648 bytes of drift span. It required
several kinds of evidence-backed repair rather than one artificial source-file
split:
SetLives,UpdateAntiTamper,SetBombCount, andSetPowernow emit from the early Ascii consumer cluster. The target places Sound helper COMDATs before these GameManager COMDATs, soth_pch.hincludesSoundPlayer.hppbeforeGameManager.hpp; PCH include order is part of the VC7 emission contract.IsSoloHuman,IsSoloYoukai, andGetLivesnow emit from their realEnemyManager.objconsumer at the target0x0042F230..0x0042F2B0neighborhood.- the retained GameManager main definitions were reordered by target address.
AddLives @ 0x0043C641is an explicit definition between the constructor andInitArcadeRegionParams, matching the continuous target sequence without changing its exact body. - the Player-local sequence at
0x0044E140..0x0044E348is now explicit inPlayer.cpp:SetYoukaiGauge,RandomizeAntiTamper,AddToDeaths, andAddToBombsUsed. This both restores the target order and replaces the stale objdiff-onlyRandomizeAntiTamperowner with the exact production copy. ScaleFloatBasedOnRank @ 0x00422B80is the first definition in the existing target-contiguousAsciiManagerBossMarker.cppblock, immediately before the two Ascii functions at0x00422BB0..0x00422C13.
Focused replay passed 65 / 65 Ascii units for the setter move, 44 / 44
Enemy units for the solo move, 62 / 62 Player units for the Player-local
block, 3 / 3 Ascii boss-marker units for the final cluster, and 40 / 40
units in the final GameManager donor. Shared-header checkpoints and the final
cold aggregate replay pass 1,105 / 1,105. The final linked metrics are
GameManager.obj 30 / 0 / 1 / 3,970, Player.obj 36 / 0 / 1 / 7,264,
and AsciiManagerBossMarker.obj 3 / 0 / 1 / 0.
The eighth bounded pass repaired Gui.obj. The target neighborhood from
Gui::OnUpdate @ 0x004338CA through Gui::FUN_00439810 @ 0x00439810 is
substantially continuous, so the twelve linked-order runs were lexical
definition disorder rather than evidence for twelve original source files. The
40 explicit definitions in Gui.cpp now follow target address order; only the
file-local helpers needed by earlier callers received forward declarations.
One residual remained after that reorder: header-inline
AnmVm::IsStopped @ 0x004396F8 emitted at its first Gui use, placing it far
earlier than the target. Production callers are confined to Gui, while the two
PlayerOption probe callers retain target-proven REL32 calls. Making the header
declaration-only and placing the unchanged body explicitly between
Gui::FUN_004396b8 @ 0x004396B8 and Gui::LoadMsg @ 0x00439710 preserves all
call boundaries and the established Gui production owner. Focused replay
passed 41 / 41 Gui units and 15 / 15 PlayerOption probe units. The final
cold aggregate replay passed 1,105 / 1,105. Gui.obj improved from 28
anchors / 167 inversions / 12 runs / 23,133 drift span to 28 / 0 / 1 /
101.
The ninth bounded pass repaired the safe lexical part of SpellCard.obj. Its
explicit function definitions now follow target address order across the current
production TU. Focused replay passed 34 / 34 accepted units, and the
subsequent cold aggregate replay passed 1,105 / 1,105. The linked object
improved from 31 anchors / 122 inversions / 7 runs / 167,120 span to 31 /
18 / 2 / 151,552.
The 18 residual inversions are intentionally retained. The continuous Spellcard
main range through CutChain @ 0x004180F0 is followed in the target by the
consumer-emitted ZunTimer::operator--(int) and GameManager spell-number
helpers at 0x00418110..0x00418180. Current source also contains explicit
Spellcard helpers whose target homes are far away (0x0041F040..0x0041FDD0,
0x0042DFF0, and later Player-region helpers). They have no bounded production
caller/owner evidence that justifies moving them into another TU, while the
existing consumer COMDATs are already exact. Do not change header visibility or
invent owners solely to erase this metric residual.
The tenth bounded pass repaired BulletManager.obj. The production source had
real definitions in a highly non-target lexical order spanning the early
0x00415C60 clear wrapper, the 0x0042A410..0x00432F20 bullet body, and the
0x00433820..0x004338C0 lifecycle/resource tail. Sorting the existing
definitions by mapped target address required only forward declarations for
CopyBulletAnmVmCore and SelectBulletSprite; no behavior or owner changed.
Focused replay passed 38 / 38, and the subsequent cold aggregate replay
passed 1,105 / 1,105. Linked metrics improved from 25 anchors / 85
inversions / 4 runs / 121,968 span to 25 / 0 / 1 / 104,240. The large
remaining span is the real target distance between 0x00415C60 and the main
0x0042F360+ block, not a target-order residual.
The eleventh bounded pass repaired Background.obj. Two tiny Background
methods were not Background-TU bodies at all in the shipped layout: the only
production callers are in Spellcard.cpp, and target addresses place
background_fun_00415ce0 @ 0x00415CE0 between StartSpell and the cut-in
sequence, and background_fun_00416ad0 @ 0x00416AD0 between EndSpell and
DeactivateWithoutCleanup. Their unchanged definitions and canonical match-unit
owners now live in SpellCard.obj. Background donor/SpellCard recipient
focused replay passed 29 / 29 and 36 / 36 respectively.
The retained Background definitions were then repaired with targeted lexical
moves rather than a whole-file sort, preserving the interspersed file-scope
arrays/globals: constructor/OnUpdate/interpolation helpers, stage-data load/
update/render, and the late resource-reload predicate now follow target order.
Background.obj improved from 26 anchors / 61 inversions / 6 runs / 68,640
span to 24 / 0 / 1 / 528. The subsequent cold aggregate replay passed
1,105 / 1,105.
The twelfth bounded pass repaired Midi.obj. Its only target-order reset was
caused by two header-defined bodies whose correct owner was already Midi but
whose deferred emission positions were wrong. MidiTimer::OnTimerElapsed @ 0x0043EF40 is the empty virtual base callback and belongs before the main
0x00443C60+ Midi implementation; MidiOutput::Ntohl @ 0x004444E0 belongs
between ParseFile and LoadFile. Both caller sets are confined to Midi and
the target preserves real call/virtual boundaries, so the header declarations
are now bodyless and the unchanged definitions sit explicitly at their target
lexical positions in Midi.cpp. Focused replay passed 33 / 33, and the
subsequent shared-header cold aggregate replay passed 1,105 / 1,105.
Midi.obj improved from 33 anchors / 43 inversions / 2 runs / 25,888 span
to 33 / 0 / 1 / 19,728; the remaining span is the real target gap from the
base virtual callback to the main Midi TU.
The thirteenth bounded pass repaired EnemyManager.obj. Its five runs mixed
ordinary explicit-definition disorder with one target-contiguous cross-class
tail. Sorting only function bodies by mapped target address (leaving file-scope
statics in place) required one natural forward declaration for
InterpolateWrappedAngle; focused replay remained 44 / 44 exact and reduced the
linked object to three residual inversions.
Those three inversions proved a real owner boundary. The target sequence is
GameManager::IsSoloHuman/IsSoloYoukai/GetLives @ 0x0042F230..0x0042F2B0,
then the recovered AsciiManagerScale block at 0x0042F2D0..0x0042F315,
then Gui::IsBossPresent @ 0x0042F320 and Gui::FUN_0042f340 @ 0x0042F340,
followed immediately by BulletManager::Initialize @ 0x0042F360. Both donor
and recipient use the same /Od PCH profile. Moving the unchanged Gui bodies
onto the end of AsciiManagerScale.cpp and canonicalizing their match-unit
owners produced an exact four-function 0x0042F2D0..0x0042F355 block. Donor
replay passed 42 / 42, recipient replay 4 / 4, and the cold aggregate
replay passed 1,105 / 1,105. EnemyManager.obj improved from 18 anchors /
32 inversions / 5 runs / 111,744 span to 17 / 0 / 1 / 89,616;
AsciiManagerScale.obj is 4 / 0 / 1 / 0.
The fourteenth bounded pass repaired Supervisor.obj. Three target-order
resets had two distinct causes. SoundPlayer::UpdateFades @ 0x00447764 and
GameManager::IsExtraUnlockedWithAllTeams @ 0x00447D04 already had the correct
Supervisor production owner, but header visibility made VC7 emit their exact
standalone bodies at earlier first-use positions. Their target locations are
respectively between TakeSnapshot/LoadConfig and LoadConfig/LoadMusic.
Both headers are now declaration-only and the unchanged bodies are explicit at
those target-local positions in Supervisor.cpp. The GameManager helper has
multiple Title callers, but their accepted units already contain real REL32
calls; focused replay passed 49 / 49 Supervisor units, 23 / 23 TitleScreen
units, and 11 / 11 TitleReplay probe units. The third reset was ordinary
lexical disorder: UpdatePlayTime @ 0x004482A1 now precedes
UpdateGameTime @ 0x00448418. The cold aggregate replay passed 1,105 /
1,105, and Supervisor.obj improved from 43 anchors / 29 inversions / 4
runs / 58,804 span to 43 / 0 / 1 / 51,658.
The fifteenth bounded pass repaired ReplayManager.obj. The object mixed plain
lexical disorder with two same-owner deferred header bodies. LoadReplayData @ 0x00451D90 is now the first Replay definition, the constructor moved from the
file head to its target slot after DeleteReplayManager @ 0x00453080, and the
existing AppendFormat @ 0x00453B80 tail is followed by explicit
GameManager::SetClockTime @ 0x00453C60 and ReplayManager::IsDemo @ 0x00453CC0. The latter two were declaration-only header repairs; each has only
ReplayManager production callers. Focused replay passed 18 / 18, the cold
aggregate replay passed 1,105 / 1,105, and linked metrics improved from 15
anchors / 23 inversions / 4 runs / 12,352 span to 15 / 0 / 1 / 192.
The sixteenth bounded pass repaired PbgFile.obj. The sole target-order reset
was caused by the inline base-class IPbgFile constructor/destructor emitting
immediately after the derived CPbgFile constructor. The target instead keeps
the complete CPbgFile implementation through GetFullFilePath @ 0x00473B40,
then emits IPbgFile::IPbgFile @ 0x00473C40 and ~IPbgFile @ 0x00473C60 before
the following PBG/LZSS code. Making those base bodies declaration-only in the
header and defining the unchanged bodies at the tail of PbgFile.cpp preserved
15 / 15 focused units. The cold aggregate replay passed 1,105 / 1,105,
and PbgFile.obj improved from 13 anchors / 20 inversions / 2 runs / 1,456
span to 13 / 0 / 1 / 48.
The seventeenth bounded pass repaired EffectManager.obj. This TU had 49
mapped function definitions in heavily scrambled lexical order while file-scope
statics and the g_EffectTemplates data definition were interspersed between
them. Rebuilding the source as the same declarations/data in their original
relative order followed by all real function bodies sorted by mapped target
address required only natural forward declarations for
AdjustStageEffectDrawPosition and HasAnimationEnded. No behavior or owner
changed. Focused replay passed 52 / 52
and the cold aggregate replay passed 1,105 / 1,105. EffectManager.obj
improved from 8 anchors / 17 inversions / 4 runs / 23,424 span to 8 / 0 /
1 / 304.
The eighteenth bounded pass repaired ResultScreen.obj and the adjacent
ScoreDat owner boundary. AnmManager::ReplaceSurface @ 0x0045A3FD has only a
ResultScreen production caller, so its header body is now declaration-only and
the unchanged explicit definition follows MoveCursorHorizontally @ 0x0045A2F2 in ResultScreen.cpp. ScoreListNode::ScoreListNode @ 0x0045A4DC
required stronger evidence: a naive move into /Od ScoreDat.cpp produced a
0x2B-byte body instead of the accepted 0x20 bytes, but placing it immediately
after Catk::WasAttemptedWithShot @ 0x0045A4A0 under that function's existing
#pragma optimize("s", on) reproduced the exact body. The canonical owner now
is ScoreDat.obj. Focused replay passed 33 / 33 ResultScreen and 14 / 14
ScoreDat units; cold aggregate replay passed 1,105 / 1,105. Linked metrics
are ResultScreen.obj 30 / 0 / 1 / 0 and ScoreDat.obj 13 / 0 / 1 / 9.
The nineteenth bounded pass repaired PbgArchive.obj. Two same-owner header bodies were deferred at first use rather than their target tail positions: CPbgFile::ReadInt @ 0x004751E0 and PbgArchiveEntry::~PbgArchiveEntry @ 0x00475270. Making both declaration-only and defining the unchanged bodies after CopyFileName / after SeekPastString respectively preserved 15 / 15 focused units. Cold aggregate replay passed 1,105 / 1,105, and PbgArchive.obj improved from 14 anchors / 11 inversions / 3 runs / 2,032 span to 14 / 0 / 1 / 32.
The twentieth bounded pass repaired TitleScreen.obj. Two target-late helper
bodies were emitted at their first users because they were still header-inline:
TitleScreen::SetKeyNumberSprite @ 0x00469FA9 and
GameManager::IsLastWordSpellCardAttempted @ 0x0046FD5F. Both are now
header declarations with unchanged explicit definitions after
OnUpdateKeyConfig and DrawSpellStageSelect respectively. Focused replay
passed 23 / 23 TitleScreen units and 11 / 11 TitleReplay probe units;
cold aggregate replay passed 1,105 / 1,105. TitleScreen.obj improved from
23 anchors / 4 inversions / 3 runs / 15,726 span to 23 / 0 / 1 / 11,979.
The twenty-second bounded pass repaired MusicRoom.obj. The first global
visibility probe for Supervisor::IsMusicPreloadEnabled @ 0x00449C79 correctly
reproduced the standalone body but changed two accepted Supervisor callers by
1-2 bytes, so it was rejected. The accepted repair makes the header
declaration-only only while compiling MusicRoom via a TU-local preprocessor
gate; every other TU still sees the original inline body and therefore keeps its
caller codegen. The explicit MusicRoom-tail definition is itself inline, so it
remains a COMDAT and coexists with the other header copies while production
object order selects the target-local MusicRoom copy. Focused replay passed
MusicRoom 11 / 11, Supervisor 49 / 49, and SoundPlayer 25 / 25; cold
aggregate replay passed 1,105 / 1,105. MusicRoom.obj improved from 10
anchors / 7 inversions / 2 runs / 2,621 span to 10 / 0 / 1 / 0.
The twenty-first bounded pass repaired ScreenEffect.obj. Its only reset was ordinary lexical disorder: source emitted DrawArcadeFade @ 0x0045BC40 before DrawPartialFade @ 0x0045BBF0, opposite the target. Swapping those complete bodies preserved 21 / 21 focused units; cold aggregate replay passed 1,105 / 1,105. ScreenEffect.obj improved from 14 anchors / 1 inversion / 2 runs / 160 span to 14 / 0 / 1 / 48.
The twenty-third bounded pass repaired the production ECL owner lane that had
been split across EnemyManager.obj, SpellCard.obj, and main.obj. Target
mapping shows EclManager::RunEcl @ 0x004184B0 followed by the ECL operand/run
neighborhood at 0x0041F000..0x0041FDF0; all four donor/recipient TUs use the
same /Od /Yu"th_pch.h" profile. The unchanged HasParentChain,
SetStoredVector, spellcard ECL flag helpers, attached/parent-chain helpers,
IsCaptureValid, GetTimerFrames, and ZunTimer::operator+= bodies now live
in production EclManager.cpp, with Spellcard::IsBombDamageEnabled @ 0x0042DFF0
also following the EclManager timeline getters. Canonical match-unit owners were
updated with the bodies. Focused replay passed 18 / 18 EclManager, 39 /
39 EnemyManager, 30 / 30 SpellCard, and 30 / 30 main units. Cold
aggregate replay passed 1,105 / 1,105. SpellCard.obj improved from 33 /
18 / 2 / 151,472 to 27 / 0 / 1 / 61,920, and main.obj from 26 / 16 /
2 / 146,336 to 25 / 0 / 1 / 16; recipient EclManager.obj is 10 / 0 /
1 / 60,848. The large EclManager span is target distance, not an order reset.
The twenty-fourth bounded pass repaired AnmManager.obj. Its only reset was
AnmManager::SpriteHasTexture @ 0x004622C0, a PCH-defined inline body emitted
after DrawTriangleFan @ 0x00464EC0 instead of between SetInterruptArray @ 0x00462270 and ExecuteScriptArray @ 0x00462310. A MusicRoom-style TU-local
header gate cannot work inside a /Yu consumer because AnmManager.hpp is
already fixed inside the PCH. The accepted PCH-safe shape therefore makes the
header declaration-only, provides the target-local inline COMDAT body in
AnmManager.cpp, and provides the same early inline definition in
TitleScreen.cpp so its optimized caller still compiles with body visibility.
Focused replay passed 83 / 83 AnmManager and 23 / 23 TitleScreen units;
cold aggregate replay passed 1,105 / 1,105. AnmManager.obj improved from
56 anchors / 19 inversions / 2 runs / 359,840 span to 56 / 0 / 1 /
359,792. The large span is the real distance from its early target COMDATs at
0x00406700/0x0040B580 to the main 0x0045E430+ region, not an order reset.
The twenty-fifth bounded pass repaired the last remaining production layout,
AsciiManager.obj. Four independent fixes were required. First,
AnmVmBase::Initialize @ 0x004068E0 became an inline COMDAT so it could join
the target helper emission queue. Second, Float3::Float3(f32,f32,f32) @ 0x00404720 moved from its header body to an unchanged ordinary definition
between PauseMenu::OnUpdate and PauseMenu::OnDraw, matching the target-local
slot. Third, only lexical order changed inside shared headers: ZunTimer
operator=/SetCurrent now precede </>, and GameManager::IsSpellPractice
precedes IsReplay, matching the observed COMDAT order. Finally,
GetGaugeInterrupt, ResetStrings, and SetSpaceWidth @ 0x00407140..7180
were made declaration-only and their unchanged bodies/canonical owners moved to
the already recovered /Od /Yu AsciiManagerGauge.obj target cluster. The
relevant accepted callers contain real REL32 calls. Final focused replay passed
62 / 62 AsciiManager, 5 / 5 AsciiManagerGauge, 41 / 41 Gui, and
62 / 62 Player units. Moving the Float3 constructor out of the shared
header exposed one final caller-visibility dependency under a full cold build:
Supervisor, TitleScreen, and the TitleReplay probe compiled larger until the
same unchanged constructor body was restored as an early TU-local inline
definition in those callers. This preserves their former inline compilation
context without changing the target-local Ascii owner. The final cold aggregate
replay then passed 1,105 / 1,105, and a fresh normal link retained zero
inversions. AsciiManager.obj improved from 63 anchors / 86 inversions / 6
runs / 9,600 span to 60 / 0 / 1 / 736; AsciiManagerGauge.obj is 5 / 0 /
1 / 32.
The twenty-sixth bounded pass restored the Spellcard::StartSpell service
cluster at 0x00415C60..0x00415CE0. Two exact helper bodies had been placed in
their class implementation TUs during early local matching even though target
layout puts them immediately after StartSpell:
BulletManager::ClearBulletsForTransition @ 0x00415C60 and
Enemy::ResetBulletRankInfluence @ 0x00415C80, followed by the already-correct
Background::background_fun_00415ce0 @ 0x00415CE0 in Spellcard.cpp. Moving
the unchanged bodies into Spellcard.cpp and rebinding their canonical exact
owners preserved focused replay at 32 / 32 SpellCard, 37 / 37
BulletManager, and 38 / 38 EnemyManager units. A fresh production link
reduced BulletManager.obj from 25 anchors / 104,240 span to 24 / 176
and EnemyManager.obj from 14 / 89,792 to 13 / 36,704. The SpellCard
cluster from StartSpell through the cut-in helpers now has one uniform linked
drift; its object remains 29 anchors / 0 inversions / 1 run / 128 span.
The twenty-seventh bounded pass closed the production-owner side of the main
ECL dispatcher dependency lane. EclManager::RunEcl @ 0x004184B0 remains
26,638 / 26,638 authored bytes and 27,398 / 27,398 compared bytes
strict exact while provisional service/global identities were rebound to their
natural production owners. The same pass canonicalized the exact integer and
float operand resolvers, restored Enemy::ClampPosition @ 0x0042C180 to
EnemyManager.cpp, and promoted the target ECL call-parameter/interpolator/EX
dispatch data owners into EclGlobals.cpp. Focused replay passed 24 / 24
EclDependencies, 33 / 33 EclExIns, 4 / 4 EnemyTimeline, 39 / 39
EnemyManager, 32 / 32 SpellCard, and all four resolver units. A no-/FORCE
diagnostic link of production objects plus the exact ECL Run/Dependencies/
Helpers/Operands/Timeline/ExIns group now resolves completely; its only
remaining unresolved object is the still-probe-only EnemyManagerUpdate.obj,
with 77 provisional owner symbols. Treat that object as the next bounded
promotion blocker rather than reopening already-closed RunEcl/resolver owners.
The twenty-eighth bounded pass closed that final authored ECL owner blocker.
EnemyManagerUpdate::OnUpdate @ 0x0042C660 remains 6,198 / 6,198 authored bytes
and 6,214 / 6,214 compared bytes strict exact while its 77 provisional
external identities were routed back to natural production owners. The pass
restored the real g_EclManager @ 0x004ECCB8 data owner; its 0x168-byte
extent ends exactly at g_EclCallParameters @ 0x004ECE20. It also rebound
GameManager/Player/subsystem singletons, ZunTimer calls, Enemy methods, the ECL
timeline lane, canonical fabsf, and the target-exact 0x0041FD20 /
0x0042ADB0 helpers. The last data alias was proven to be the member now named
g_EnemyManager.spawnTemplate.bulletSpawnDescriptor: BulletSpawnDescriptor is
exactly 0x210 bytes and its address is 0x00577F20 + 0x2E24 = 0x0057AD44, matching
the target memcpy source. A no-/FORCE diagnostic link of production objects
plus EclRun, EclDependencies, EclHelpers, EclOperandsInt/Float, EnemyTimeline,
EclExIns, and EnemyManagerUpdate now exits successfully with zero unresolved
externals. Cold authored replay remains 1,105 / 1,105 exact. The next
bounded step is production promotion and fresh whole-image measurement, not
further owner canonicalization.
The twenty-ninth bounded pass moved the closed ECL lane into the real production
link without changing its compiler contract: EclRun, EclDependencies, EclHelpers,
EclOperandsInt/Float, EnemyTimeline, EclExIns, and EnemyManagerUpdate now build as
build/*.obj with the same generic /Oi /Gr profile that produced their accepted
probe objects. All 69 / 69 promoted match units replay strict exact from the
production paths. Fresh whole-image routing then exposed three earlier probe lanes as
missing bodies inside existing target TUs rather than standalone objects.
PauseMenu::OnDraw / RetryMenu::OnDraw were restored to their real void ABI and
inserted into AsciiManager.cpp, which now replays 65 / 65 exact and reduces its
former 0 / -304 / -752 drift staircase to only 0 / -80-class alignment/COMDAT
noise. GameManager::GameplaySetupThread @ 0x0043ABD7 and FUN_0043BBE1 returned to
GameManager.cpp; the target g_TimeRequirementParams @ 0x004C77F0 storage is a
semantic 9 x 4 i32 table, the setup member has the observed __fastcall void(void *)
ABI, and GameManager.obj now replays 42 / 42 exact with the former 3,418-byte
drift jump removed. Fifteen Player option/shot functions were restored into the two
target gaps in Player.cpp; after preserving their VC7 #pragma var_order contracts,
Player.obj replays 77 / 77 exact and the former -63,824 -> -67,152 -> -70,960
staircase collapses to one approximately constant drift. The resulting fresh linked
PBG lane is only 4,896..4,848 bytes early, down from 16,672 bytes before these
owner restorations.
The promoted ECL code is target-backed but its object placement is not yet target-like.
The current normal image has .text raw size 0xBA800 versus target 0xB1C00,
.rdata 0x15200 versus 0x11400, .data 0x6400 versus 0x6E00, and file-size
delta +49,152 bytes. Do not interpret the .text overshoot as evidence that the
exact ECL bodies are spurious: their target anchors are valid, but the eight promoted
objects are currently appended after the main authored lane. In particular
EclDependencies.obj now reports 160 target-order inversions / 7 runs and must be
partitioned/routed by target neighborhoods; EclExIns.obj has a small two-run kink.
The next bounded whole-image task is ECL object order/partition, followed by the small
remaining authored gap (PBG about 4.9 KB early), not further ECL owner canonicalization.
The production link contains 44 authored/PBG object files. The fold-aware
whole-image report now includes the promoted ECL objects as production layout
evidence. Existing settled objects such as AsciiManager, GameManager, Player, and the
PBG lane retain one target-order run, while the newly promoted mixed ECL dependency TU
is intentionally the active partition candidate. utils.obj remains folded-only and
EclGlobals.obj remains data-only; neither should be used as TU-placement evidence. The final cold authored replay remains 1,105 /
1,105 exact, config/claims.csv remains header-only, and the final
tracking/progress/CI/doc/whitespace gates pass. Do not infer whole-image byte
identity from this milestone: section drift, imports/resources, and non-layout
reconstruction work remain separate evidence classes. For future layout
changes, preserve the same acceptance rule: target-neighborhood evidence,
focused donor/recipient/caller replay, a cold normal linked-order measurement,
and a cold aggregate exact replay before committing.
Large intra-object drift means today's source combines or orders target TUs
differently, so permuting the existing object list alone cannot solve it. A
run reset is a routing clue, not automatically a TU boundary: the Player pass
showed that one investigation may require both a real split and target-order
restoration inside the retained TU. The 17
extra imports already have bounded contributing archive members; do not hide
them with /OPT:REF. A trial of that flag made the import set exact while
shrinking .text to 0x72B6F and losing 212 located accepted anchors, so
/OPT:NOREF remains required by stronger whole-image evidence. ICF is a
separate target-proven contract: constructor-iterator sites across multiple
subsystems load the same 0x0040B580 body for four differently decorated
vertex constructors, and the linked image also contains the reviewed folded
sound-manager constructor at 0x004716E0. Keep /OPT:ICF enabled. Return to
CRT/D3DX exact recovery only if a subsequent diff identifies a specific member
operation. Do not copy the target icon payload and do not claim whole-image
exactness from successful linking or same-size sections.
Use docs/TOOLS.md to choose commands. Keep .analysis/ disposable and update
this file whenever the active milestone or a durable blocker changes.
The latest semantic checkpoint types the Replay serialization boundary without
inventing meanings for unconsumed storage. StageReplayData::inputStream owns
the variable input tail, while ReplayDataHeader::stageFpsData is correctly a
nine-entry table of byte-stream pointers. Zero-filled serialized gaps are
reserved storage; the payload constant at +0x120 and manager reset word at
+0x4E are named only for their proven write protocols. Four manager
Float3s remain explicit unknown vectors because an opaque-storage trial
removed their target-visible implicit construction (constructor 58 -> 14
bytes); the ranges at +0x48/+0x7C/+0xCC remain opaque for lack of consumers.
Focused Replay replay passes 18 / 18 exact, the required non-reuse cold
aggregate passes 1,106 / 1,106 exact, the normal VC7 image links, and the
complete i386 Linux build plus fixed-layout verifier pass. This is a bounded
semantic checkpoint, not a whole-program semantic-completion claim.
PR #2 merged the first semantic reconstruction phase into main as merge
commit 4fb955225ff9621d2eaa5ff2113a709ff5576407; the continuing
semantic/typed-reconstruction branch was then fast-forwarded from that exact
mainline state. The next checkpoint classifies residual ANM/Ascii/Sound/MIDI
state without inventing consumers. ANM +0x0C is proven to count scripts
started during the frame, Ascii script 9 belongs to an auxiliary gauge VM, and
several Sound/MIDI fields are now explicitly marked unconsumed or reset-only
where the authored corpus supplies no stronger behavior. Serialized ANM
unknowns retain neutral offset-bearing names and layout assertions.
Focused replay across the six affected production objects passes 259 / 259 exact. The required non-reuse cold aggregate passes 1,106 / 1,106 exact, the normal VC7 image links, and the complete i386 Linux container build plus fixed-layout verifier pass. Continue treating the remaining heuristic anonymous/opaque list as evidence-routing work: it is neither an exactness regression nor proof that every residual byte has a recoverable semantic name.
The following score/result checkpoint classifies the remaining ResultScreen,
TitleScreen replay-enumeration, Hscr, and Catk tail state. Reset-only and
unconsumed runtime fields are named only for their observed protocols, while
serialized alignment/tail bytes remain explicit reserved storage. The byte at
Hscr + 0x166 is now defaultScoreMarker: only the synthetic leaderboard
producer writes it, and no unobserved consumer is claimed. All renamed owner
offsets are asserted.
Focused replay across ResultScreen, ScoreDat, TitleScreen, and GameManager
passes 113 / 113 exact; cold aggregate replay passes 1,106 / 1,106
exact; the normal VC7 image and complete i386 Linux image both link; and the
Linux fixed-layout verifier passes. TitleScreen::RegisterChain @ 0x0047146D
is currently 281 / 281 exact under the repository Oracle. The sole
authored/non-accepted row is instead
ReplayManager::PlaybackExtendedInputAndFps @ 0x004526C0 (361 bytes), which
has no configured match unit. Treat adding and proving that unit as a
separate Oracle-ledger closure task, not as evidence of a current mismatch.
The current Bullet residual checkpoint replaces generic transform payload
reads with kind-specific union views while preserving opcode 111's generic raw
write boundary. Descriptor +0x1FA, template +0xD40, live-bullet +0xDBC,
and the remaining unaccessed ranges are classified only as unconsumed or
alignment storage; sprite height +0xD41 is named from its exact loaded-sprite
producer. BulletManager.hpp/.cpp now has zero semantic-router candidates.
Focused replay passes 94 / 94 across BulletManager/EclDependencies/EclExIns
plus 1 / 1 for EclRun; the cold aggregate passes 1,106 / 1,106 exact;
the normal VC7 image and complete Linux i386 image link; and the Linux fixed-
layout verifier passes. Continue with Enemy/ANM residual storage without
weakening the sole Replay near-match blocker.
The Enemy residual checkpoint classifies the remaining flag bits, neutral
words/dwords, and structural alignment ranges in the shared Enemy and
EnemyManager layouts without inventing gameplay meanings. Explicit
alignment arrays are retained where removing them would alter the
target-visible Enemy aggregate-copy source shape, and every classified range
is offset-asserted. Focused replay across EnemyManager, EnemyManagerUpdate,
EclRun, EclDependencies, EclExIns, Gui, and SpellCard passes 171 / 171
exact; the required cold aggregate passes 1,106 / 1,106 exact; the normal
VC7 image and complete Linux i386 image link; and the Linux fixed-layout
verifier passes. EnemyManager.hpp now has no semantic-router candidates.
Continue with the ANM residual family while preserving the sole Replay
near-match as a separately documented Oracle limitation.
The ANM residual checkpoint closes the remaining AnmManager.hpp router
candidates. Serialized record fields with no consumer are named reserved;
runtime tails that are only zeroed or aggregate-copied remain explicitly
unconsumed; and manager +0x24C6..+0x24C7 is now compiler-owned alignment
before the asserted currentSprite @ +0x24C8. Focused AnmManager replay
passes 82 / 82 exact before and after the edit; the required cold aggregate
passes 1,106 / 1,106 exact; the normal VC7 image and complete Linux i386
image link; and the Linux fixed-layout verifier passes. Continue on
semantic/typed-reconstruction with Player/ECL/Game residual state. Do not
open or merge another PR until explicitly requested; preserve the sole Replay
near-match as a separate Oracle limitation.
The Player residual checkpoint classifies the last neutral SHT, collision,
option, Bomb, shot, and Player bytes without inventing consumers. Four proven
alignment ranges are now compiler-owned padding, while serialized and runtime
dwords remain reserved/unconsumed and offset-asserted. Focused Player plus
PlayerBomb replay passes 136 / 136 exact before and after the edit; the
required cold aggregate passes 1,106 / 1,106 exact; the normal VC7 image
and complete Linux i386 image link; and the Linux fixed-layout verifier
passes. Player.hpp now has no semantic-router candidates. Continue on the
branch with ECL/Game residual state, without opening or merging a PR until the
user explicitly requests it.
The ECL/Effect residual checkpoint classifies only storage with no independent consumer and serialized instruction bytes skipped by all exact dispatch views. Context tail alignment stays explicit because whole-context assignment is target-visible. Focused replay across EclManager, EclRun, EclDependencies, EclExIns, and EffectManager passes 128 / 128 exact before and after the edit, including full RunEcl; the required cold aggregate passes 1,106 / 1,106 exact; the normal VC7 image and complete Linux i386 image link; and the Linux fixed-layout verifier passes. Continue with Game/GUI/ Ending residual state on the branch. Do not open or merge a PR until the user explicitly asks.
The GameManager residual checkpoint proves three formerly anonymous ranges as compiler alignment and retains only two independently unconsumed dwords. All surrounding replay, RNG, stage, playtime, frame-skip, and active-frame fields are offset-asserted. Focused GameManager replay passes 42 / 42 exact before and after the edit; the required cold aggregate passes 1,106 / 1,106 exact; the normal VC7 image and complete Linux i386 image link; and the Linux fixed-layout verifier passes. Continue with the bounded GUI/Ending residuals and audit the Replay constructor-sensitive opaque ranges separately. Keep all work on the branch until the user explicitly requests another PR/merge.
The GUI/Ending residual checkpoint removes the fictitious final message byte
in favor of compiler-owned tail alignment and classifies the two remaining
aggregate-cleared dwords as explicitly unconsumed. Target-pinned packets for
GuiMsgVm::GuiMsgVm @ 0x00437CE2, Gui::RegisterChain @ 0x00437AD0,
Ending::Ending @ 0x004297B0, and Ending::OnUpdate @ 0x00429860 are exact.
Focused GUI plus Ending replay passes 52 / 52 exact before and after the
edit; the required cold aggregate passes 1,106 / 1,106 exact; the normal
VC7 image and complete Linux i386 image link; and the Linux fixed-layout
verifier passes. Continue with the Replay constructor-sensitive residual
storage and keep all work on the branch until the user explicitly requests a
PR/merge.
The Replay residual checkpoint preserves the four target-observed Float3
constructor calls while classifying those objects and the three manager byte
ranges as explicitly unconsumed; serialized stage byte +0x23 is reserved
wire storage rather than false compiler padding. The now-unused
unknown_fields macro is removed. Target-pinned Replay constructor and
RegisterChain packets are exact, focused Replay replay passes 18 / 18
exact before and after the edit, the required cold aggregate passes 1,106
/ 1,106 exact, the normal VC7 image and complete Linux i386 image link, and
the Linux fixed-layout verifier passes. The four-category source router is
now zero, but this is a layout-routing milestone rather than a whole-program
semantic-completion claim. Continue by auditing router-invisible neutral
function names and evidence-limited state. Keep all work on the branch until
the user explicitly requests a PR/merge.
The Effect camera-particle naming checkpoint replaces the last six authored
FUN_00426... identities with three evidence-backed initializer/update pairs:
tinted Boss-tracking, rising Boss-tracking, and spinning camera particles.
Template slots 51, 63, and 19 respectively establish the pairings; names stop
short of guessing the artwork. Source, portable template mirror, tracking
ledgers, accepted rows, and VC7 decorated-symbol units move together. Focused
EffectManager.obj replay passes 52 / 52 exact, the required cold
aggregate passes 1,106 / 1,106 exact, the normal VC7 image and complete
Linux i386 image link, and the Linux fixed-layout verifier passes. Continue
the router-invisible neutral-field audit on the branch; do not open or merge a
PR until explicitly requested.
The neutral-identifier checkpoint replaces the remaining production-source
unknown*/unk* names with evidence-bounded unconsumed,
constructorCleared, or serializedReserved identities across AsciiManager,
Background, Midi, SoundPlayer, ScoreDat, ResultScreen, and zwave. New offset
assertions pin the affected public and wire layouts. Focused cold replay of
the seven affected objects passes 238 / 238 exact before and after the
edit; the required cold aggregate passes 1,106 / 1,106 exact; the normal
VC7 image and complete Linux i386 image link; and the Linux fixed-layout
verifier passes. Production source now has no unknown*, unk*, or authored
address-style FUN_* identifiers, while the four-category semantic router
remains zero. Continue auditing router-invisible weak names and intentionally
neutral state on semantic/typed-reconstruction; do not open or merge a PR
until explicitly requested.
The Item spawn-state checkpoint replaces the remaining unknown item enums with
behavior-backed death-drop spread, ordinary time-orb rise, and apex-gated
time-orb request/state identities; the two unproduced values remain explicitly
reserved. All authored constant SpawnItem calls now use typed item/state
names, while genuinely data-driven ECL/drop/cancel values retain casts.
Target-pinned packets cover SpawnItem @ 0x004400A0 and
OnUpdate @ 0x00440500. Focused cold replay of the eight affected objects
passes 244 / 244 exact before and after the edit; the required cold
aggregate passes 1,106 / 1,106 exact; the normal VC7 image and complete
Linux i386 image link; and the Linux fixed-layout verifier passes. Continue
the router-invisible semantic audit on semantic/typed-reconstruction; do not
open or merge a PR until explicitly requested.
The Player collision-region API checkpoint replaces all four allocator
signatures' value1..value4 parameters with width/height or initial-radius/
growth plus damage/collision-value/lifetime roles. It preserves the
target-observed circle-cancel argument-order asymmetry. Target-pinned packets
cover 0x0044DE60, 0x0044DF00, 0x0044DFA0, and 0x0044E040; focused
Player replay passes 77 / 77 exact before and after the edit, with each
allocator at 153 / 153; the required cold aggregate passes 1,106 / 1,106
exact; the normal VC7 image and complete Linux i386 image link; and the Linux
fixed-layout verifier passes. Continue the weak-parameter audit on
semantic/typed-reconstruction; do not open or merge a PR until explicitly
requested.
The shared-helper parameter checkpoint removes residual paramN/decompiler
locals from seven exact helpers in AsciiManager, TextHelper, Supervisor,
ScreenEffect, and TitleScreen. Names are bounded to directly observed roles:
two popup digit sequences, music preload slot/fade seconds, ScreenEffect
duration/variant parameters/draw priority, TextHelper's A1R5G5B5 gentle
falloff selector, and pie fraction/diameter. Target-pinned packets cover all
seven addresses; focused replay passes 178 / 178 exact before and after
the edit; the required cold aggregate passes 1,106 / 1,106 exact; the
normal VC7 image and complete Linux i386 image link; and the Linux fixed-
layout verifier passes. Continue the remaining generic-local/API audit on
semantic/typed-reconstruction; do not open or merge a PR until explicitly
requested.
The background/MIDI/spell-portrait checkpoint closes three high-signal weak-
identifier families. Background cubic interpolation now exposes its
start/end values and tangents, and RegisterChain's stage index is named from
both production callers and the registeredStage write. MidiOutput::ProcessMsg
now reads as a running-status MIDI event decoder, including its TH08 loop
checkpoint/jump controllers and fade-adjusted channel volume. Spellcard's
+0x90C and +0xE54 VMs are named only as enemy-portrait auxiliaries with
their observed draw modes; no absent asset role is guessed. Target packets
cover all nine affected exact functions; focused post-edit replay passes
94 / 94 exact; the required cold aggregate passes 1,106 / 1,106 exact;
the normal VC7 image and complete Linux i386 image link; and the Linux fixed-
layout verifier passes. Continue on semantic/typed-reconstruction; commit
and push stable checkpoints, but do not open or merge a PR until explicitly
requested.
The runtime-queue checkpoint replaces the last intentionally opaque local
names in Chain::ReleaseSingleChain and Controller::GetControllerInput with a
release-snapshot model and explicit WinMM/DirectInput state. Readable names
initially exposed VC7's identifier-sensitive stack allocation; target stack
evidence and #pragma var_order now preserve the exact 0x54/0x160 frames
without returning to a0/aa/aaa. GetControllerState retains and documents
the original discarded-GetDeviceState-HRESULT quirk. ResultScreen's VM and
keyboard animation locals and SoundPlayer's command/SFX queue locals are also
named by observed roles. Target packets cover all six functions; focused
replay passes 106 / 106 exact; the cold aggregate passes 1,106 / 1,106
exact; VC7 and Linux i386 images link; and the Linux fixed-layout verifier
passes. Continue on semantic/typed-reconstruction; do not open or merge a
PR until explicitly requested.
The runtime-owner checkpoint names Laser's midpoint-rendered bodyVm and
start-offset startCapVm; enemy-spawn ECL subroutine/life/drop/score arguments;
and DirectSound's current play/write cursors. The ECL global at 0x004E4B60
is proven to be the g_Background + 0xB30 spell-background view and now exposes
spellVmCount, spellVmScriptBase, and spellVms[2]. The synthetic
g_TargetPlayerPosition017D61AC symbol is proven to be g_Player.position @ +0x2B4; all production users and COFF relocation manifests now name the real
owner, and the obsolete Linux linker alias is gone. ECL helpers at
0x0041EFC0, 0x0041F110, and 0x0041F280 are now
FindAttachmentChainTail, SpawnChildAtScriptPosition, and
SpawnChildAtParentOffset, with source and every tracking ledger migrated
together. Focused replays pass 190 / 190, 182 / 182, and 25 / 25
exact for the three bounded groups; the required cold aggregate passes
1,106 / 1,106 exact; the normal VC7 image and complete Linux i386 image
link; and the Linux fixed-layout verifier passes. Continue on
semantic/typed-reconstruction; commit and push stable checkpoints, but do
not open or merge a PR until explicitly requested.
The ECL spell-background owner checkpoint removes three remaining synthetic
global identities. Target address 0x004ECCA8 is
g_Spellcard.scoreLimit @ +0x2638; ECL opcode 155 now writes the same readable
99999990 timeout limit as Spellcard::StartSpell. Addresses 0x004E4B60
and 0x004EA28C are respectively g_Background.spellVmCount @ +0xB30 and
g_Background.spellBackgroundDrawCallback @ +0x625C; all ECL extension and
Effect users now name the real Background fields directly, and the callback
slot carries the only assigned target function's __fastcall type. The stale
DIFFBUILD _g_Stage label at the Background base is also corrected. The
obsolete
target/modern bridge declarations, Linux linker alias, global-ledger rows, and
three synthetic relocation identities are gone; COFF addends continue to
prove the same absolute target addresses through the real owners. Focused
replay passes 86 / 86 exact, and the required non-reuse cold aggregate
passes 1,106 / 1,106 exact with zero failures. The normal VC7 image and
complete Linux i386 image link, and the Linux fixed-layout verifier passes.
Continue on semantic/typed-reconstruction; commit and push stable
checkpoints, but do not open or merge a PR until explicitly requested.
The ECL runtime-type checkpoint promotes the exact low/high interpreter helper
families from the temporary EclRunLowProposal / EclRunHighProposal
namespaces to EclRunLow / EclRunHigh, migrating all 17 helper identities,
the call-parameter global, and every source/configuration/COFF/linker reference
together. The dead private Player overlay is gone. ApplyRandomBiasedMove @ 0x004224A0 and DispatchShotInstruction @ 0x00422720 now take the real
Enemy * owner and shared EclRawInstruction; RunEcl and the high dispatcher no
longer carry duplicate raw-instruction or vector shells. The public
EnemyEclInterpolationSlot is now shared by the ECL context, RunEcl,
InterpolateLinear @ 0x00421120, InterpolateHermite @ 0x00421180, and slot
installation, with the callback table at 0x004C6C90 carrying its observed
__fastcall ABI. Focused replay passes 68 / 68 exact and the required
non-reuse cold aggregate passes 1,106 / 1,106 exact with zero failures.
The normal VC7 image and complete Linux i386 image link, and the Linux fixed-
layout verifier passes. Continue the semantic audit on
semantic/typed-reconstruction; commit and push stable checkpoints, but do
not open or merge a PR until explicitly requested.
The Enemy/ECL owner checkpoint removes the dormant standalone low/high
dispatcher harness and its unused Services/TargetApi abstractions; the two
.inl files now contain only the production lexical opcode bodies compiled by
EclManager::RunEcl. The temporary EclOperands::EnemyOverlay is gone:
Enemy::ResolveFloat @ 0x00420120, Enemy::DetachEnemyChain @ 0x0042ADB0,
both operand resolvers, all ECL helpers, and the EX-instruction family now use
the real Enemy * owner. Fifty-three function identities and 465 configured
COFF relocation references move to the corresponding VC7 Enemy * symbols
without changing their target addresses.
EclExInstruction is now a shared serialized record, and g_EclExInsn @ 0x004C6CB0 is a typed __fastcall callback table. Opcode 136 calls that ABI
directly; opcode 137 keeps an explicit compatibility cast because the target
reuses the same table with a different second-argument interpretation.
StartEnemySpell @ 0x00421280, EndEnemySpell @ 0x004212E0, and
Spellcard::StartSpell @ 0x004152A0 carry typed instruction/Enemy arguments,
while Spellcard::activeEnemy @ +0x004 names the same owner directly. Focused
replay across the 11 affected objects passes 213 / 213 exact. The required
non-reuse cold aggregate passes 1,106 / 1,106 exact with zero failures;
the normal VC7 image and complete Linux i386 image link, and the Linux fixed-
layout verifier passes. Continue on semantic/typed-reconstruction; commit
and push stable checkpoints, but do not open a PR or merge until explicitly
requested.
The Enemy spawn/lifecycle checkpoint makes EnemyManager::SpawnEnemy1 @ 0x0042A4E0 and SpawnEnemy2 @ 0x0042A680 return Enemy *, matching the
storage they scan and the timeline/ECL callers that immediately consume the
result as an Enemy. Their COFF-defined VC7 symbols are
QAEPAUEnemy@2@..., so all six configured references to each old QAEPAX...
identity move with the declaration. EnemyManager::Initialize @ 0x00429E00
and KillAllNonBossEnemies @ 0x0042EFB0 now traverse Enemy * records rather
than maintaining a byte cursor and repeating casts; chain cleanup and ECL
context calls likewise use their real fields directly. Focused replay across
the five affected objects passes 101 / 101 exact. The required cold
aggregate passes 1,106 / 1,106 exact with zero failures; the normal VC7
image and complete Linux i386 image link, and the fixed-layout verifier passes.
Continue on semantic/typed-reconstruction; commit and push stable
checkpoints, but do not open a PR or merge until explicitly requested.
The runtime-pointer checkpoint types two residual owner fields with complete
production-use evidence. Spellcard::lifetimeObject @ +0x263C is a
ChainElem * produced by Chain::CreateElem, submitted to the calc chain, and
cleaned through ChainElem lifetime state; RegisterChain and DeletedCallback no
longer reinterpret it. AnmManager::currentSprite @ +0x24C8 is an
AnmLoadedSprite *, matching the sole value compared and assigned in
Draw3D. Focused replay passes 175 / 175, 157 / 157, and 1,318 /
1,318 exact for Spellcard::RegisterChain @ 0x00417F60,
Spellcard::DeletedCallback @ 0x00418050, and AnmManager::Draw3D @ 0x00464470. The required cold aggregate passes 1,106 / 1,106 exact with
zero failures; the normal VC7 image and complete Linux i386 image link, and
the fixed-layout verifier passes. Continue on semantic/typed-reconstruction;
commit and push stable checkpoints, but do not open a PR or merge until
explicitly requested.
The Effect lifecycle/ownership checkpoint replaces the remaining false
AnmVm * view of full Effect objects. Target OnUpdate @ 0x00427BF0 and
OnDraw @ 0x00427F00 pass the Effect address in ECX to their indirect
callbacks, so the instance and 66-row template tables now carry typed Effect
update/initialize/draw callbacks. The splash/orbit and player radial/barrier
families consume Effect * directly; the one
AnmVm::UpdatePulsingRadialTrail table entry remains an explicit member-ABI
compatibility case.
All five spawn APIs at 0x00425430..0x00425B70 now return the Effect they
initialize, and GetFixedSlotEffect @ 0x004253E0 names the same fixed-pool
owner. Player deathbomb/work-item fields and ECL/Background/Spellcard callers
retain Effect *, taking &effect->vm only at genuine ANM boundaries. The
post-change QAEPAUEffect@2@... spawn/getter and PAUEffect@1@@Z callback
decorated identities were read from new VC7 objects before migrating every
configured relocation. Focused replay passes 26 / 26 exact; the required
non-reuse cold aggregate passes 1,106 / 1,106 exact with zero failures; the
normal VC7 image and complete Linux i386 image link; and the Linux fixed-layout
verifier passes. Continue on semantic/typed-reconstruction; commit and push
stable checkpoints, but do not open a PR or merge until explicitly requested.
The core-callback/update-owner checkpoint gives the projected-position ANM
callback its target-observed void __fastcall(AnmVm *, D3DXVECTOR3 *) ABI and
removes its void * call boundary. Target references also prove that
g_PlayerShotCollisionCallbacks @ 0x004C7F24 is a three-entry typed callback
array and that the replay difficulty strings beginning at 0x004C7F30 are a
separate ReplayManager.cpp owner, retiring the false nine-entry mixed table.
EnemyManager::OnUpdate @ 0x0042C660 is now the real non-static member proven
by target ECX use and the rebuilt ?OnUpdate@EnemyManager@th08@@QAEHXZ COFF
definition. The one-byte overlay and whole-object casts are gone; a distinct
portable Chain callback adapter preserves RegisterChain behavior. All nine
focused units pass exact, including OnUpdate 6,214 / 6,214 compared bytes
and RegisterChain 203 / 203. The required cold aggregate passes 1,106 /
1,106 exact with zero failures; the normal VC7 image and complete Linux i386
image link; and the Linux fixed-layout verifier passes. Continue on
semantic/typed-reconstruction; commit and push stable checkpoints, but do
not open a PR or merge until explicitly requested.
The residual-owner/Title-parity checkpoint removes Background's last authored
raw field access, gives the ECL context's opcode-137 per-frame callback and
instruction real types, preserves EnemyManager's ECL header/subtable through
their fields, types ReplayManager::LoadReplayData as ReplayData *, and moves
the three Title spell-history queries onto Catk. The mapping ledger now also
reflects the already exact ZunResult/Enemy * signatures for RenderObjects,
RunEcl, and AddedCallback.
A complete production/probe duplicate audit found three real portable Title
drifts: completion status used && cursor > 3 instead of the target's
independent || cursor > 3, and spell-card selection reversed two cursor
upper-bound comparisons. Production now follows the exact probe, and
completion rendering is single-owned by a shared .inl. Player option/shot,
score initialization, Stage-menu draw, and Gameplay setup duplicates have no
behavioral drift. Focused replay passes 11 / 11 exact; the required cold
aggregate passes 1,106 / 1,106 exact with zero failures; the normal VC7
image and complete Linux i386 image link; and the Linux fixed-layout verifier
passes. Continue the router-invisible API/config signature and intentionally
neutral-state audit on semantic/typed-reconstruction. Commit and push stable
checkpoints, but do not open a PR or merge until explicitly requested.
The child-ECL-runtime checkpoint replaces the raw 0x24B0 opcode-135 block
and its +0x8/+0x230/+0x6 arithmetic with EnemyChildEclBlock: one active
EnemyEclContext, a 16-context call stack, and signed call-stack depth, all
pinned by layout assertions. The word at +0x4 remains explicitly
unconsumed, and serialized ECL operands remain byte-oriented. The stale
AddSpriteToDrawBuffer mapping now records its exact
ZunResult (VertexTex1DiffuseXyzrhw *) API. The exact Title DrawPieChart probe
also corrected production's center vertex w initialization. Focused replay
passes 26,638 / 26,638, 211 / 211, and 510 / 510 exact; the required
non-reuse cold aggregate passes 1,106 / 1,106 exact with zero failures;
the normal VC7 image and complete Linux i386 image link; and the Linux
fixed-layout verifier passes. Continue on semantic/typed-reconstruction;
commit and push stable checkpoints, but do not open a PR or merge until
explicitly requested.
The authored-API-ledger checkpoint aligns 119 stale config/mapping.csv
signatures with the existing typed exact source and rebuilt VC7 COFF symbols.
It corrects receiver ownership, member/static conventions, return widths,
const pointer types, missing arguments, and varargs boundaries across the
major gameplay, UI, rendering, audio, and data APIs. Focused comparison
passes 119 / 119 exact; the required non-reuse cold aggregate passes
1,106 / 1,106 exact with zero failures; the normal VC7 image and complete
Linux i386 image link; and the fixed-layout verifier passes. None of the 119
gameplay/API rows in scope retains an unknown convention. Sixteen accepted
exact rows remain in the bundled CSound/CWaveFile, PBG, and LZSS helper-source
families; compiler-runtime, D3DX, initializer, and unselected rows remain
separate audit lanes. The single authored allocator residual is unchanged.
Continue with a bounded semantic-completion audit on
semantic/typed-reconstruction; commit and push stable checkpoints, but do
not open a PR or merge until explicitly requested.
The bundled-helper closure aligns 54 accepted CSound/CWaveFile, PBG, and LZSS
mapping signatures with their exact source and VC7 decorated symbols. The
accepted implementation/mapping intersection now contains zero unknown
calling conventions. The two remaining production m_unk* fields in
CSound are renamed to honest unconsumedDword28/2C; target-pinned evidence
only proves that Play clears +0x2C, so no stronger meaning is invented.
Focused replay passes 54 / 54 exact, including Play 236 / 236; the
required non-reuse cold aggregate passes 1,106 / 1,106 exact with zero
failures; the normal VC7 image and complete Linux i386 image link; and the
fixed-layout verifier passes. Continue the repository-wide semantic
completion audit on semantic/typed-reconstruction; commit and push stable
checkpoints, but do not open a PR or merge until explicitly requested.
The residual-accessor checkpoint completes the authored semantic-readability
audit. EclManager::GetTimeline @ 0x0042DFD0 now returns the rebased
EclTimelineInstruction *, and EnemyManager::OnUpdate @ 0x0042C660 consumes
it without an integer cast; the raw header keeps its correct pre-rebase wire
offset view. AnmVm::IsStopped uses ZunBool, and 14 residual API rows gain
their exact receiver, pointer, callback, Win32, wave, or argument types.
Focused replay passes 15 / 15 exact; the required non-reuse cold aggregate
passes 1,106 / 1,106 exact with zero failures; the normal VC7 image and
complete Linux i386 image link; and the fixed-layout verifier passes. The
production semantic-debt router is zero in all four categories, no bare
m_unk*/decompiler placeholder remains, accepted mappings have zero unknown
conventions and correct __thiscall receivers, and constructor/lifecycle
families are typed. Retained reserved/unconsumed storage and runtime/D3DX
library work are explicit evidence boundaries. The authored semantic phase
is therefore broadly complete; remain on semantic/typed-reconstruction,
commit and push the checkpoint, and do not open a PR or merge until explicitly
requested.
The point-of-collection auto-collect correction is tracked by
N0zoM1z0/th08#10 on fix/item-autocollect-power-threshold. Target
ItemManager::OnUpdate @ 0x00440500 calls GetPower, converts its integer
result, and compares it with the qword at 0x004B5B30; the canonical target
bytes 00 00 00 00 00 00 60 40 prove double 128.0, not the reconstructed
0.0. Source now uses 128.0, VC7 naturally emits
__real@4060000000000000, and the match-unit relocation records the target
literal as data_hex = "0000000000006040". Shared validation rejects both a
COFF-symbol/declared-byte mismatch and a declared-byte/target-byte mismatch,
closing the relocation-replay hole that had hidden the gameplay difference.
Focused ItemManager replay passes 24 / 24 exact, including the complete
OnUpdate 1,989-byte authored / 2,025-byte code-plus-table extent. A
single-job non-reuse cold replay passes 1,106 / 1,106 exact; the normal VC7
image links; target-independent CI and tracking validation pass; and the i386
Linux container build links and passes the fixed-layout verifier. Authored
and accepted counts are unchanged because this corrects the semantic identity
of an already accepted relocation rather than adding a function or byte range.
The repository-wide floating-literal follow-up audits every configured 32-bit
or 64-bit VC7 __real@... relocation instead of checking only rows that opted
into data_hex. All 1,548 current literal relocations now agree with the
hash-attested target; the audit corrected 12 stale rows in five already
accepted units:
ReplayManager::SaveReplay @ 0x004531F0:10000.0fat0x004B42A0;BulletManager::RemoveAllBullets @ 0x00430830:32.0fat0x004B42CC;Player::UpdateRespawnAnimation @ 0x0044D180:30.0fat0x004B4534;InitializeDirectionalOffset @ 0x004270C0:1.0fat0x004B4338;WarpBulletsAcrossNarrowBarrier @ 0x00423A60: eight corrected branch-owned references to67.8822556f @ 0x004B48E8and135.7645111f @ 0x004B48EC.
Those values and branch ownership are direct target observations. The score,
laser-item spread, respawn, effect-motion, and mirror-barrier descriptions are
semantic interpretations of the surrounding exact code. All accepted units
in the five affected objects replayed exact (18 + 37 + 77 + 52 + 33), and
the five focused functions replayed exact individually. A single-job
non-reuse cold rebuild then passed 1,106 / 1,106 exact with zero failures.
The normal VC7 image linked; the complete Linux i386 build linked and passed
the fixed-layout verifier. scripts/test-match-literals.py protects both
single- and double-precision decoding plus malformed-manifest rejection, and
validate-tracking.py --require-target now fails on any future __real
symbol/target-data mismatch even when data_hex is absent.
The latest protocol-readability checkpoint names all 184 ECL opcodes and
removes numeric dispatch labels without changing their physical handler order.
It separates ECL interaction selector flags from Enemy storage flags, names
stable GameManager raw masks, adds exact-expanding Float3/D3DXVECTOR3
views, and names the complete sound and ANM resource-slot protocols plus the
evidence-supported portion of the Effect table and Player main VM scripts.
Bullet transform, rank-influence, and trail instructions use asserted wire
schemas with compile-time field indices while retaining the exact /Ob0
resolver expression tree. A generic float-view trial changed RunEcl from
0x6B06 to 0x6B05 and was rejected; the accepted bit-cast field view keeps
RunEcl 26,638 / 26,638 exact.
The required single-job non-reuse cold replay rebuilt all 75 comparison
objects and passed 1,106 / 1,106 exact with zero failures. The normal VC7
image links, the complete i386 Linux image rebuilds and links, and the fixed-
layout verifier passes. scripts/check-semantic-protocols.py, wired into CI,
prevents regression to numeric ECL cases, raw vector pointer casts, or fixed
numeric effect/sound/ANM resource IDs. The reusable source-shape rules are in
.agents/skills/th08-semantic/SKILL.md. This closes the protocol/readability
milestone; future reconstruction work begins from the latest main on a fresh
short-lived branch.