more work done

This commit is contained in:
MaddoScientisto 2026-04-09 00:32:12 +02:00
commit d323bb28fc
68 changed files with 714 additions and 19 deletions

View file

@ -3,6 +3,8 @@
<PROJECT_DATA_XML_NAME NAME="DISPLAY_DATA"> <PROJECT_DATA_XML_NAME NAME="DISPLAY_DATA">
<SAVE_STATE> <SAVE_STATE>
<ARRAY NAME="EXPANDED_PATHS" TYPE="string"> <ARRAY NAME="EXPANDED_PATHS" TYPE="string">
<A VALUE="Crusader:psx:" />
<A VALUE="Crusader:psx:remorse:" />
<A VALUE="Crusader:" /> <A VALUE="Crusader:" />
</ARRAY> </ARRAY>
<STATE NAME="SHOW_TABLE" TYPE="boolean" VALUE="false" /> <STATE NAME="SHOW_TABLE" TYPE="boolean" VALUE="false" />

View file

@ -279,10 +279,28 @@ That is no longer true after the next live pass on 2026-04-07.
- `+0x46/+0x48 = owned_buffer_a` - `+0x46/+0x48 = owned_buffer_a`
- `+0x4a/+0x4c = owned_buffer_b` - `+0x4a/+0x4c = owned_buffer_b`
- Those two runtime-state methods now carry provisional `EntityDispatchEntryRuntimeState * this` signatures and in-session comments tying them back to the older `000d:` evidence, which is enough to treat the runtime-state lane as class-authored rather than only documented. - Those two runtime-state methods now carry provisional `EntityDispatchEntryRuntimeState * this` signatures and in-session comments tying them back to the older `000d:` evidence, which is enough to treat the runtime-state lane as class-authored rather than only documented.
- The next derived-family step is now landed too for the periodic/timed branch in the live `11e0:` substrate segment. Six more methods are re-anchored from the older `0008:` note cluster by preserved offset delta from `0008:ba00 -> 11e0:0000` and now live under `Remorse::EntityDispatchEntry` with short provenance comments:
- `11e0:14fb Process_Create_0x36byte` -> `ConstructVtable3AD2` (older note anchor `0008:cefb`)
- `11e0:1814 Process_Init0x40ByteProc` -> `ConstructVtable3AA6` (older note anchor `0008:d214`)
- `11e0:187e Process_Set_MaybeTimesPerSecond` -> `SetUpdatePeriodAndReschedule` (older note anchor `0008:d27e`)
- `11e0:1913 FUN_11e0_1913` -> `TickPeriodic` (older note anchor `0008:d313`)
- `11e0:19e6 Process_11e0_19e6` -> `EnableActiveCounters` (older note anchor `0008:d3e6`)
- `11e0:1a33 Process_11e0_1a33` -> `DisableActiveCounters` (older note anchor `0008:d433`)
- The earlier word-list blocker is now closed too, but by re-anchoring rather than by `11e0:` boundary repair. The expected live `11e0:2000..25a1` window is not code in the current database; the actual word-list-owned subtype lives in the `11e8:` `MList_*` cluster, with the root at `11e8:0000` carrying explicit old `0008:da00` segment metadata in the decompiler. That full batch now also lives under `Remorse::EntityDispatchEntry` with short provenance comments:
- `11e8:0000 MList_11e8_0000` -> `SetWordList0408Terminated` (older note anchor `0008:da00`)
- `11e8:01a3 MList_11e8_01a3` -> `FreeWordList` (older note anchor `0008:dba3`)
- `11e8:01ec MList_11e8_01ec` -> `Destroy` (older note anchor `0008:dbec`)
- `11e8:0238 FUN_11e8_0238` -> `EnsureWordListContains` (older note anchor `0008:dc38`)
- `11e8:02ab MList_11e8_02ab` -> `AppendUniqueWord` (older note anchor `0008:dcab`)
- `11e8:03af MList_11e8_03af` -> `RemoveWordValue` (older note anchor `0008:ddaf`)
- `11e8:04ea MList_GetInt16` -> `GetWordAt` (older note anchor `0008:deea`)
- `11e8:051b MList_11e8_051b` -> `SetWordAt` (older note anchor `0008:df1b`)
- `11e8:05a1 FUN_11e8_05a1` -> `FindUnflaggedWordById10` (older note anchor `0008:dfa1`)
- That correction matters more than the names alone. The pilot family is no longer blocked on a missing word-list method surface; the remaining uncertainty is now about how explicitly the word-list-owned subtype should be split in datatypes and eventual C++ modeling, not about whether those methods exist in live `CRUSADER.EXE`.
## Questions To Close Later ## Questions To Close Later
- the remaining live `CRUSADER.EXE` method mapping for the note's old `0008:` / `000d:` anchors after the first `11e0:` base-method port and the `1440:` runtime-state port, especially `entity_word_list_destroy` and the timed/periodic constructors around old `0008:cefb` / `0008:d214` - whether the live `11e8:` word-list-owned subtype should stay modeled as a method batch under `EntityDispatchEntry` alone or be split further into an explicit derived/overlay class once a safe instance-size boundary is chosen
- whether `+0x00` should be modeled as a literal `kind` field in all variants or only in some factory-built subtypes - whether `+0x00` should be modeled as a literal `kind` field in all variants or only in some factory-built subtypes
- exact ownership split between the base object and the embedded surfaces at `+0x1e` and `+0x28` - exact ownership split between the base object and the embedded surfaces at `+0x1e` and `+0x28`
- whether the seg126 startup/display subtype is truly part of the same inheritance family or only shares a lower-level dispatch-entry substrate - whether the seg126 startup/display subtype is truly part of the same inheritance family or only shares a lower-level dispatch-entry substrate

View file

@ -72,17 +72,18 @@ Strong anchors:
Best current helper shape: Best current helper shape:
- compact file-backed helper object - compact `0x14`-byte file-backed wrapper object
- helper-owned count at `+0x14` - first `0x08` bytes are the embedded file-handle base initialized by `File_MallocOrInit8Bytes`
- far-pointer table at `+0x10` - helper vtable/dispatch pointer lives at `+0x08`
- paired 16-bit table at `+0x18` - materialized owner-row table far pointer lives at `+0x0c`
- helper vtable `+0x04` acts as size query - helper vtable `+0x04` acts as the current best size/query callback
- helper vtable `+0x0c` materializes the `0x0d`-stride owner rows later consumed by contexts - helper vtable `+0x0c` materializes owner data; the thin wrapper now asserts if the first output byte is `0xff`
Current safest interpretation: Current safest interpretation:
- this is the most bounded class-lift target in the VM lane - this is the most bounded class-lift target in the VM lane
- it looks like a real helper object with a compact stable layout and a clear owner relationship to `EntityVmRuntime` - it looks like a real helper object with a compact stable layout and a clear owner relationship to `EntityVmRuntime`
- the earlier `+0x14` / `+0x18` count-and-table claims belong to the downstream materialized loader data, not to the outer `0x14`-byte wrapper itself
### seg070 loader contract ### seg070 loader contract
@ -172,9 +173,9 @@ Verified first batch landed in the live `CRUSADER.EXE` session on 2026-04-05.
- Created namespace `Remorse` and class owners `Remorse::EntityVmOwnerResource`, `Remorse::EntityVmRuntime`, and `Remorse::EntityVmContext`. - Created namespace `Remorse` and class owners `Remorse::EntityVmOwnerResource`, `Remorse::EntityVmRuntime`, and `Remorse::EntityVmContext`.
- Created provisional datatype `/Remorse/EntityVmOwnerResource` with current stable anchors: - Created provisional datatype `/Remorse/EntityVmOwnerResource` with current stable anchors:
- `+0x10 owner_row_table` - first `0x08` bytes reserved for the embedded file base
- `+0x14 entry_count` - `+0x08 helper_vtable`
- `+0x18 entry_word_table` - `+0x0c owner_row_table`
- Created provisional datatype `/Remorse/EntityVmRuntime` with size `0x1319` and only the currently stable tail anchors around the owner-resource attachment lane. - Created provisional datatype `/Remorse/EntityVmRuntime` with size `0x1319` and only the currently stable tail anchors around the owner-resource attachment lane.
- Created provisional datatype `/Remorse/EntityVmSlotEntry` with size `0x26` and only the currently stable tail buffer fields named: - Created provisional datatype `/Remorse/EntityVmSlotEntry` with size `0x26` and only the currently stable tail buffer fields named:
- `+0x1e owner_buffer_offset` - `+0x1e owner_buffer_offset`
@ -296,6 +297,11 @@ Verified first batch landed in the live `CRUSADER.EXE` session on 2026-04-05.
- `1420:1601 Remorse::EntityVmRuntime::Destroy` -> `byte __cdecl16far Destroy(EntityVmRuntime * this, word destroy_flags)` - `1420:1601 Remorse::EntityVmRuntime::Destroy` -> `byte __cdecl16far Destroy(EntityVmRuntime * this, word destroy_flags)`
- `1420:10b6/10da/1162/118f/1278 Remorse::EntityVmContext::{FreeBuffer, SyncGlobalValueAndDispatch, Destroy, Save, Load}` now all carry explicit `EntityVmContext *32 this` - `1420:10b6/10da/1162/118f/1278 Remorse::EntityVmContext::{FreeBuffer, SyncGlobalValueAndDispatch, Destroy, Save, Load}` now all carry explicit `EntityVmContext *32 this`
- That leaves `CreateFromSlotIndex` as the one clearly still-complex VM signature in this family cluster: the body still shows a far `this`, but the remaining argument pack needs a dedicated caller-side recovery pass rather than another pointer-only rewrite. - That leaves `CreateFromSlotIndex` as the one clearly still-complex VM signature in this family cluster: the body still shows a far `this`, but the remaining argument pack needs a dedicated caller-side recovery pass rather than another pointer-only rewrite.
- Verified sixteenth live batch landed on 2026-04-08.
- Re-read `1430:0000 Remorse::EntityVmOwnerResource::Create` and `1430:00fd Destroy` against the live decompiler and corrected the outer-wrapper model: the object itself is only `0x14` bytes, with the first `0x08` bytes acting as the embedded file base, the helper vtable pointer at `+0x08`, and the materialized owner-row table far pointer at `+0x0c`.
- Moved `1430:014c` under `Remorse::EntityVmOwnerResource` as `MaterializeChecked` after confirming from `InitSlotOwnerBuffers` and `EnsureSlotChunkLoaded` that it is the thin wrapper over helper vtable slot `+0x0c` used to materialize owner data and assert on `0xff` sentinel failure.
- Moved `1430:0195` under `Remorse::EntityVmOwnerResource` as `QueryMaterializationSize` after confirming it is the adjacent thin wrapper over helper vtable slot `+0x04`, with the current safest read still being the same size/query callback already seen from the `Create` path.
- Added short decompiler comments at `1430:014c` and `1430:0195` so the callback-slot evidence remains visible in-session without pretending the full seg069/070 helper contract is closed.
- Verified fifteenth live batch landed on 2026-04-06. - Verified fifteenth live batch landed on 2026-04-06.
- Recovered the mixed caller pack on `1420:0eec Remorse::EntityVmContext::CreateFromSlotIndex` far enough to replace the old anonymous split arguments with caller-backed names: - Recovered the mixed caller pack on `1420:0eec Remorse::EntityVmContext::CreateFromSlotIndex` far enough to replace the old anonymous split arguments with caller-backed names:
- `dword owner_source_farptr` - `dword owner_source_farptr`
@ -317,6 +323,7 @@ Verified first batch landed in the live `CRUSADER.EXE` session on 2026-04-05.
Current live datatype state: Current live datatype state:
- `/Remorse/EntityVmOwnerResource` is the cleanest landed class in this lane so far. - `/Remorse/EntityVmOwnerResource` is the cleanest landed class in this lane so far.
- Its current stable outer-wrapper model is now tighter too: embedded file base at `+0x00..+0x07`, helper vtable at `+0x08`, and materialized owner-row table far pointer at `+0x0c`.
- `/Remorse/EntityVmRuntime` currently only freezes the stable tail fields and helper pointer, not the full slot-entry schema. - `/Remorse/EntityVmRuntime` currently only freezes the stable tail fields and helper pointer, not the full slot-entry schema.
- `/Remorse/EntityVmSlotEntry` now exists both as a bounded helper datatype and as a live `Remorse` class owner. Its current authored surface is intentionally small: one constructor/clear method plus the stable `match_key_farptr`, `owner_chunk_count`, `owner_data_base`, and owner-buffer / chunk-state pointer anchors. - `/Remorse/EntityVmSlotEntry` now exists both as a bounded helper datatype and as a live `Remorse` class owner. Its current authored surface is intentionally small: one constructor/clear method plus the stable `match_key_farptr`, `owner_chunk_count`, `owner_data_base`, and owner-buffer / chunk-state pointer anchors.
- `/Remorse/EntityVmLoadedChunkRecord` now exists as the shared cleanup/iteration record for the chunk-release and conditional-unload lane, with the currently stable next-link, saved-owner-buffer, slot-index, and chunk-index fields named. - `/Remorse/EntityVmLoadedChunkRecord` now exists as the shared cleanup/iteration record for the chunk-release and conditional-unload lane, with the currently stable next-link, saved-owner-buffer, slot-index, and chunk-index fields named.
@ -335,7 +342,7 @@ Current live datatype state:
Current scope of that batch stayed intentionally conservative: Current scope of that batch stayed intentionally conservative:
- no final source-format schema naming for the owner rows - no final source-format schema naming for the owner rows
- no speculative promotion of additional seg069/070 helper callbacks into owned methods yet - no speculative promotion of additional seg069/070 helper callbacks beyond `QueryMaterializationSize` and `MaterializeChecked`
- no speculative promotion of the masked-create wrapper ladder into `EntityVmContext` methods - no speculative promotion of the masked-create wrapper ladder into `EntityVmContext` methods
- no speculative typing yet for the entity-like pointer parameter on `AcquireSlotForEntity` - no speculative typing yet for the entity-like pointer parameter on `AcquireSlotForEntity`
- no attempt yet to force slot-entry field names beyond the stable `+0x1e..+0x24` tail region and the current conservative helper prototypes - no attempt yet to force slot-entry field names beyond the stable `+0x1e..+0x24` tail region and the current conservative helper prototypes

View file

@ -200,6 +200,26 @@ Why not first:
3. decide whether `0x45a6` is an owned buffer, fallback object, or adapter scratch lane 3. decide whether `0x45a6` is an owned buffer, fallback object, or adapter scratch lane
4. tighten the constructor/installation provenance in the live NE program, not only the raw notes 4. tighten the constructor/installation provenance in the live NE program, not only the raw notes
## Live Ghidra Authoring Status
Verified first live `PresentationCallbackBroker` batch landed on 2026-04-08.
- Created class owner `Remorse::PresentationCallbackBroker` in the active `CRUSADER.EXE` database.
- Re-anchored the global install/teardown pair into live `12d0:` helpers by direct access to the `0x4588/0x458c/0x4590/0x4594/0x4595/0x45a6` broker globals:
- `12d0:0513` -> `InitOnce`
- `12d0:0656` -> `TeardownOnce`
- Added short decompiler comments to both methods so the current `0x4588` lifecycle remains visible in-session:
- `InitOnce` now records the `0x4594` guard, state snapshot into `0x458c/0x4590`, broker install at `0x4588`, and fallback-buffer allocation at `0x45a6`.
- `TeardownOnce` now records the `0x4595` guard, broker clear, conditional slot `+0x0c` emit when `0x4590 != 0x458c`, slot `+0x04` release, and final cleanup path.
- Re-anchored the finalize-phase caller as well: `1288:0fc3` is now `allocator_phase_finalize_pass` in the live database with a comment recording that it exercises broker slot `+0x08` twice before sweeping allocator heads.
- Preserved two verified live slot `+0x0c` callers with decompiler comments instead of forcing premature renames:
- `1278:0616 Vport_1278_0616` uses the installed broker callback when the local vport path takes its fallback branch with `param_2 == 0`.
- `1320:1588 Dispatch_ModalGump` emits the broker callback before and after modal dispatch when the requested state pair differs from the current mode snapshot.
- This remains intentionally conservative live authoring:
- no forced `this` typing yet for the broker pointer parameter on `InitOnce`
- no live promotion yet of `allocator_phase_finalize_pass` under the class owner itself, because it is a broker caller rather than a broker method
- no attempt yet to rename the wider subsystem beyond the existing `PresentationCallbackBroker` placeholder
## Bottom Line ## Bottom Line
The `0x4588` family is now documented well enough to be treated as a real object candidate. The `0x4588` family is now documented well enough to be treated as a real object candidate.

View file

@ -60,6 +60,58 @@ If the goal is to make later class-authoring work fast and low-risk, the best or
This order prioritizes bounded families with visible constructors, derived variants, or explicit method tables before the larger gameplay and VM surfaces. This order prioritizes bounded families with visible constructors, derived variants, or explicit method tables before the larger gameplay and VM surfaces.
## Broad Sweep Priorities
The earlier class-lift lane focused on proving a few pilot families end to end. After the recent live authoring batches, the next useful broader sweep is smaller and more structured than the raw inventory table alone suggests.
### Tier 1: Ready Or Already Started
These families now have enough evidence or live foothold that they can be treated as the main near-term queue rather than as speculative future ideas.
1. `EntityVmOwnerResource`
- Why it is next-ready: bounded helper object, explicit create/destroy pair, stable callback slots at `+0x04` and `+0x0c`, and already-strong loader/table layout evidence.
- Why it broadens coverage well: it adds a non-UI, non-dispatch, non-debugger object family with a compact file-backed contract.
2. `CacheBackendObject`
- Why it stays in Tier 1: it is still a compact object with a concrete `0x20` size and explicit method-table state.
- Current live status: the constructor shell is now started in `CRUSADER.EXE` as `Remorse::CacheBackendObject::Create` at live `1250:0000`, backed by the decompiler's explicit old `0009:5600` segment metadata.
3. `SpriteNode`
- Why it stays active even in a broader sweep: its core live surface is now materially landed, but the constructor-wrapper split, deeper slot semantics, and subtype boundary questions remain open.
- Current live status: class owner plus first method batch and first datatypes already exist in-session, so future broader work can deepen it selectively instead of rediscovering it.
### Tier 2: Good Broader Identification Targets
These families are strong enough to be promoted intentionally in a wider identification pass, but they still benefit from one more bounded evidence sweep before heavy datatype work.
1. `WatchEntityController`
- Why it stands out: global object ownership is clear, create-global and direct create paths are already named, and vtable dispatch at `+0x24`, `+0x2c`, and `+0x30` is explicit.
- Main current caution: the wider subsystem label is still weaker than the local object mechanics.
2. `DialogMenuObject`
- Why it stands out: small UI/dialog object with one stable vtable (`0x28b5`) and multiple event-notify wrappers that already behave like owned methods.
- Main current caution: destructor and richer layout evidence still trail the create/event surface.
3. `PresentationCallbackBroker`
- Why it still belongs in the broader class map: object lifecycle and vtable-slot surface are real, and it gives the broader sweep one presentation-side callback family.
- Main current caution: subsystem naming is intentionally conservative and should probably remain that way until installation provenance tightens further.
### Tier 3: High-Value But Bigger Families
These are absolutely class-worthy, but they are better treated as longer arcs than as quick additions to a broad identification pass.
1. `Entity`
2. `EntityVmRuntime`
3. `EntityVmContext`
The common pattern here is that they are already clearly object-shaped, but each carries enough subtype, caller, or schema breadth that broad identification alone is not the main problem anymore.
## First Pilot Candidates ## First Pilot Candidates
### Best pilot: `EntityDispatchEntryBase` ### Best pilot: `EntityDispatchEntryBase`

View file

@ -1,3 +1,19 @@
That `aux_farptr` lane also survived one more direct caller pass without widening. `CallstackPushFrame` still has only the retail `1418:051d Interpreter_NextUsecodeOp` caller, that caller still pushes literal zero for the trailing field, and the current seg109 consumers still read only `+0x09` and `+0x0d`, so the field remains intentionally neutral rather than weakly renamed.
That first `SpriteNode` batch now includes datatype authoring too. `/Remorse/SpriteNodeBase` and `/Remorse/SpriteNodeVtable` both exist live with the current safest base offsets and event-slot anchors, so the family is no longer just a method-owner shell.
The constructor side has now started too. `1360:036a` lives as `Remorse::SpriteNode::Create` with an explicit comment that preserves the remaining caveat: this is the current safest constructor-style anchor because it allocates `0x34` bytes, stamps the `0x501a` vtable, and initializes the core node lanes, but it may still prove to be a higher derived/UI wrapper once more subtype evidence lands. The main remaining `SpriteNode` gap is therefore the live `GetOrTraverse` anchor, not whether the family has any constructor surface at all.
That traversal gap is now closed too. `1360:0955` is live as `Remorse::SpriteNode::GetOrTraverse`, with a comment recording that it recurses through the child-linked subtree, adjusts query coordinates by the local offsets, and returns either the matched child node or the default sentinel through the out pointer. The remaining `SpriteNode` questions are now the constructor-wrapper split and the deeper slot/layout story rather than missing core method anchors.
The next bounded family after `SpriteNode` is started too. `Remorse::CacheBackendObject` now exists live with `1250:0000` promoted as `Create`, backed by the decompiler's explicit old `0009:5600` segment metadata and a comment recording the `0x20`-byte object allocation plus file-handle/method-table initialization path. That family is still only at its constructor shell, but it is no longer inventory-only.
That broader Tier 1 sweep is now complete too. `EntityVmOwnerResource` is no longer just a create/destroy shell: the outer wrapper model is corrected to a `0x14`-byte file-backed object with helper vtable at `+0x08` and materialized owner-row table at `+0x0c`, and the two adjacent `1430:` wrappers now live under the class as `QueryMaterializationSize` and `MaterializeChecked`. `CacheBackendObject` also moved beyond its constructor shell: `1250:026c` is now `LoadEntryTableFromManifest`, `1250:0749` is now `InitFixedEntryTable`, and the live decompiler now supports a tighter `0x20`-byte layout read around the `+0x10/+0x14/+0x16/+0x18/+0x1c` entry-table lanes. `SpriteNode` slot work tightened as well: `DispatchEvent` now maps event codes `1/2/4/8/0x10/0x20/0x40/0x100` onto concrete vtable offsets instead of the earlier generic `A/B/C/D` placeholders.
The next broader shortlist is now partially started as well. `PresentationCallbackBroker` has its first live foothold in `CRUSADER.EXE`: `12d0:0513` and `12d0:0656` now live under `Remorse::PresentationCallbackBroker::{InitOnce,TeardownOnce}` with comments tied to the `0x4588` lifecycle globals. The same pass also clarified what is *not* ready yet: first-pass live searches for `WatchEntityController` (`0x2c2b`, `0x2be4`, `0x39ca`, `0x0219`) and `DialogMenuObject` (`0x28b5`, `0x27ca`, `0x2843`) hit camera/process and controller-save false positives rather than safe reanchors, so those two families remain documented candidates but not yet live-authored classes.
That next-pass follow-up is now tighter too. The raw `WatchEntityController` lane no longer needs to stay completely abstract: its `0007:ba00/ba45` create pair maps onto the live `Camera_Init` / `Camera_CreateProcess` cluster at `1180:0000/0045`, and those functions now carry provenance comments instead of forcing a weaker duplicate rename over the clearer camera-process naming. `DialogMenuObject` remains the one compact family in this batch that still lacks a safe live re-anchor: second-pass searches on the obvious `0x28b5/0x27ca/0x2843` leads still landed on unrelated process/save helpers, so that family stays note-backed but not yet live-authored. `PresentationCallbackBroker` also has its first non-method live adjunct now: `1288:0fc3` is renamed `allocator_phase_finalize_pass` with a comment recording the two broker slot `+0x08` calls before allocator-head sweep, and two slot `+0x0c` caller sites (`1278:0616`, `1320:1588`) now carry caller-evidence comments in-session. `CacheBackendObject` advanced another bounded step as well: `1250:0910` now lives under the class as `SetEntryNameAndTag`, and the latest `SpriteNode` caller pass supports treating `Create` as the compact shared node constructor used by the larger `GumpCreate_*` wrapper family.
# Remorse Class-Lift Work Index # Remorse Class-Lift Work Index
## Purpose ## Purpose
@ -88,6 +104,25 @@ The future MCP endpoint sequence should follow the spec note rather than ad hoc
3. Add one more dedicated note for the callback/object lane around `0x4588` only if later caller evidence supports a stronger subsystem name than `PresentationCallbackBroker`. 3. Add one more dedicated note for the callback/object lane around `0x4588` only if later caller evidence supports a stronger subsystem name than `PresentationCallbackBroker`.
4. Turn the first-class-authoring checklist into a completed execution log once the first real MCP batch lands. 4. Turn the first-class-authoring checklist into a completed execution log once the first real MCP batch lands.
## Broader Sweep Shortlist
The broader class-identification pass is now narrow enough to be explicit.
Current best near-term shortlist after the recent live authoring work:
1. `WatchEntityController`
2. `DialogMenuObject`
3. `PresentationCallbackBroker`
4. deeper `CacheBackendObject` follow-up around `1250:0910`
5. `SpriteNode` subtype/layout follow-up beyond the recovered event-slot map
6. broader `Entity` family partitioning
That ordering is deliberate:
- the old Tier 1 set is now complete, and the next pass already resolved part of the follow-up list
- `PresentationCallbackBroker` still belongs on the map, but its subsystem label remains weaker than its mechanics even after the first live reanchor
- the last two items stay in view because they now have live footholds and cleaner remaining follow-up than they did before
## Current Live Authoring Snapshot ## Current Live Authoring Snapshot
The live `CRUSADER.EXE` class-authoring lane is no longer just a plan. The live `CRUSADER.EXE` class-authoring lane is no longer just a plan.
@ -106,6 +141,12 @@ The next planned pilot family is no longer purely preparatory either. `Remorse::
That family also has its first derived slice now. The old `000d:7e00/8078` runtime-state pair is re-anchored in the live `1440:` fade/palette cluster as `InitRuntimeState` and `ReleaseRuntimeState`, and `/Remorse/EntityDispatchEntryRuntimeState` now exists as a provisional overlay datatype with the recovered `+0x40..+0x4c` runtime-state tail fields. That is a meaningful pause point because the pilot family now has a class owner, a base datatype, a vtable shell, a first base-method batch, and one concrete derived/runtime-state batch rather than just one isolated constructor lane. That family also has its first derived slice now. The old `000d:7e00/8078` runtime-state pair is re-anchored in the live `1440:` fade/palette cluster as `InitRuntimeState` and `ReleaseRuntimeState`, and `/Remorse/EntityDispatchEntryRuntimeState` now exists as a provisional overlay datatype with the recovered `+0x40..+0x4c` runtime-state tail fields. That is a meaningful pause point because the pilot family now has a class owner, a base datatype, a vtable shell, a first base-method batch, and one concrete derived/runtime-state batch rather than just one isolated constructor lane.
That pause point has moved again. The timed/periodic derived branch is now partially lifted in-session too: `ConstructVtable3AD2`, `ConstructVtable3AA6`, `SetUpdatePeriodAndReschedule`, `TickPeriodic`, `EnableActiveCounters`, and `DisableActiveCounters` are now owned by `Remorse::EntityDispatchEntry` in the live `11e0:` substrate segment with short `0008:` provenance comments preserved.
The earlier word-list blocker on that same family is now closed by a re-anchor correction rather than by local boundary repair. The old `0008:da00..dfa1` word-list lane does not live in the dead `11e0:2000..25a1` window after all; it reappears as the live `11e8:` `MList_*` cluster, and that full batch is now also owned under `Remorse::EntityDispatchEntry`: `SetWordList0408Terminated`, `FreeWordList`, `Destroy`, `EnsureWordListContains`, `AppendUniqueWord`, `RemoveWordValue`, `GetWordAt`, `SetWordAt`, and `FindUnflaggedWordById10`. The main remaining `EntityDispatchEntry` question is therefore no longer "where did the word-list methods go," but whether that `11e8:` subtype should eventually become its own explicit derived/overlay class in the datatype model.
The next family switch has now started for real too. `Remorse::SpriteNode` exists live in `CRUSADER.EXE`, and its first bounded `1360:` batch is no longer just a note: `Destroy`, `IsDirty`, `MarkDirty`, `DispatchEvent`, and `UpdateAndDispatch` are now class-owned with short `000b:` provenance comments. That shifts the `SpriteNode` family from note-only planning into the same live-authoring lane as the earlier Remorse pilots, while still keeping the constructor side and `GetOrTraverse` mapping deliberately open.
The latest signature-recovery pass also tightened two of those runtime methods materially: The latest signature-recovery pass also tightened two of those runtime methods materially:
- `GetSlotChunkPtrAtOffset(runtime_farptr, slot_index, chunk_index, intra_chunk_offset)` now reads as a real slot-chunk accessor instead of a five-word anonymous wrapper. - `GetSlotChunkPtrAtOffset(runtime_farptr, slot_index, chunk_index, intra_chunk_offset)` now reads as a real slot-chunk accessor instead of a five-word anonymous wrapper.
@ -123,6 +164,8 @@ The next debugger pass tightened the bounded helper and callback edges too. `140
The follow-up seg109 consumer pass is also done now. `13a0:0291` and its local helper `13a0:045c` are documented in-session as the first concrete consumers of the current callstack entry's trailing payload: they read entry `+0x09` as a descriptor/source-stream cursor and entry `+0x0d` as the current-frame payload context while formatting debugger dump/watch text. That cursor name is now promoted into the live `/Remorse/UsecodeDebuggerCallstackEntry` datatype and the `CallstackPushFrame` signature too, which means the remaining open callstack question is mostly the unused `aux_farptr` lane rather than the first two dwords. The follow-up seg109 consumer pass is also done now. `13a0:0291` and its local helper `13a0:045c` are documented in-session as the first concrete consumers of the current callstack entry's trailing payload: they read entry `+0x09` as a descriptor/source-stream cursor and entry `+0x0d` as the current-frame payload context while formatting debugger dump/watch text. That cursor name is now promoted into the live `/Remorse/UsecodeDebuggerCallstackEntry` datatype and the `CallstackPushFrame` signature too, which means the remaining open callstack question is mostly the unused `aux_farptr` lane rather than the first two dwords.
That formatter consumer lane is no longer just comment-backed either. The two seg109 helpers now have stable live names in `CRUSADER.EXE`: `13a0:0291` is `usecode_debugger_format_expression_to_shared_buffer`, and `13a0:045c` is `usecode_debugger_format_descriptor_expression`. That keeps the debugger-family residue focused where it belongs now: mainly the still-neutral `aux_farptr` lane and any later evidence for non-retail callback behavior, not anonymous formatter plumbing.
The VM lane also advanced one more selective step without overpromoting inheritance: `Remorse::EntityVmContext::CreateFromSlotIndex` now has a caller-backed mixed parameter pack (`owner_source_farptr`, `pitemno_farptr`, `mode_flags`, `slot_index`, `value_add_offset`, `intra_chunk_offset`, `ucparam_farptr`, `ucparamsize`) and an explicit far return restored in `AX:DX`, even though the current live endpoint still textualizes that repaired signature conservatively as plain `dword __cdecl`. The VM lane also advanced one more selective step without overpromoting inheritance: `Remorse::EntityVmContext::CreateFromSlotIndex` now has a caller-backed mixed parameter pack (`owner_source_farptr`, `pitemno_farptr`, `mode_flags`, `slot_index`, `value_add_offset`, `intra_chunk_offset`, `ucparam_farptr`, `ucparamsize`) and an explicit far return restored in `AX:DX`, even though the current live endpoint still textualizes that repaired signature conservatively as plain `dword __cdecl`.
## Bottom Line ## Bottom Line

View file

@ -109,14 +109,19 @@ So the safe future modeling strategy is:
## Candidate Virtual Slot Map ## Candidate Virtual Slot Map
The currently verified slots are already good enough for a first typed vtable. `DispatchEvent` now gives a materially tighter slot map than the earlier placeholder `A/B/C/D` read.
| Slot offset | Current best role | Evidence | | Slot offset | Current best role | Evidence |
|---|---|---| |---|---|---|
| `+0x14` | event handler A | `sprite_node_dispatch_event` dispatches here for one event class | | `+0x04` | event `1` handler | `DispatchEvent` routes event code `1` here directly |
| `+0x18` | event handler B | same dispatcher | | `+0x08` | event `2` handler | same dispatcher |
| `+0x20` | event handler C | same dispatcher | | `+0x0c` | event `4` handler | same dispatcher |
| `+0x24` | event handler D | same dispatcher | | `+0x10` | event `8` handler | same dispatcher |
| `+0x14` | event `0x10` handler | same dispatcher |
| `+0x18` | event `0x20` handler | same dispatcher |
| `+0x1c` | event `0x40` self handler | called after the dispatcher walks child nodes for the same event |
| `+0x24` | event `0x100` handler | same dispatcher |
| `+0x34` | child-broadcast event `0x40` slot | used on child nodes during the `0x40` walk, not on the root dispatch object itself |
The seg091 default-slot helpers are also useful evidence: The seg091 default-slot helpers are also useful evidence:
@ -153,11 +158,45 @@ When class authoring begins, the safest sequence for this family is:
4. create provisional vtable with slots `+0x14`, `+0x18`, `+0x20`, `+0x24` 4. create provisional vtable with slots `+0x14`, `+0x18`, `+0x20`, `+0x24`
5. keep recursive tree helpers outside the class until decompiler output shows they benefit from becoming methods 5. keep recursive tree helpers outside the class until decompiler output shows they benefit from becoming methods
## Live Ghidra Authoring Status
Verified first live `SpriteNode` batch landed on 2026-04-08.
- Created class owner `Remorse::SpriteNode` in the active `CRUSADER.EXE` database.
- Re-anchored the strongest old `000b:` method batch into the live `1360:` segment by preserved offset delta from `000b:326e -> 1360:046e`.
- Created minimal live datatypes `/Remorse/SpriteNodeBase` and `/Remorse/SpriteNodeVtable` from the current safest note anchors.
- `/Remorse/SpriteNodeBase` currently names only the bounded field block that is directly supported by the live method batch:
- `+0x19 = child_or_next_farptr`
- `+0x21 = local_x_offset`
- `+0x23 = local_y_offset`
- `+0x29 = dirty_flags`
- `/Remorse/SpriteNodeVtable` is still the earlier minimal shell in-session, but the live `DispatchEvent` read now supports a deeper slot map than the first authoring batch encoded:
- direct self dispatch uses `+0x04`, `+0x08`, `+0x0c`, `+0x10`, `+0x14`, `+0x18`, `+0x1c`, and `+0x24`
- child broadcast during event `0x40` uses child slot `+0x34`
- Moved the first bounded method set under the class owner with short provenance comments:
- `1360:036a` -> `Create` (best current constructor-style anchor; still keep the higher-wrapper caveat visible)
- `1360:046e` -> `Destroy` (older note anchor `000b:326e`)
- `1360:0580` -> `IsDirty` (older note anchor `000b:3380`)
- `1360:05a6` -> `MarkDirty` (older note anchor `000b:33a6`)
- `1360:0955` -> `GetOrTraverse` (best current live anchor for older note anchor `000a:b988`)
- `1360:0cb2` -> `DispatchEvent` (older note anchor `000b:3ab2`)
- `1360:12ee` -> `UpdateAndDispatch` (older note anchor `000b:40ee`)
- The live decompiler now makes the core family surface much easier to navigate directly in-session:
- `Create` now exists live as the current safest constructor-style anchor: it allocates `0x34` bytes when `this` is null, stamps the `0x501a` SpriteNode vtable, initializes the child-link/core offset fields, and links the incoming parent/child lane.
- Direct callers now narrow the subtype story materially: the current call set is dominated by `GumpCreate_*` and adjacent UI wrapper constructors, which supports treating `Create` as the compact shared base-node constructor used by higher-level gump/display objects rather than as a one-off derived leaf.
- `Destroy` restores the `0x501a` base vtable, clears the global focus pointer when `this` owns it, releases child linkage, and optionally frees self.
- `IsDirty` and `MarkDirty` read and mutate the `+0x29` dirty-state lane exactly as the note predicted.
- `GetOrTraverse` now exists live as the best current `000a:b988` re-anchor: it recursively walks the child-linked subtree, adjusts the incoming query coordinates by the local offsets, and returns either the matched child node or the default sentinel through the out pointer.
- `DispatchEvent` updates the global focus pointer at `0x4fd0:0x4fd2` and now ties concrete event codes to concrete slot offsets: `1/2/4/8 -> +0x04/+0x08/+0x0c/+0x10`, `0x10/0x20 -> +0x14/+0x18`, `0x40 -> child +0x34 then optional self +0x1c`, and `0x100 -> +0x24`.
- `UpdateAndDispatch` now clearly shows the `IsDirty` / `MarkDirty` / recompute / child-walk sequence instead of staying as an anonymous seg1360 helper.
- The `SpriteNode` family is therefore no longer note-only. What remains open is narrower now: chiefly whether `Create` should remain the family's public constructor-style entry or later be split into a higher derived/UI wrapper and a smaller base-node constructor once more callers and subtype evidence land, plus the deeper vtable-slot and subtype-layout questions.
## Open Questions ## Open Questions
- whether the current `Create` signature itself can be cleaned up further now that its caller set points to a shared compact base-node constructor used by higher-level gump wrappers
- exact root vtable address or addresses for the main SpriteNode family - exact root vtable address or addresses for the main SpriteNode family
- whether the `+0x17e` redraw flag belongs to a derived display node rather than the compact base node - whether the `+0x17e` redraw flag belongs to a derived display node rather than the compact base node
- which event-code cases map to which slot semantically beyond the current `A/B/C/D` placeholder naming - whether the recovered event-code map can now be promoted from raw event-code labels to prettier semantic slot names without overfitting UI behavior too early
- whether `sprite_tree_accumulate_pos` should become a class method, a static helper, or a separate geometry utility - whether `sprite_tree_accumulate_pos` should become a class method, a static helper, or a separate geometry utility
## Immediate Follow-Up Value ## Immediate Follow-Up Value

View file

@ -204,11 +204,16 @@ Verified first live class batch landed on 2026-04-06.
- `13a0:045c FUN_13a0_045c` now also carries a decompiler comment recording the same split from the consumer side: it reads descriptor bytes and inline strings directly from the `+0x09` lane and dereferences the `+0x0d` lane as the evaluation context while formatting debugger dump/watch text into the shared output buffer at `0x45a6`. - `13a0:045c FUN_13a0_045c` now also carries a decompiler comment recording the same split from the consumer side: it reads descriptor bytes and inline strings directly from the `+0x09` lane and dereferences the `+0x0d` lane as the evaluation context while formatting debugger dump/watch text into the shared output buffer at `0x45a6`.
- The `CallstackPushFrame` comment in seg1408 was updated to reflect that narrower live read: `+0x09` is no longer just a generic source-derived far pointer, but a real source-stream cursor used by the seg109 formatter path. - The `CallstackPushFrame` comment in seg1408 was updated to reflect that narrower live read: `+0x09` is no longer just a generic source-derived far pointer, but a real source-stream cursor used by the seg109 formatter path.
- That naming decision is now applied live too. `/Remorse/UsecodeDebuggerCallstackEntry` now exposes `source_stream_cursor_farptr` at offset `+0x09` with a matching field comment in the datatype manager, and `CallstackPushFrame` now uses `source_stream_cursor_farptr` in its parameter list instead of the older `source_stream_target_farptr` wording. - That naming decision is now applied live too. `/Remorse/UsecodeDebuggerCallstackEntry` now exposes `source_stream_cursor_farptr` at offset `+0x09` with a matching field comment in the datatype manager, and `CallstackPushFrame` now uses `source_stream_cursor_farptr` in its parameter list instead of the older `source_stream_target_farptr` wording.
- The seg109 consumer helpers are now named live as stable free functions rather than left comment-only:
- `13a0:0291` -> `usecode_debugger_format_expression_to_shared_buffer`
- `13a0:045c` -> `usecode_debugger_format_descriptor_expression`
- Those names are still intentionally conservative. They record the verified behavior now visible in both producer-side and consumer-side comments: `13a0:0291` resolves the current debugger callstack entry and writes the formatted result into the shared output buffer, while `13a0:045c` interprets the descriptor/source-stream cursor plus frame payload context rather than merely dumping raw bytes.
- Added decompiler comments on the breakpoint and callstack helpers so the recovered inline-record layout is visible in-session even before every field is formally typed. - Added decompiler comments on the breakpoint and callstack helpers so the recovered inline-record layout is visible in-session even before every field is formally typed.
- Added decompiler comments on the only verified `Interpreter_NextUsecodeOp` caller of `CallstackPushFrame`, which confirms the current live read of the three trailing callstack dwords: - Added decompiler comments on the only verified `Interpreter_NextUsecodeOp` caller of `CallstackPushFrame`, which confirms the current live read of the three trailing callstack dwords:
- `source_stream_target_farptr` is source-stream-derived from the interpreter `+0xd6/+0xd8` lane plus one fetched word - `source_stream_target_farptr` is source-stream-derived from the interpreter `+0xd6/+0xd8` lane plus one fetched word
- `current_frame_payload_farptr` is current-frame-derived from the `frame_base + 0x04` lane - `current_frame_payload_farptr` is current-frame-derived from the `frame_base + 0x04` lane
- `aux_farptr` is still zero in the only verified caller - `aux_farptr` is still zero in the only verified caller
- A second caller pass on 2026-04-08 still did not widen that lane: `get_callers(1408:02f5)` still reports only `1418:051d Interpreter_NextUsecodeOp`, and the live `CallstackPushFrame` comment now records that the trailing `aux_farptr` slot remains literal zero in retail while the current seg109 consumers still only read `+0x09` and `+0x0d`.
## Current Cautions ## Current Cautions
@ -221,7 +226,7 @@ Verified first live class batch landed on 2026-04-06.
1. Cross-check the seg1408 class note against the interpreter callback site at `1418:04b5` so the dormant-orphan lifecycle remains explicit in the live notes now that slot 0 is confirmed to land on an inert retail stub. 1. Cross-check the seg1408 class note against the interpreter callback site at `1418:04b5` so the dormant-orphan lifecycle remains explicit in the live notes now that slot 0 is confirmed to land on an inert retail stub.
2. Decide whether `aux_farptr` should remain neutral or can be promoted after one more caller or consumer pass. 2. Decide whether `aux_farptr` should remain neutral or can be promoted after one more caller or consumer pass.
3. Decide whether `13a0:0291` and `13a0:045c` are ready for stable debugger-UI names or should remain comment-backed until another direct caller or string anchor lands. 3. If the debugger family is revisited again, prioritize non-retail callback behavior or instantiation evidence before spending more time on the already-stable retail formatter lane.
4. If the debugger family stalls there, switch to the next planned class-lift family instead of overworking this orphaned subsystem. 4. If the debugger family stalls there, switch to the next planned class-lift family instead of overworking this orphaned subsystem.
## Bottom Line ## Bottom Line

View file

@ -85,9 +85,21 @@ Latest verified batch: [docs/psx/psx.md](docs/psx/psx.md), [docs/psx/map-renderi
- The latest debugger class-lift pass closed two more bounded gaps without overpromoting semantics: `1408:0230` now lives under `Remorse::UsecodeDebuggerBreakState::BreakpointFindFirstForUnitAtOrAfterLine` as the breakpoint-table lower-bound helper for `(unit_name, line_number)` queries, and the retail vtable root at `1478:65ab` is now resolved enough to show that `MaybeBreakOnCurrentLine` dispatches slot 0 into a shipped no-op stub while slot 1 currently returns zero through a second inert method. - The latest debugger class-lift pass closed two more bounded gaps without overpromoting semantics: `1408:0230` now lives under `Remorse::UsecodeDebuggerBreakState::BreakpointFindFirstForUnitAtOrAfterLine` as the breakpoint-table lower-bound helper for `(unit_name, line_number)` queries, and the retail vtable root at `1478:65ab` is now resolved enough to show that `MaybeBreakOnCurrentLine` dispatches slot 0 into a shipped no-op stub while slot 1 currently returns zero through a second inert method.
- The next debugger follow-up also closed the planned seg109 consumer pass: `13a0:0291` plus its helper `13a0:045c` now show that the current callstack entry's `+0x09` lane is a real source-stream cursor consumed byte-by-byte by the debugger formatter and that `+0x0d` is the paired current-frame payload context used for expression/watch rendering. The remaining open tail-field question is now mostly `aux_farptr`, not the first two dwords. - The next debugger follow-up also closed the planned seg109 consumer pass: `13a0:0291` plus its helper `13a0:045c` now show that the current callstack entry's `+0x09` lane is a real source-stream cursor consumed byte-by-byte by the debugger formatter and that `+0x0d` is the paired current-frame payload context used for expression/watch rendering. The remaining open tail-field question is now mostly `aux_farptr`, not the first two dwords.
- That naming decision is now landed live rather than only in notes: `/Remorse/UsecodeDebuggerCallstackEntry` now names offset `+0x09` as `source_stream_cursor_farptr` with an in-session field comment, and `CallstackPushFrame` now carries the same parameter name in its signature. The debugger-family residue is therefore narrower again: mainly `aux_farptr`, plus whether the seg109 formatter helpers deserve stable names. - That naming decision is now landed live rather than only in notes: `/Remorse/UsecodeDebuggerCallstackEntry` now names offset `+0x09` as `source_stream_cursor_farptr` with an in-session field comment, and `CallstackPushFrame` now carries the same parameter name in its signature. The debugger-family residue is therefore narrower again: mainly `aux_farptr`, plus whether the seg109 formatter helpers deserve stable names.
- That last formatter-helper hesitation is now closed too. The seg109 consumer pair is no longer anonymous in-session: `13a0:0291` now lives as `usecode_debugger_format_expression_to_shared_buffer`, and `13a0:045c` now lives as `usecode_debugger_format_descriptor_expression`. The debugger-family residue is therefore narrower again: mainly `aux_farptr`, plus any future evidence that the retail-stub callback slots ever had non-retail behavior.
- The follow-up retail caller pass did not widen `aux_farptr` either. `get_callers(1408:02f5)` still reports only `1418:051d Interpreter_NextUsecodeOp`, that caller still pushes literal zero for the trailing field, and the current seg109 formatter consumers still read only `+0x09` and `+0x0d`. For now the right live result is to keep `aux_farptr` intentionally neutral rather than invent a prettier but weak name.
- The next bounded class-family step landed too. `Remorse::SpriteNode` now exists live in `CRUSADER.EXE`, and the first strong `000b:` batch is re-anchored into live `1360:` by preserved offset delta from `000b:326e -> 1360:046e`: `Destroy` (`1360:046e`), `IsDirty` (`1360:0580`), `MarkDirty` (`1360:05a6`), `DispatchEvent` (`1360:0cb2`), and `UpdateAndDispatch` (`1360:12ee`) are now class-owned with in-session provenance comments. The remaining `SpriteNode` work is narrower and safer than before: mainly the constructor side, the exact live anchor for `GetOrTraverse`, and later vtable/datatype authoring rather than basic family existence.
- That same `SpriteNode` pass also moved beyond method ownership into datatype work: `/Remorse/SpriteNodeBase` now names `child_or_next_farptr`, `local_x_offset`, `local_y_offset`, and `dirty_flags`, and `/Remorse/SpriteNodeVtable` now exists as a provisional slot shell exposing `+0x14`, `+0x18`, `+0x20`, and `+0x24`.
- The constructor side is now started too: `1360:036a` lives as `Remorse::SpriteNode::Create` with an in-session caveat comment that preserves the remaining wrapper uncertainty. The live search for the old `000a:b988 GetOrTraverse` anchor is still open, but the family no longer lacks a constructor-style entry outright.
- That remaining traversal gap is now closed too. `1360:0955` now lives as `Remorse::SpriteNode::GetOrTraverse`, and the decompiler comment records the currently safest read of the helper: recurse over child-linked nodes, adjust the incoming query coordinates by the local offsets, and return either the matched node or the default sentinel through the out pointer. The main `SpriteNode` residue is therefore structural again: constructor-wrapper split, deeper slot naming, and subtype layout boundaries.
- The next bounded-family start is now landed too. `Remorse::CacheBackendObject` exists live with `1250:0000` promoted as `Create`; the decompiler itself carries explicit old `0009:5600` segment metadata on that body, and the current comment records the `0x20`-byte allocation plus file-handle/method-table initialization path. That family is still only at its constructor shell, but it is now a live class-lift lane instead of a pure inventory entry.
- The broader Tier 1 Remorse class sweep is now closed too. `EntityVmOwnerResource` gained two real accessor wrappers in-session (`QueryMaterializationSize` and `MaterializeChecked`) plus a corrected outer-wrapper layout (`0x14` bytes total, embedded file base at `+0x00..+0x07`, helper vtable at `+0x08`, owner-row table at `+0x0c`); `CacheBackendObject` gained the first two non-constructor class methods (`LoadEntryTableFromManifest` and `InitFixedEntryTable`) plus a tighter live layout read around `+0x10/+0x14/+0x16/+0x18/+0x1c`; and `SpriteNode::DispatchEvent` now ties concrete event codes to concrete vtable slots instead of generic placeholder slot names.
- The next broader Remorse batch also has its first post-Tier-1 live foothold now. `PresentationCallbackBroker` is no longer note-only: `12d0:0513` and `12d0:0656` are now live as `Remorse::PresentationCallbackBroker::{InitOnce, TeardownOnce}` with comments tied directly to the `0x4588/0x458c/0x4590/0x4594/0x4595/0x45a6` lifecycle cluster. The same pass also clarified that `WatchEntityController` and `DialogMenuObject` still need a second re-anchor pass before any live authoring: first-pass searches on the obvious type/vtable/callback constants hit unrelated camera/process and controller-save functions rather than safe class-family matches.
- That second pass is now partly closed. The old `WatchEntityController` create lane maps onto the live `Camera_Init` / `Camera_CreateProcess` cluster at `1180:0000/0045`, so those functions now carry provenance comments instead of a weaker forced rename; `DialogMenuObject` still lacks a safe live re-anchor after a second search on the obvious `0x28b5/0x27ca/0x2843` leads; `PresentationCallbackBroker` now has its raw `0009:b1c3` finalize-phase caller re-anchored live as `allocator_phase_finalize_pass` plus two preserved live slot `+0x0c` callers at `1278:0616` and `1320:1588`; `CacheBackendObject` gained `SetEntryNameAndTag` at `1250:0910`; and the widened `SpriteNode::Create` caller map now shows that the `0x34` allocation path is the compact shared node constructor used by many `GumpCreate_*` wrappers.
- The next planned pilot family also started for real: `Remorse::EntityDispatchEntry` now exists in-session with provisional `/Remorse/EntityDispatchEntryBase` and `/Remorse/EntityDispatchEntryVtable` datatypes, so this family is no longer just a note cluster. The remaining blocker is now concrete rather than vague: the current source note still points at older `0008:` / `000d:` anchors that are not yet ported back onto the live `CRUSADER.EXE` method objects, so the first base-method ownership move has to wait on that mapping step instead of being guessed. - The next planned pilot family also started for real: `Remorse::EntityDispatchEntry` now exists in-session with provisional `/Remorse/EntityDispatchEntryBase` and `/Remorse/EntityDispatchEntryVtable` datatypes, so this family is no longer just a note cluster. The remaining blocker is now concrete rather than vague: the current source note still points at older `0008:` / `000d:` anchors that are not yet ported back onto the live `CRUSADER.EXE` method objects, so the first base-method ownership move has to wait on that mapping step instead of being guessed.
- That mapping step is now partially closed too. The older `0008:ba00` base cluster ports into live `11e0:` by offset, and the first base-method batch now lives under `Remorse::EntityDispatchEntry`: `InitBase`, `SetSourceType`, `SetEventTypeChecked`, `SetGroupId`, `Unlink`, and `IncrementGroupId`. The next blocker on this family is therefore narrower again: not whether the pilot can move methods at all, but which live segments carry the remaining word-list, timed/periodic, and runtime-state methods from the older `0008:` / `000d:` notes. - That mapping step is now partially closed too. The older `0008:ba00` base cluster ports into live `11e0:` by offset, and the first base-method batch now lives under `Remorse::EntityDispatchEntry`: `InitBase`, `SetSourceType`, `SetEventTypeChecked`, `SetGroupId`, `Unlink`, and `IncrementGroupId`. The next blocker on this family is therefore narrower again: not whether the pilot can move methods at all, but which live segments carry the remaining word-list, timed/periodic, and runtime-state methods from the older `0008:` / `000d:` notes.
- The runtime-state follow-up is now partially closed too. `FadeProcess_Create` is explicitly tagged by the decompiler as old `000d:7e00`, `FUN_1440_0278` matches the old `000d:8078` release path by both offset delta and behavior, and both now live under `Remorse::EntityDispatchEntry` as `InitRuntimeState` and `ReleaseRuntimeState` with a new `/Remorse/EntityDispatchEntryRuntimeState` overlay datatype. That leaves the remaining `EntityDispatchEntry` pilot work in a narrower end-of-day state: mainly the word-list destroy lane and the timed/periodic constructor cluster, not the core base or runtime-state surfaces. - The runtime-state follow-up is now partially closed too. `FadeProcess_Create` is explicitly tagged by the decompiler as old `000d:7e00`, `FUN_1440_0278` matches the old `000d:8078` release path by both offset delta and behavior, and both now live under `Remorse::EntityDispatchEntry` as `InitRuntimeState` and `ReleaseRuntimeState` with a new `/Remorse/EntityDispatchEntryRuntimeState` overlay datatype. That leaves the remaining `EntityDispatchEntry` pilot work in a narrower end-of-day state: mainly the word-list destroy lane and the timed/periodic constructor cluster, not the core base or runtime-state surfaces.
- That pilot moved one more bounded step in-session too. The periodic/timed branch from the old `0008:` note cluster is now re-anchored live onto `11e0:` well enough to move six more methods under `Remorse::EntityDispatchEntry`: `ConstructVtable3AD2` (`11e0:14fb`), `ConstructVtable3AA6` (`11e0:1814`), `SetUpdatePeriodAndReschedule` (`11e0:187e`), `TickPeriodic` (`11e0:1913`), `EnableActiveCounters` (`11e0:19e6`), and `DisableActiveCounters` (`11e0:1a33`). Each now has an in-session provenance comment tying it back to the old `0008:` anchor, so the remaining `EntityDispatchEntry` blocker is narrower again: the word-list-owned subtype still has no live function objects in the expected `11e0:2000..25a1` window, and a bounded boundary scan did not yet yield safe entries to promote.
- That remaining `EntityDispatchEntry` blocker is now closed by a re-anchor correction. The expected `11e0:2000..25a1` window is not code in the current live database; the old `0008:da00..dfa1` word-list-owned subtype actually lives in the `11e8:` `MList_*` cluster, with `11e8:0000` carrying explicit old `0008:da00` segment metadata in the decompiler. That full batch now also lives under `Remorse::EntityDispatchEntry`: `SetWordList0408Terminated`, `FreeWordList`, `Destroy`, `EnsureWordListContains`, `AppendUniqueWord`, `RemoveWordValue`, `GetWordAt`, `SetWordAt`, and `FindUnflaggedWordById10`, each with an in-session provenance comment. The remaining question on this pilot family is therefore modeling depth rather than location: whether the `11e8:` word-list branch deserves its own explicit derived/overlay datatype instead of remaining a method cluster under the shared class owner.
- `CreateFromSlotIndex` is no longer a raw anonymous pack either: the live signature now separates `owner_source_farptr`, `pitemno_farptr`, `mode_flags`, `slot_index`, `value_add_offset`, `intra_chunk_offset`, `ucparam_farptr`, and `ucparamsize`, with explicit `AX:DX` return storage restored even though the endpoint still textualizes the function conservatively as plain `dword __cdecl`. - `CreateFromSlotIndex` is no longer a raw anonymous pack either: the live signature now separates `owner_source_farptr`, `pitemno_farptr`, `mode_flags`, `slot_index`, `value_add_offset`, `intra_chunk_offset`, `ucparam_farptr`, and `ucparamsize`, with explicit `AX:DX` return storage restored even though the endpoint still textualizes the function conservatively as plain `dword __cdecl`.
### Areas That Are No Longer Live Priorities ### Areas That Are No Longer Live Priorities
@ -124,7 +136,7 @@ Latest verified batch: [docs/psx/psx.md](docs/psx/psx.md), [docs/psx/map-renderi
5. Tighten the seg006 masked-helper caller chains so the local state-selector/value family can be tied to concrete gameplay subsystems. 5. Tighten the seg006 masked-helper caller chains so the local state-selector/value family can be tied to concrete gameplay subsystems.
6. Classify the paired seg070 loops behind `entity_vm_runtime_owner_resource_create`, especially which temporary buffers and record schemas each family populates. 6. Classify the paired seg070 loops behind `entity_vm_runtime_owner_resource_create`, especially which temporary buffers and record schemas each family populates.
7. Stay on the Remorse VM class-lift batch while the repaired runtime lane is warm: use the now-recovered `CreateFromSlotIndex` caller pack to decide whether any remaining scalar positions deserve stronger typedefs, but keep the return semantically conservative until the base-process inheritance model is explicit enough to justify a prettier live return type. 7. Stay on the Remorse VM class-lift batch while the repaired runtime lane is warm: use the now-recovered `CreateFromSlotIndex` caller pack to decide whether any remaining scalar positions deserve stronger typedefs, but keep the return semantically conservative until the base-process inheritance model is explicit enough to justify a prettier live return type.
8. Continue the `UsecodeDebuggerBreakState` family from the now-landed live array layout, callback-map pass, seg109 consumer pass, and live datatype promotion only if the last `aux_farptr` lane can be closed cheaply; otherwise resume from the current `EntityDispatchEntry` stopping point and map the remaining old `0008:` method groups onto live `CRUSADER.EXE` segments, especially the word-list destroy lane and the timed/periodic constructor cluster. 8. The current broader Remorse follow-up batch is now materially tighter: `WatchEntityController` is effectively re-identified as the live camera-process create lane, `DialogMenuObject` is the last compact family here without a safe live re-anchor, `PresentationCallbackBroker` now has install/teardown plus both slot `+0x08` and preserved slot `+0x0c` caller evidence, `CacheBackendObject` has its indexed entry writer, and `SpriteNode::Create` now looks like the shared compact node constructor for `GumpCreate_*` wrappers. The clearest next unresolved items are therefore: a safer live reanchor for `DialogMenuObject`, a decision on whether the camera-process lane should stay under the stronger live `Camera_*` naming or also receive a class-owner layer, deeper slot `+0x0c` payload classification in the broker lane, and higher-level subtype/layout work above the compact `SpriteNode` base.
8. In the local GhidraMCP upgrade lane, add support for dual POST body decoding (`application/json` plus form-urlencoded) and a constrained live write-side PyGhidra endpoint family so future custom-storage/type repairs can stay inside the active MCP session when Python is enabled. 8. In the local GhidraMCP upgrade lane, add support for dual POST body decoding (`application/json` plus form-urlencoded) and a constrained live write-side PyGhidra endpoint family so future custom-storage/type repairs can stay inside the active MCP session when Python is enabled.
9. Promote additional ledger rows directly from already-verified docs and live comments, especially where segments already deserve `Foothold`, `Partial`, or `Deep`; the new seg029 step-aware sweep batch, seg031 queue-release batch, and seg090 movement-helper batch should be the immediate template. 9. Promote additional ledger rows directly from already-verified docs and live comments, especially where segments already deserve `Foothold`, `Partial`, or `Deep`; the new seg029 step-aware sweep batch, seg031 queue-release batch, and seg090 movement-helper batch should be the immediate template.
10. If the VM lane stalls, revisit `000e:ffb0` from the now-better-constrained video/audio caller windows and try to recover an adjacent non-overlapped helper before attempting broad boundary repair. 10. If the VM lane stalls, revisit `000e:ffb0` from the now-better-constrained video/audio caller windows and try to recover an adjacent non-overlapped helper before attempting broad boundary repair.

View file

@ -0,0 +1,14 @@
import sys
sys.path.insert(0, r"k:\mcp\GhidraMCP")
import bridge_mcp_ghidra as bridge
print(bridge.rename_function_by_address("1288:0fc3", "allocator_phase_finalize_pass"))
print(
bridge.set_decompiler_comment(
"1288:0fc3",
"Live re-anchor for raw 0009:b1c3. Calls PresentationCallbackBroker vtable slot +0x08 twice through the installed 0x4588 broker pointer, then sweeps allocator heads for finalize cleanup.",
)
)
print(bridge.get_function_by_address("1288:0fc3"))

View file

@ -0,0 +1,38 @@
import sys
sys.path.insert(0, r"k:\mcp\GhidraMCP")
import bridge_mcp_ghidra as bridge
updates = [
{
"address": "1250:026c",
"name": "LoadEntryTableFromManifest",
"comment": (
"Constructor-selected cache-backend branch. Fetches a manifest-style buffer through the "
"object callback table, allocates the +0x10/+0x18 entry tables, and copies parsed entry "
"records into backend-owned storage."
),
},
{
"address": "1250:0749",
"name": "InitFixedEntryTable",
"comment": (
"Constructor-selected cache-backend fallback branch. Uses the current entry count and a "
"caller-supplied fixed record size to allocate default entry records and seed the +0x10/+0x18 tables."
),
},
]
for update in updates:
print(f"=== {update['address']} -> {update['name']} ===")
print(
bridge.set_function_class(
function_address=update["address"],
class_path="Remorse::CacheBackendObject",
method_name=update["name"],
)
)
print(bridge.set_decompiler_comment(update["address"], update["comment"]))
print(bridge.get_function_by_address(update["address"]))
print()

View file

@ -0,0 +1,20 @@
import sys
sys.path.insert(0, r"k:\mcp\GhidraMCP")
import bridge_mcp_ghidra as bridge
print(
bridge.set_function_class(
function_address="1250:0910",
class_path="Remorse::CacheBackendObject",
method_name="SetEntryNameAndTag",
)
)
print(
bridge.set_decompiler_comment(
"1250:0910",
"Indexed CacheBackendObject writer. Resolves the logical entry id through the +0x18 remap table, ensures the +0x10 entry buffer exists and is large enough, stores a 4-byte tag/header, and copies the caller-supplied name string at offset +4.",
)
)
print(bridge.get_function_by_address("1250:0910"))

View file

@ -0,0 +1,39 @@
import sys
sys.path.insert(0, r"k:\mcp\GhidraMCP")
import bridge_mcp_ghidra as bridge
updates = [
{
"address": "1430:014c",
"name": "MaterializeChecked",
"comment": (
"Owner-resource wrapper over helper vtable slot +0x0c. "
"Called by InitSlotOwnerBuffers and EnsureSlotChunkLoaded to materialize owner data "
"and assert if the first output byte is 0xff."
),
},
{
"address": "1430:0195",
"name": "QueryMaterializationSize",
"comment": (
"Owner-resource wrapper over helper vtable slot +0x04. "
"Current best read from the 1430:0000 create path is a size-query callback used ahead "
"of owner-data materialization."
),
},
]
for update in updates:
print(f"=== {update['address']} -> {update['name']} ===")
print(
bridge.set_function_class(
function_address=update["address"],
class_path="Remorse::EntityVmOwnerResource",
method_name=update["name"],
)
)
print(bridge.set_decompiler_comment(update["address"], update["comment"]))
print(bridge.get_function_by_address(update["address"]))
print()

View file

@ -0,0 +1,39 @@
import sys
sys.path.insert(0, r"k:\mcp\GhidraMCP")
import bridge_mcp_ghidra as bridge
updates = [
{
"address": "12d0:0513",
"name": "InitOnce",
"comment": (
"Live re-anchor for the 0x4588 presentation-callback broker install path. "
"Guards on 0x4594, snapshots current video/system state into 0x458c/0x4590, installs "
"the nullable broker pointer at 0x4588, and ensures the fallback buffer at 0x45a6 exists."
),
},
{
"address": "12d0:0656",
"name": "TeardownOnce",
"comment": (
"Live re-anchor for the 0x4588 presentation-callback broker teardown path. "
"Guards on 0x4595, clears the installed broker pointer, conditionally emits broker slot +0x0c "
"when 0x4590 != 0x458c, then calls broker slot +0x04 before final video/system cleanup."
),
},
]
for update in updates:
print(f"=== {update['address']} -> {update['name']} ===")
print(
bridge.set_function_class(
function_address=update["address"],
class_path="Remorse::PresentationCallbackBroker",
method_name=update["name"],
)
)
print(bridge.set_decompiler_comment(update["address"], update["comment"]))
print(bridge.get_function_by_address(update["address"]))
print()

View file

@ -0,0 +1,21 @@
import sys
sys.path.insert(0, r"k:\mcp\GhidraMCP")
import bridge_mcp_ghidra as bridge
comments = {
"1278:0616": (
"Verified live caller of PresentationCallbackBroker slot +0x0c through the installed 0x4588 broker pointer. "
"When the local vport state takes the fallback path and param_2 == 0, this function emits the broker callback instead of the normal direct graphics path."
),
"1320:1588": (
"Verified live caller of PresentationCallbackBroker slot +0x0c through the installed 0x4588 broker pointer. "
"Dispatch_ModalGump emits the broker callback before and after modal dispatch when the requested state pair differs from the current SuperVGA mode snapshot."
),
}
for address, comment in comments.items():
print(bridge.set_decompiler_comment(address, comment))
print(bridge.get_function_by_address(address))
print()

View file

@ -0,0 +1,22 @@
import sys
sys.path.insert(0, r"k:\mcp\GhidraMCP")
import bridge_mcp_ghidra as bridge
comments = {
"1180:0000": (
"Live re-anchor for raw 0007:ba00 watch_entity_controller_create_global. "
"The current live NE build exposes this family more concretely as Camera_Init / Camera_CreateProcess "
"rather than under the older watch-entity-controller label."
),
"1180:0045": (
"Live re-anchor for raw 0007:ba45 watch_entity_controller_create. "
"This family now reads as the camera-process create path: allocates the process object, stores the global at 0x2bd8, and seeds the process-name row at 0x2be4."
),
}
for address, comment in comments.items():
print(bridge.set_decompiler_comment(address, comment))
print(bridge.get_function_by_address(address))
print()

View file

@ -0,0 +1,32 @@
import sys
sys.path.insert(0, r"k:\mcp\GhidraMCP")
import bridge_mcp_ghidra as bridge
struct_result = bridge.create_or_update_struct(
name="SpriteNodeBase",
category_path="/Remorse",
size=0x2b,
fields=[
{"offset": 25, "name": "child_or_next_farptr", "datatype": "undefined4", "comment": "Child-chain pointer pair used by traversal and accumulated-position helpers.", "confidence": "high"},
{"offset": 33, "name": "local_x_offset", "datatype": "undefined2", "comment": "Summed by the live SpriteNode offset-accumulation helpers.", "confidence": "high"},
{"offset": 35, "name": "local_y_offset", "datatype": "undefined2", "comment": "Summed by the live SpriteNode offset-accumulation helpers.", "confidence": "high"},
{"offset": 41, "name": "dirty_flags", "datatype": "undefined2", "comment": "Checked by IsDirty and updated by MarkDirty.", "confidence": "high"},
],
)
print("=== struct ===")
print(struct_result)
print()
vtable_result = bridge.create_or_update_struct(
name="SpriteNodeVtable",
category_path="/Remorse",
size=40,
fields=[
{"offset": 20, "name": "event_handler_slot14", "datatype": "undefined4", "comment": "Verified DispatchEvent target for one event class.", "confidence": "high"},
{"offset": 24, "name": "event_handler_slot18", "datatype": "undefined4", "comment": "Verified DispatchEvent target for one event class.", "confidence": "high"},
{"offset": 32, "name": "event_handler_slot20", "datatype": "undefined4", "comment": "Verified DispatchEvent target for one event class.", "confidence": "high"},
{"offset": 36, "name": "event_handler_slot24", "datatype": "undefined4", "comment": "Verified DispatchEvent target for one event class.", "confidence": "high"},
],
)
print("=== vtable ===")
print(vtable_result)

View file

@ -0,0 +1,8 @@
import sys
sys.path.insert(0, r"k:\mcp\GhidraMCP")
import bridge_mcp_ghidra as bridge
for address in ["11e0:14fb", "11e0:1814", "11e0:1913", "11e0:19e6", "11e0:1a33"]:
print(f"=== {address} ===")
print(bridge.decompile_function_by_address(address))
print()

View file

@ -0,0 +1,9 @@
import sys
sys.path.insert(0, r"k:\mcp\GhidraMCP")
import bridge_mcp_ghidra as bridge
for address in ["11e0:17a4", "11e0:17dc", "11e0:187e"]:
print(f"=== {address} ===")
print(bridge.get_function_containing(address))
print(bridge.decompile_function_by_address(address))
print()

View file

@ -0,0 +1,22 @@
import sys
sys.path.insert(0, r"k:\mcp\GhidraMCP")
import bridge_mcp_ghidra as bridge
RANGES = [
("11e0:1ff0", "11e0:2090"),
("11e0:2190", "11e0:2260"),
("11e0:2290", "11e0:22f0"),
("11e0:2390", "11e0:23f0"),
("11e0:24d0", "11e0:2520"),
("11e0:2580", "11e0:25d0"),
]
for start, end in RANGES:
print(f"=== {start} .. {end} ===")
result = bridge.disassemble_region(start, end)
if isinstance(result, list):
for line in result:
print(line)
else:
print(result)
print()

View file

@ -0,0 +1,11 @@
import sys
sys.path.insert(0, r"k:\mcp\GhidraMCP")
import bridge_mcp_ghidra as bridge
for address in ["1288:0fd1", "1278:06b1", "1320:15e9"]:
print(f"=== {address} ===")
print(bridge.get_function_containing(address))
print(bridge.decompile_function_by_address(address))
print()

View file

@ -0,0 +1,9 @@
import sys
sys.path.insert(0, r"k:\mcp\GhidraMCP")
import bridge_mcp_ghidra as bridge
for address in ["0009:5600", "0009:5601"]:
print(f"=== {address} ===")
print(bridge.get_function_containing(address))
print(bridge.decompile_function_by_address(address))
print()

View file

@ -0,0 +1,23 @@
import sys
sys.path.insert(0, r"k:\mcp\GhidraMCP")
import bridge_mcp_ghidra as bridge
for address in ["1250:0000", "1250:0001"]:
print(f"=== {address} ===")
print(bridge.get_function_containing(address))
print(bridge.decompile_function_by_address(address))
print()
for address in ["1250:026c", "1250:0749"]:
print(f"=== helper {address} ===")
print(bridge.get_function_containing(address))
print(bridge.decompile_function_by_address(address))
print(bridge.get_xrefs_to(address))
print()
for address in ["1250:0910"]:
print(f"=== helper {address} ===")
print(bridge.get_function_containing(address))
print(bridge.decompile_function_by_address(address))
print(bridge.get_xrefs_to(address))
print()

View file

@ -0,0 +1,9 @@
import sys
sys.path.insert(0, r"k:\mcp\GhidraMCP")
import bridge_mcp_ghidra as bridge
for address in ["12d8:03b4", "12d8:03d6", "12d8:0451"]:
print(f"=== {address} ===")
print(bridge.get_function_containing(address))
print(bridge.decompile_function_by_address(address))
print()

View file

@ -0,0 +1,9 @@
import sys
sys.path.insert(0, r"k:\mcp\GhidraMCP")
import bridge_mcp_ghidra as bridge
for address in ["13a0:0291", "13a0:045c"]:
print(f"=== {address} ===")
print(bridge.get_function_containing(address))
print(bridge.decompile_function_by_address(address))
print()

View file

@ -0,0 +1,30 @@
from pathlib import Path
import sys
sys.path.insert(0, r"k:\mcp\GhidraMCP")
import bridge_mcp_ghidra as bridge
ADDRESSES = [
"11e0:14fb",
"11e0:1814",
"11e0:1913",
"11e0:19e6",
"11e0:1a33",
"11e0:2000",
"11e0:21a3",
"11e0:21ec",
"11e0:2238",
"11e0:22ab",
"11e0:23af",
"11e0:24ea",
"11e0:251b",
"11e0:25a1",
]
for address in ADDRESSES:
print(f"=== {address} ===")
try:
print(bridge.get_function_containing(address))
except Exception as exc:
print(f"get_function_containing failed: {exc}")
print()

View file

@ -0,0 +1,9 @@
import sys
sys.path.insert(0, r"k:\mcp\GhidraMCP")
import bridge_mcp_ghidra as bridge
for address in ["11e8:0000", "11e8:01a3"]:
print(f"=== {address} ===")
print(bridge.get_function_containing(address))
print(bridge.decompile_function_by_address(address))
print()

View file

@ -0,0 +1,9 @@
import sys
sys.path.insert(0, r"k:\mcp\GhidraMCP")
import bridge_mcp_ghidra as bridge
for address in ["11e8:01fa", "11e8:0246", "11e8:02b9", "11e8:03bd", "11e8:04f8", "11e8:0529", "11e8:05af"]:
print(f"=== {address} ===")
print(bridge.get_function_containing(address))
print(bridge.decompile_function_by_address(address))
print()

View file

@ -0,0 +1,38 @@
import sys
sys.path.insert(0, r"k:\mcp\GhidraMCP")
import bridge_mcp_ghidra as bridge
for address in ["1430:0000", "1430:00fd"]:
print(f"=== {address} ===")
print(bridge.get_function_containing(address))
print(bridge.decompile_function_by_address(address))
print()
for address in ["1220:0000", "1478:67b6", "1478:1228"]:
print(f"=== symbol {address} ===")
if hasattr(bridge, "get_symbol_at"):
print(bridge.get_symbol_at(address))
else:
print(bridge.get_function_by_address(address))
print()
for address in ["1220:0000"]:
print(f"=== decompile {address} ===")
print(bridge.get_function_containing(address))
print(bridge.decompile_function_by_address(address))
print()
for address in ["1420:1866", "1420:19fd"]:
print(f"=== runtime {address} ===")
print(bridge.get_function_containing(address))
print(bridge.decompile_function_by_address(address))
print()
for address in ["1430:014c", "1430:0195"]:
print(f"=== owner helper {address} ===")
print(bridge.get_function_containing(address))
print(bridge.decompile_function_by_address(address))
print(bridge.get_xrefs_to(address))
print()

View file

@ -0,0 +1,9 @@
import sys
sys.path.insert(0, r"k:\mcp\GhidraMCP")
import bridge_mcp_ghidra as bridge
for address in ["1320:0b18", "1320:0b29", "1320:0b44"]:
print(f"=== {address} ===")
print(bridge.get_function_containing(address))
print(bridge.decompile_function_by_address(address))
print()

View file

@ -0,0 +1,9 @@
import sys
sys.path.insert(0, r"k:\mcp\GhidraMCP")
import bridge_mcp_ghidra as bridge
for address in ["1360:0955", "1360:0790", "1360:10d8"]:
print(f"=== {address} ===")
print(bridge.get_function_containing(address))
print(bridge.decompile_function_by_address(address))
print()

View file

@ -0,0 +1,9 @@
import sys
sys.path.insert(0, r"k:\mcp\GhidraMCP")
import bridge_mcp_ghidra as bridge
for address in ["1360:0580", "1360:05a6", "1360:0cb2", "1360:12ee"]:
print(f"=== {address} ===")
print(bridge.get_function_containing(address))
print(bridge.decompile_function_by_address(address))
print()

View file

@ -0,0 +1,9 @@
import sys
sys.path.insert(0, r"k:\mcp\GhidraMCP")
import bridge_mcp_ghidra as bridge
for address in ["000a:b988", "000b:326e", "000b:3380", "000b:33a6", "000b:3ab2", "000b:40ee"]:
print(f"=== {address} ===")
print(bridge.get_function_containing(address))
print(bridge.decompile_function_by_address(address))
print()

View file

@ -0,0 +1,9 @@
import sys
sys.path.insert(0, r"k:\mcp\GhidraMCP")
import bridge_mcp_ghidra as bridge
for address in ["1360:0380", "1360:03af", "1360:0483", "1360:0cd7", "1360:0d79"]:
print(f"=== {address} ===")
print(bridge.get_function_containing(address))
print(bridge.decompile_function_by_address(address))
print()

View file

@ -0,0 +1,11 @@
import sys
sys.path.insert(0, r"k:\mcp\GhidraMCP")
import bridge_mcp_ghidra as bridge
for address in ["1180:000f", "1180:00a8", "1130:3038", "1130:31cc", "1130:3269", "12d0:0516", "12d0:0668"]:
print(f"=== {address} ===")
print(bridge.get_function_containing(address))
print(bridge.decompile_function_by_address(address))
print()

View file

@ -0,0 +1,15 @@
import sys
sys.path.insert(0, r"k:\mcp\GhidraMCP")
import bridge_mcp_ghidra as bridge
comment = (
"Current best live read: compact shared SpriteNode constructor. "
"The body allocates 0x34 bytes, stamps the 0x501a node vtable, and initializes the core link/offset lanes; "
"current direct callers are overwhelmingly GumpCreate_* wrappers, which supports treating this as the base node "
"constructor used by higher-level UI/gump objects rather than a one-off derived leaf."
)
print(bridge.set_decompiler_comment("1360:036a", comment))
print(bridge.get_function_by_address("1360:036a"))

View file

@ -0,0 +1,15 @@
import sys
sys.path.insert(0, r"k:\mcp\GhidraMCP")
import bridge_mcp_ghidra as bridge
comment = (
"Old 000b:3ab2 by preserved offset delta from live 1360:046e. "
"DispatchEvent maps event codes 1/2/4/8/0x10/0x20/0x40/0x100 onto vtable slots "
"+0x04/+0x08/+0x0c/+0x10/+0x14/+0x18/+0x1c/+0x24; the 0x40 path also walks child nodes "
"and dispatches through child slot +0x34 before optional self-slot +0x1c handling."
)
print(bridge.set_decompiler_comment("1360:0cb2", comment))
print(bridge.get_function_by_address("1360:0cb2"))