more work done
This commit is contained in:
parent
5cc5612f4e
commit
d323bb28fc
68 changed files with 714 additions and 19 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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`
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue