diff --git a/Crusader.rep/idata/01/~00000015.db/change.data.gbf b/Crusader.rep/idata/01/~00000015.db/change.data.gbf index b761a34..cd9f537 100644 Binary files a/Crusader.rep/idata/01/~00000015.db/change.data.gbf and b/Crusader.rep/idata/01/~00000015.db/change.data.gbf differ diff --git a/Crusader.rep/idata/01/~00000015.db/change.map.gbf b/Crusader.rep/idata/01/~00000015.db/change.map.gbf index 773701a..af7724f 100644 Binary files a/Crusader.rep/idata/01/~00000015.db/change.map.gbf and b/Crusader.rep/idata/01/~00000015.db/change.map.gbf differ diff --git a/Crusader.rep/idata/01/~00000015.db/db.85.gbf b/Crusader.rep/idata/01/~00000015.db/db.95.gbf similarity index 98% rename from Crusader.rep/idata/01/~00000015.db/db.85.gbf rename to Crusader.rep/idata/01/~00000015.db/db.95.gbf index 1af9f95..e6ca9ef 100644 Binary files a/Crusader.rep/idata/01/~00000015.db/db.85.gbf and b/Crusader.rep/idata/01/~00000015.db/db.95.gbf differ diff --git a/Crusader.rep/idata/01/~00000015.db/db.86.gbf b/Crusader.rep/idata/01/~00000015.db/db.96.gbf similarity index 99% rename from Crusader.rep/idata/01/~00000015.db/db.86.gbf rename to Crusader.rep/idata/01/~00000015.db/db.96.gbf index b70c39a..1c04aa4 100644 Binary files a/Crusader.rep/idata/01/~00000015.db/db.86.gbf and b/Crusader.rep/idata/01/~00000015.db/db.96.gbf differ diff --git a/Crusader.rep/projectState b/Crusader.rep/projectState index 6244408..877ece6 100644 --- a/Crusader.rep/projectState +++ b/Crusader.rep/projectState @@ -3,6 +3,8 @@ + + diff --git a/Crusader.rep/user/00/~00000008.db/db.52.gbf b/Crusader.rep/user/00/~00000008.db/db.54.gbf similarity index 99% rename from Crusader.rep/user/00/~00000008.db/db.52.gbf rename to Crusader.rep/user/00/~00000008.db/db.54.gbf index 83c418d..b2eb540 100644 Binary files a/Crusader.rep/user/00/~00000008.db/db.52.gbf and b/Crusader.rep/user/00/~00000008.db/db.54.gbf differ diff --git a/Crusader.rep/user/00/~0000000d.db/db.5.gbf b/Crusader.rep/user/00/~0000000d.db/db.7.gbf similarity index 99% rename from Crusader.rep/user/00/~0000000d.db/db.5.gbf rename to Crusader.rep/user/00/~0000000d.db/db.7.gbf index 5e2d55c..4a45395 100644 Binary files a/Crusader.rep/user/00/~0000000d.db/db.5.gbf and b/Crusader.rep/user/00/~0000000d.db/db.7.gbf differ diff --git a/docs/entity-dispatch-entry-class-layout.md b/docs/entity-dispatch-entry-class-layout.md index faf41e9..0adeb2e 100644 --- a/docs/entity-dispatch-entry-class-layout.md +++ b/docs/entity-dispatch-entry-class-layout.md @@ -279,10 +279,28 @@ That is no longer true after the next live pass on 2026-04-07. - `+0x46/+0x48 = owned_buffer_a` - `+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. +- 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 -- 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 - 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 diff --git a/docs/entity-vm-runtime-owner-resource-layout.md b/docs/entity-vm-runtime-owner-resource-layout.md index 9a1c502..aa1927c 100644 --- a/docs/entity-vm-runtime-owner-resource-layout.md +++ b/docs/entity-vm-runtime-owner-resource-layout.md @@ -72,17 +72,18 @@ Strong anchors: Best current helper shape: -- compact file-backed helper object -- helper-owned count at `+0x14` -- far-pointer table at `+0x10` -- paired 16-bit table at `+0x18` -- helper vtable `+0x04` acts as size query -- helper vtable `+0x0c` materializes the `0x0d`-stride owner rows later consumed by contexts +- compact `0x14`-byte file-backed wrapper object +- first `0x08` bytes are the embedded file-handle base initialized by `File_MallocOrInit8Bytes` +- helper vtable/dispatch pointer lives at `+0x08` +- materialized owner-row table far pointer lives at `+0x0c` +- helper vtable `+0x04` acts as the current best size/query callback +- helper vtable `+0x0c` materializes owner data; the thin wrapper now asserts if the first output byte is `0xff` Current safest interpretation: - 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` +- 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 @@ -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 provisional datatype `/Remorse/EntityVmOwnerResource` with current stable anchors: - - `+0x10 owner_row_table` - - `+0x14 entry_count` - - `+0x18 entry_word_table` + - first `0x08` bytes reserved for the embedded file base + - `+0x08 helper_vtable` + - `+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/EntityVmSlotEntry` with size `0x26` and only the currently stable tail buffer fields named: - `+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: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. +- 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. - 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` @@ -317,6 +323,7 @@ Verified first batch landed in the live `CRUSADER.EXE` session on 2026-04-05. Current live datatype state: - `/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/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. @@ -335,7 +342,7 @@ Current live datatype state: Current scope of that batch stayed intentionally conservative: - 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 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 diff --git a/docs/presentation-callback-broker-layout.md b/docs/presentation-callback-broker-layout.md index 623328f..8bf0d81 100644 --- a/docs/presentation-callback-broker-layout.md +++ b/docs/presentation-callback-broker-layout.md @@ -200,6 +200,26 @@ Why not first: 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 +## 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 The `0x4588` family is now documented well enough to be treated as a real object candidate. diff --git a/docs/remorse-class-candidate-inventory.md b/docs/remorse-class-candidate-inventory.md index e3170a1..a68ea18 100644 --- a/docs/remorse-class-candidate-inventory.md +++ b/docs/remorse-class-candidate-inventory.md @@ -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. +## 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 ### Best pilot: `EntityDispatchEntryBase` diff --git a/docs/remorse-class-lift-index.md b/docs/remorse-class-lift-index.md index b9845c7..457cbd5 100644 --- a/docs/remorse-class-lift-index.md +++ b/docs/remorse-class-lift-index.md @@ -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 ## 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`. 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 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 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: - `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. +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`. ## Bottom Line diff --git a/docs/sprite-node-class-layout.md b/docs/sprite-node-class-layout.md index 9aa6822..6d272eb 100644 --- a/docs/sprite-node-class-layout.md +++ b/docs/sprite-node-class-layout.md @@ -109,14 +109,19 @@ So the safe future modeling strategy is: ## 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 | |---|---|---| -| `+0x14` | event handler A | `sprite_node_dispatch_event` dispatches here for one event class | -| `+0x18` | event handler B | same dispatcher | -| `+0x20` | event handler C | same dispatcher | -| `+0x24` | event handler D | same dispatcher | +| `+0x04` | event `1` handler | `DispatchEvent` routes event code `1` here directly | +| `+0x08` | event `2` handler | same dispatcher | +| `+0x0c` | event `4` handler | 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: @@ -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` 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 +- 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 - 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 ## Immediate Follow-Up Value diff --git a/docs/usecode-debugger-break-state-layout.md b/docs/usecode-debugger-break-state-layout.md index af4292e..6faab63 100644 --- a/docs/usecode-debugger-break-state-layout.md +++ b/docs/usecode-debugger-break-state-layout.md @@ -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`. - 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. +- 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 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 - `current_frame_payload_farptr` is current-frame-derived from the `frame_base + 0x04` lane - `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 @@ -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. 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. ## Bottom Line diff --git a/plan-mid.md b/plan-mid.md index b1561be..f677c2e 100644 --- a/plan-mid.md +++ b/plan-mid.md @@ -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 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 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. - 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. +- 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`. ### 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. 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. -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. 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. diff --git a/scripts/_tmp_apply_broker_slot08.py b/scripts/_tmp_apply_broker_slot08.py new file mode 100644 index 0000000..c89551e --- /dev/null +++ b/scripts/_tmp_apply_broker_slot08.py @@ -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")) \ No newline at end of file diff --git a/scripts/_tmp_apply_cache_backend_methods.py b/scripts/_tmp_apply_cache_backend_methods.py new file mode 100644 index 0000000..fd09185 --- /dev/null +++ b/scripts/_tmp_apply_cache_backend_methods.py @@ -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() \ No newline at end of file diff --git a/scripts/_tmp_apply_cache_backend_setter.py b/scripts/_tmp_apply_cache_backend_setter.py new file mode 100644 index 0000000..da2c4de --- /dev/null +++ b/scripts/_tmp_apply_cache_backend_setter.py @@ -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")) \ No newline at end of file diff --git a/scripts/_tmp_apply_owner_resource_accessors.py b/scripts/_tmp_apply_owner_resource_accessors.py new file mode 100644 index 0000000..61f99ae --- /dev/null +++ b/scripts/_tmp_apply_owner_resource_accessors.py @@ -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() \ No newline at end of file diff --git a/scripts/_tmp_apply_presentation_callback_broker.py b/scripts/_tmp_apply_presentation_callback_broker.py new file mode 100644 index 0000000..d79255b --- /dev/null +++ b/scripts/_tmp_apply_presentation_callback_broker.py @@ -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() \ No newline at end of file diff --git a/_tmp_apply_vm_class_types.py b/scripts/_tmp_apply_vm_class_types.py similarity index 100% rename from _tmp_apply_vm_class_types.py rename to scripts/_tmp_apply_vm_class_types.py diff --git a/_tmp_changer_ir_dump.py b/scripts/_tmp_changer_ir_dump.py similarity index 100% rename from _tmp_changer_ir_dump.py rename to scripts/_tmp_changer_ir_dump.py diff --git a/scripts/_tmp_comment_broker_slot0c_callers.py b/scripts/_tmp_comment_broker_slot0c_callers.py new file mode 100644 index 0000000..76190cb --- /dev/null +++ b/scripts/_tmp_comment_broker_slot0c_callers.py @@ -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() \ No newline at end of file diff --git a/scripts/_tmp_comment_watch_controller_mapping.py b/scripts/_tmp_comment_watch_controller_mapping.py new file mode 100644 index 0000000..4c157b4 --- /dev/null +++ b/scripts/_tmp_comment_watch_controller_mapping.py @@ -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() \ No newline at end of file diff --git a/_tmp_create_entity_vm_slot_entry_datatype.py b/scripts/_tmp_create_entity_vm_slot_entry_datatype.py similarity index 100% rename from _tmp_create_entity_vm_slot_entry_datatype.py rename to scripts/_tmp_create_entity_vm_slot_entry_datatype.py diff --git a/scripts/_tmp_create_sprite_node_datatypes.py b/scripts/_tmp_create_sprite_node_datatypes.py new file mode 100644 index 0000000..9d4f16c --- /dev/null +++ b/scripts/_tmp_create_sprite_node_datatypes.py @@ -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) diff --git a/scripts/_tmp_decompile_entity_dispatch_periodic.py b/scripts/_tmp_decompile_entity_dispatch_periodic.py new file mode 100644 index 0000000..5d17673 --- /dev/null +++ b/scripts/_tmp_decompile_entity_dispatch_periodic.py @@ -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() diff --git a/scripts/_tmp_decompile_entity_dispatch_periodic_extras.py b/scripts/_tmp_decompile_entity_dispatch_periodic_extras.py new file mode 100644 index 0000000..5acab7f --- /dev/null +++ b/scripts/_tmp_decompile_entity_dispatch_periodic_extras.py @@ -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() diff --git a/scripts/_tmp_disasm_entity_dispatch_word_list.py b/scripts/_tmp_disasm_entity_dispatch_word_list.py new file mode 100644 index 0000000..0fc8027 --- /dev/null +++ b/scripts/_tmp_disasm_entity_dispatch_word_list.py @@ -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() diff --git a/_tmp_entity_vm_context_this_type.py b/scripts/_tmp_entity_vm_context_this_type.py similarity index 100% rename from _tmp_entity_vm_context_this_type.py rename to scripts/_tmp_entity_vm_context_this_type.py diff --git a/_tmp_find_text_section.py b/scripts/_tmp_find_text_section.py similarity index 100% rename from _tmp_find_text_section.py rename to scripts/_tmp_find_text_section.py diff --git a/_tmp_fix_entity_vm_runtime_create.py b/scripts/_tmp_fix_entity_vm_runtime_create.py similarity index 100% rename from _tmp_fix_entity_vm_runtime_create.py rename to scripts/_tmp_fix_entity_vm_runtime_create.py diff --git a/_tmp_fix_entity_vm_runtime_create_typed.py b/scripts/_tmp_fix_entity_vm_runtime_create_typed.py similarity index 100% rename from _tmp_fix_entity_vm_runtime_create_typed.py rename to scripts/_tmp_fix_entity_vm_runtime_create_typed.py diff --git a/_tmp_fix_probably_some_alloc_1000_42e2.py b/scripts/_tmp_fix_probably_some_alloc_1000_42e2.py similarity index 100% rename from _tmp_fix_probably_some_alloc_1000_42e2.py rename to scripts/_tmp_fix_probably_some_alloc_1000_42e2.py diff --git a/_tmp_inspect_entity_vm_runtime_create_frame.py b/scripts/_tmp_inspect_entity_vm_runtime_create_frame.py similarity index 100% rename from _tmp_inspect_entity_vm_runtime_create_frame.py rename to scripts/_tmp_inspect_entity_vm_runtime_create_frame.py diff --git a/_tmp_inspect_psx_banks.js b/scripts/_tmp_inspect_psx_banks.js similarity index 100% rename from _tmp_inspect_psx_banks.js rename to scripts/_tmp_inspect_psx_banks.js diff --git a/_tmp_map248_observers.js b/scripts/_tmp_map248_observers.js similarity index 100% rename from _tmp_map248_observers.js rename to scripts/_tmp_map248_observers.js diff --git a/_tmp_parse_combat_dat.py b/scripts/_tmp_parse_combat_dat.py similarity index 100% rename from _tmp_parse_combat_dat.py rename to scripts/_tmp_parse_combat_dat.py diff --git a/scripts/_tmp_probe_broker_slot08.py b/scripts/_tmp_probe_broker_slot08.py new file mode 100644 index 0000000..93ed482 --- /dev/null +++ b/scripts/_tmp_probe_broker_slot08.py @@ -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() \ No newline at end of file diff --git a/scripts/_tmp_probe_cache_backend_object.py b/scripts/_tmp_probe_cache_backend_object.py new file mode 100644 index 0000000..d2728dc --- /dev/null +++ b/scripts/_tmp_probe_cache_backend_object.py @@ -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() diff --git a/scripts/_tmp_probe_cache_backend_object_callee.py b/scripts/_tmp_probe_cache_backend_object_callee.py new file mode 100644 index 0000000..47204cf --- /dev/null +++ b/scripts/_tmp_probe_cache_backend_object_callee.py @@ -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() diff --git a/scripts/_tmp_probe_cache_backend_object_reanchor.py b/scripts/_tmp_probe_cache_backend_object_reanchor.py new file mode 100644 index 0000000..6bb3a47 --- /dev/null +++ b/scripts/_tmp_probe_cache_backend_object_reanchor.py @@ -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() diff --git a/scripts/_tmp_probe_debugger_formatter_helpers.py b/scripts/_tmp_probe_debugger_formatter_helpers.py new file mode 100644 index 0000000..c99669f --- /dev/null +++ b/scripts/_tmp_probe_debugger_formatter_helpers.py @@ -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() diff --git a/scripts/_tmp_probe_entity_dispatch_entry_live.py b/scripts/_tmp_probe_entity_dispatch_entry_live.py new file mode 100644 index 0000000..dc08a6c --- /dev/null +++ b/scripts/_tmp_probe_entity_dispatch_entry_live.py @@ -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() diff --git a/scripts/_tmp_probe_entity_dispatch_word_list_head.py b/scripts/_tmp_probe_entity_dispatch_word_list_head.py new file mode 100644 index 0000000..2de53b7 --- /dev/null +++ b/scripts/_tmp_probe_entity_dispatch_word_list_head.py @@ -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() diff --git a/scripts/_tmp_probe_entity_dispatch_word_list_reanchor.py b/scripts/_tmp_probe_entity_dispatch_word_list_reanchor.py new file mode 100644 index 0000000..9d02917 --- /dev/null +++ b/scripts/_tmp_probe_entity_dispatch_word_list_reanchor.py @@ -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() diff --git a/scripts/_tmp_probe_owner_resource_live.py b/scripts/_tmp_probe_owner_resource_live.py new file mode 100644 index 0000000..66aabd7 --- /dev/null +++ b/scripts/_tmp_probe_owner_resource_live.py @@ -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() \ No newline at end of file diff --git a/_tmp_probe_psx_section16.js b/scripts/_tmp_probe_psx_section16.js similarity index 100% rename from _tmp_probe_psx_section16.js rename to scripts/_tmp_probe_psx_section16.js diff --git a/_tmp_probe_sections.js b/scripts/_tmp_probe_sections.js similarity index 100% rename from _tmp_probe_sections.js rename to scripts/_tmp_probe_sections.js diff --git a/scripts/_tmp_probe_sprite_get_or_traverse_candidate.py b/scripts/_tmp_probe_sprite_get_or_traverse_candidate.py new file mode 100644 index 0000000..f9bebb1 --- /dev/null +++ b/scripts/_tmp_probe_sprite_get_or_traverse_candidate.py @@ -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() diff --git a/scripts/_tmp_probe_sprite_get_or_traverse_lead.py b/scripts/_tmp_probe_sprite_get_or_traverse_lead.py new file mode 100644 index 0000000..a46cd33 --- /dev/null +++ b/scripts/_tmp_probe_sprite_get_or_traverse_lead.py @@ -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() diff --git a/scripts/_tmp_probe_sprite_node_followups.py b/scripts/_tmp_probe_sprite_node_followups.py new file mode 100644 index 0000000..9f173ff --- /dev/null +++ b/scripts/_tmp_probe_sprite_node_followups.py @@ -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() diff --git a/scripts/_tmp_probe_sprite_node_live.py b/scripts/_tmp_probe_sprite_node_live.py new file mode 100644 index 0000000..47ec5b0 --- /dev/null +++ b/scripts/_tmp_probe_sprite_node_live.py @@ -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() diff --git a/scripts/_tmp_probe_sprite_node_reanchor.py b/scripts/_tmp_probe_sprite_node_reanchor.py new file mode 100644 index 0000000..ce14d6a --- /dev/null +++ b/scripts/_tmp_probe_sprite_node_reanchor.py @@ -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() diff --git a/scripts/_tmp_probe_tier2_candidates.py b/scripts/_tmp_probe_tier2_candidates.py new file mode 100644 index 0000000..7fdee23 --- /dev/null +++ b/scripts/_tmp_probe_tier2_candidates.py @@ -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() \ No newline at end of file diff --git a/_tmp_psx_gpu_search.py b/scripts/_tmp_psx_gpu_search.py similarity index 100% rename from _tmp_psx_gpu_search.py rename to scripts/_tmp_psx_gpu_search.py diff --git a/_tmp_psx_mode1_live_row0_batch.py b/scripts/_tmp_psx_mode1_live_row0_batch.py similarity index 100% rename from _tmp_psx_mode1_live_row0_batch.py rename to scripts/_tmp_psx_mode1_live_row0_batch.py diff --git a/_tmp_rebind_entity_vm_runtime_create.py b/scripts/_tmp_rebind_entity_vm_runtime_create.py similarity index 100% rename from _tmp_rebind_entity_vm_runtime_create.py rename to scripts/_tmp_rebind_entity_vm_runtime_create.py diff --git a/_tmp_scene_link_scan.ps1 b/scripts/_tmp_scene_link_scan.ps1 similarity index 100% rename from _tmp_scene_link_scan.ps1 rename to scripts/_tmp_scene_link_scan.ps1 diff --git a/_tmp_scummvm_attack_process.cpp b/scripts/_tmp_scummvm_attack_process.cpp similarity index 100% rename from _tmp_scummvm_attack_process.cpp rename to scripts/_tmp_scummvm_attack_process.cpp diff --git a/_tmp_scummvm_combat_dat.h b/scripts/_tmp_scummvm_combat_dat.h similarity index 100% rename from _tmp_scummvm_combat_dat.h rename to scripts/_tmp_scummvm_combat_dat.h diff --git a/_tmp_spawner_compare.js b/scripts/_tmp_spawner_compare.js similarity index 100% rename from _tmp_spawner_compare.js rename to scripts/_tmp_spawner_compare.js diff --git a/_tmp_spawner_truth_pass.js b/scripts/_tmp_spawner_truth_pass.js similarity index 100% rename from _tmp_spawner_truth_pass.js rename to scripts/_tmp_spawner_truth_pass.js diff --git a/_tmp_targets.py b/scripts/_tmp_targets.py similarity index 100% rename from _tmp_targets.py rename to scripts/_tmp_targets.py diff --git a/scripts/_tmp_update_sprite_create_comment.py b/scripts/_tmp_update_sprite_create_comment.py new file mode 100644 index 0000000..436dc8a --- /dev/null +++ b/scripts/_tmp_update_sprite_create_comment.py @@ -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")) \ No newline at end of file diff --git a/scripts/_tmp_update_sprite_dispatch_comment.py b/scripts/_tmp_update_sprite_dispatch_comment.py new file mode 100644 index 0000000..89078ed --- /dev/null +++ b/scripts/_tmp_update_sprite_dispatch_comment.py @@ -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")) \ No newline at end of file diff --git a/_tmp_valuebox_cache_scan.py b/scripts/_tmp_valuebox_cache_scan.py similarity index 100% rename from _tmp_valuebox_cache_scan.py rename to scripts/_tmp_valuebox_cache_scan.py diff --git a/_tmp_valuebox_cache_scan_output.txt b/scripts/_tmp_valuebox_cache_scan_output.txt similarity index 100% rename from _tmp_valuebox_cache_scan_output.txt rename to scripts/_tmp_valuebox_cache_scan_output.txt