8.4 KiB
Remorse Class-Lift Work Index
Purpose
This note is the easy-to-find landing page for the current Remorse C++ and class-lifting preparation work.
Use it as the starting point when the project returns to:
- class and namespace authoring inside Ghidra
- vtable and instance-layout promotion
- hand-maintained C++ skeleton emission
- ABI-safe source reconstruction planning
This index does not replace the detailed notes. It groups them into one work order so later implementation can resume quickly.
Read This First
- docs/remorse-cpp-decompilation-plan.md
- docs/remorse-class-candidate-inventory.md
- docs/remorse-rebuild-abi-notes.md
- docs/ghidra-mcp-class-lifting-endpoint-spec.md
That set gives the high-level target, the current candidate families, the rebuild constraints, and the future MCP authoring surface.
Current Note Groups
1. Overall Direction
- docs/remorse-cpp-decompilation-plan.md: staged route from decompiler-style C toward evidence-backed C++.
- docs/remorse-rebuild-abi-notes.md: Track A versus Track B guardrails, segmented-pointer concerns, packing, and calling-convention constraints.
- docs/remorse-toolchain-fingerprint-evidence.md: focused evidence note for the bound NE/Phar Lap/High-C-related toolchain story that underlies the ABI constraints.
- docs/remorse-cpp-compatibility-header-draft.md: first draft of the compatibility/support layer that future C++ skeletons should target.
2. Candidate Inventory And Tooling Surface
- docs/remorse-class-candidate-inventory.md: strongest current class candidates and modeling order.
- docs/ghidra-mcp-class-lifting-endpoint-spec.md: missing class/vtable/datatype authoring operations for the local MCP fork.
3. Family-Specific Layout Notes
- docs/entity-dispatch-entry-class-layout.md: current
EntityDispatchEntrybase-versus-derived model, release surface, and subtype overlays. - docs/sprite-node-class-layout.md:
SpriteNodedestructor/event surface and candidate virtual-slot map. - docs/entity-class-family-split.md: conservative split of the large
Entitylane into base, projectile, debris, corpse/actor, and adjacent non-entity families. - docs/entity-vm-runtime-owner-resource-layout.md: current runtime/helper/context ownership model for the VM lane.
- docs/presentation-callback-broker-layout.md: current object/lifecycle/vtable evidence for the
0x4588presentation-state callback broker family. - docs/usecode-debugger-break-state-layout.md: current object/lifecycle/layout evidence for the dormant seg1408 debugger-state family.
4. Execution Checklists
- docs/remorse-first-class-authoring-checklist.md: concrete first-batch checklist for the initial Ghidra/MCP class-authoring pass, with pilot-family guidance and source-emission gates.
Recommended Work Order
Stage 1: Keep The Evidence Model Honest
- Re-read the plan, ABI note, and candidate inventory.
- Pick one family with bounded ambiguity.
- Confirm ctor, dtor, vtable root, and stable field groups before any class ownership changes in Ghidra.
Best current pilot families:
EntityDispatchEntrySpriteNodeEntityVmOwnerResource
Entity remains a top-priority family, but it should be split deliberately rather than promoted as one giant class too early.
Stage 2: Ghidra Authoring Pass
- Create class or namespace owners.
- Move only strongly owned methods first.
- Create provisional instance structs and vtable structs.
- Preserve slot order and unresolved fields instead of trying to beautify them.
The future MCP endpoint sequence should follow the spec note rather than ad hoc scripting.
Stage 3: First C++ Skeleton Slice
- Emit one header/source pair for the pilot family.
- Build it against the compatibility layer rather than raw host C++ alone.
- Keep unresolved offsets as named placeholders instead of collapsing them into speculative semantics.
- Record which parts are Track A safe versus Track B convenience-only.
Immediate Follow-Ups Still Open
- Convert the first family note into a hand-maintained C++ skeleton once the compatibility header is accepted.
- Implement the MCP class/vtable authoring endpoints only after the workflow and note set above are stable enough to drive them.
- Add one more dedicated note for the callback/object lane around
0x4588only if later caller evidence supports a stronger subsystem name thanPresentationCallbackBroker. - Turn the first-class-authoring checklist into a completed execution log once the first real MCP batch lands.
Current Live Authoring Snapshot
The live CRUSADER.EXE class-authoring lane is no longer just a plan.
Current authored Remorse classes in the active database are:
EntityVmOwnerResourceEntityVmRuntimeEntityVmContextEntityVmSlotEntry
The VM lane is still the furthest along in actual Ghidra authoring. Recent live batches added the bounded EntityVmSlotEntry class owner plus more owned EntityVmRuntime methods (GetSlotChunkPtrAtOffset, ReleaseSlotChunkRef, TryUnloadSlotChunk, DebugDumpSlotMemory, ApplyToMatchingOwnerRows) rather than stopping at free-function naming.
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.ApplyToMatchingOwnerRows(runtime_farptr, slot_index_filter, chunk_index_filter)now reads as a real iterator/filter helper instead of a split-word scratch signature.
The next live batch pushed that further still: most of the EntityVmRuntime method cluster now carries an explicit 4-byte EntityVmRuntime * this in-session, including Create. The main remaining type gap inside that class is no longer the runtime object itself, but the exact far slot-entry pointer positions on AcquireSlotForEntity and InitSlotOwnerBuffers.
That VM-side gap is now closed too: AcquireSlotForEntity returns EntityVmSlotEntry *32 in DX:AX, InitSlotOwnerBuffers now accepts EntityVmSlotEntry *32, EntityVmOwnerResource::{Create,Destroy} now carry explicit 4-byte this, and the simple EntityVmContext lifecycle methods now do the same.
The next family switch has also landed in the live database: Remorse::UsecodeDebuggerBreakState now exists as a real class owner with a provisional 0x2f2 datatype and a stronger method batch (Create, MaybeBreakOnCurrentLine, BreakpointInsertSorted, BreakpointRemove, HasBreakpoint, CallstackPushFrame, CallstackPushEntry, CallstackPopEntry, EnableSingleStep, ClearStepState, CurrentEntryGetUnitName).
That debugger family is no longer just a top-level shell. The internal record shapes are now recovered and applied live well enough to treat the two tables as real fixed-size arrays in-session: breakpoint entries are 0x0b bytes with unit_name_inline[9] + line_number, and callstack entries are 0x15 bytes with unit_name_inline[9] plus the currently safest trailing fields source_stream_target_farptr, current_frame_payload_farptr, and still-neutral aux_farptr.
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
The current prep work is now large enough that it should be treated as one coordinated lane rather than scattered notes.
Use this file as the resume point for future class-lift and C++ reconstruction work.