This note gathers the current class-lift-relevant structure for the VM runtime lane into one place.
It focuses on four connected objects:
-`EntityVmRuntime`
-`EntityVmOwnerResource`
-`EntityVmContext`
- the slot/value helpers that connect gameplay entities to owner-loaded VM source data
The goal is not full opcode recovery. The goal is to make later class authoring and C++ skeleton emission faster by freezing the current ownership model.
## High-Level Ownership Model
Current best model from [docs/raw-0008-000c.md](docs/raw-0008-000c.md) and [docs/raw-000a-000d.md](docs/raw-000a-000d.md):
1. startup path resolves a configured EUSECODE root/path
2.`entity_vm_runtime_create` allocates the main runtime body
3. runtime constructor attaches one file-backed helper created by `entity_vm_runtime_owner_resource_create`
4. gameplay entities map to slot indices through `entity_vm_slot_index_from_entity`
5. masked-create helpers test owner-side capability bits and then build per-entity or per-slot `EntityVmContext` objects
6. contexts seed their local stream/value state from owner-loaded source rows and runtime slot caches
The paired loops rooted at raw windows `0009:67b6` and `0009:6916` are current best evidence that the helper is file-backed rather than a pure in-memory descriptor copier.
Verified behavior already captured in the main notes:
- iterate helper-owned count at `+0x14`
- index path/id tables at `+0x10` and `+0x18`
- build formatted paths with two distinct format strings
- open, seek/read, close, and free loop-local buffers through the DOS/file helper lane
Current caution:
- exact per-family record schema is still open, so the helper should be modeled as a loader/index object first, not as a final descriptor-schema class.
- per-entity or per-slot execution/context object built from runtime slot state plus owner-loaded source data
Current layout claims that matter for class lifting:
-`+0x32` stores slot index
-`+0x34` stores the additive offset word used by the `slot_load_value_plus_offset` lane
-`+0x36` embeds the mini-VM/state object
-`+0xd6/+0xd8` hold the seeded source/control stream lane
-`+0x102` is the backward-growing local payload/buffer lane
-`+0x10c/+0x10e` store a derived low/high pair reused by save/load
-`+0x117/+0x119` cache the owner-linked source pair
-`+0x123` behaves as a busy or active flag in the sync/dispatch path
Current caution:
- context dispatch semantics are still active work, so this object should be modeled around lifecycle and data ownership first, not around final method names for every opcode-facing helper.
## Gameplay Entity To VM Bridge
### Slot selection
`entity_vm_slot_index_from_entity` (`000d:45c5`) is the key bridge from gameplay entity identity into the VM lane.
Current safest summary:
- it does not choose `NPCTRIG` versus `EVENT` directly
- it maps gameplay entities into category spans using runtime base words such as `0x8c7c/0x8c7e/0x8c80`
- owner-row capability bits and later slot-value materialization do the next stage of filtering
### Masked-create helpers
The masked-create family is already class-lift relevant even before final event labels are known.
What is safe now:
- the hub at `000d:463a` checks runtime-disable state and owner-side mask bits
- low-slot and high-slot wrappers differ by slot id, mask, and whether they pass an extra signed/additive word
- wrappers like slot `0x0a` / `0x0b` are offset-specialized context creators, not separate selector universes
This means future Ghidra or C++ modeling should treat them as helper factories around `EntityVmContext`, not as methods on unrelated gameplay classes.
## Best Class-Lift Targets In This Lane
1.`EntityVmOwnerResource`
2.`EntityVmRuntime`
3.`EntityVmContext`
Why this order:
- owner-resource helper is compact and structurally bounded
- runtime has clear ownership over the helper and slot table
- context has the richest semantics but also the most unresolved dispatcher behavior
- 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`
-`+0x20 owner_buffer_segment`
-`+0x22 chunk_state_offset`
-`+0x24 chunk_state_segment`
- Moved `1430:0000` under `Remorse::EntityVmOwnerResource::Create`.
- Moved `1430:00fd` under `Remorse::EntityVmOwnerResource::Destroy`.
- Moved `1420:1499` under `Remorse::EntityVmRuntime::Create`.
- Moved `1420:1536` under `Remorse::EntityVmRuntime::InitSlots`.
- Moved `1420:1575` under `Remorse::EntityVmRuntime::ReleaseSlots`.
- Moved `1420:1601` under `Remorse::EntityVmRuntime::Destroy`.
- Moved `1420:167c` under `Remorse::EntityVmRuntime::AcquireSlotForEntity` after live decompilation showed a `0x80`-entry scan over the runtime slot table with free-slot fallback and eviction of the currently selected slot.
- Moved `1420:1866` under `Remorse::EntityVmRuntime::InitSlotOwnerBuffers` after live decompilation showed owner-resource reads plus the two slot-local buffer allocations and initial sentinel fill.
- Moved `1420:19fd` under `Remorse::EntityVmRuntime::EnsureSlotChunkLoaded` after live decompilation showed per-slot chunk materialization and cache-presence marking.
- Renamed `1420:1cca` to `entity_vm_runtime_debug_dump_slot_memory` after live decompilation showed a debug-gated walk of the runtime slot list with slot memory usage output.
- Renamed `1420:1f24` to `entity_vm_runtime_apply_to_matching_owner_rows` after live decompilation showed filtered iteration over the runtime owner-row list.
- Renamed `1420:2040` to `entity_vm_slot_entry_create_or_clear` after live decompilation showed allocation and zeroing of one `0x26`-byte slot record.
- Added short decompiler comments at `1430:0000` and `1430:00fd` to preserve the raw `000d:7000` / `000d:70fd` provenance.
- Added short decompiler comments at `1420:1499`, `1420:1536`, `1420:1575`, and `1420:1601` to preserve the runtime-lifecycle provenance and current layout claims.
- Added short decompiler comments at `1420:167c`, `1420:1866`, `1420:19fd`, `1420:1cca`, `1420:1f24`, and `1420:2040` so the slot-table evidence stays visible in the live database.
- Repaired the decompiler health of `1420:1499 Remorse::EntityVmRuntime::Create` after the delete/recreate cycle left it throwing `Low-level Error: Symbol $$undef00000006 extends beyond the end of the address space`; the root cause was the shared allocator helper at `1000:42e2`, whose pointer-return signature decompiled with a hidden `__return_storage_ptr__` and poisoned the caller stack model until it was normalized to an explicit `dword` return plus explicit stack storage.
- Verified second batch landed in the live `CRUSADER.EXE` session on 2026-04-06.
- Moved `1420:0eec` under `Remorse::EntityVmContext::CreateFromSlotIndex`.
- Moved `1420:10b6` under `Remorse::EntityVmContext::FreeBuffer`.
- Moved `1420:10da` under `Remorse::EntityVmContext::SyncGlobalValueAndDispatch`.
- Moved `1420:1162` under `Remorse::EntityVmContext::Destroy`.
- Moved `1420:118f` under `Remorse::EntityVmContext::Save`.
- Moved `1420:1278` under `Remorse::EntityVmContext::Load`.
- Added short decompiler comments at `1420:0eec`, `1420:10b6`, `1420:10da`, `1420:1162`, `1420:118f`, and `1420:1278` to preserve the raw `000d:46ec`, `000d:48b6`, `000d:48da`, `000d:4962`, `000d:498f`, and `000d:4a78` provenance after the class-owner move.
- Verified third batch landed through local PyGhidra on 2026-04-06 after the live `run_write_script(...)` route still returned `404 No context found for request` against the active GUI session.
- Created provisional datatype `/Remorse/EntityVmContext` with size `0x124` and the currently safest stable anchors:
- Updated `1420:2040 entity_vm_slot_entry_create_or_clear` to `EntityVmSlotEntry * __cdecl16far entity_vm_slot_entry_create_or_clear(EntityVmSlotEntry * slot_entry)` so the slot-record helper no longer falls back to anonymous `byte *` parameters.
- Updated `1420:167c Remorse::EntityVmRuntime::AcquireSlotForEntity` to return `EntityVmSlotEntry *`, while leaving the third `param_3` entity-like pointer conservative until caller-side role recovery is tighter.
- Updated `1420:1866 Remorse::EntityVmRuntime::InitSlotOwnerBuffers` so the third parameter is now `EntityVmSlotEntry * slot_entry`.
- Updated `1420:1536 Remorse::EntityVmRuntime::InitSlots` to `void __cdecl16far InitSlots(EntityVmRuntime * this)`.
- Updated `1420:1575 Remorse::EntityVmRuntime::ReleaseSlots` to `void __cdecl16far ReleaseSlots(EntityVmRuntime * this)`.
- Tried the same typed-`this` collapse on `1420:1499 Remorse::EntityVmRuntime::Create`, but the pointer-sized `this` variant reintroduced a hidden `__return_storage_ptr__`; the function was restored immediately to the known-good split-word custom-storage signature `dword __cdecl16far Create(word this, word runtime_segment, word owner_type, word owner_id)`.
- Verified fourth live batch landed on 2026-04-06.
- Updated the `local_a` decompiler local in `1420:19fd Remorse::EntityVmRuntime::EnsureSlotChunkLoaded` to `EntityVmSlotEntry *`, so the slot-entry cache path now renders the stable `owner_buffer_offset` and `chunk_state_offset` fields directly instead of anonymous `undefined4` pairs.
- Retried the `EntityVmContext` lifecycle typing pass through live MCP. `apply_class_layout` dry-run for `/Remorse/EntityVmContext` now returns a normal structured preview instead of the earlier null failure, but the real apply path still fails with `Failed to apply this type: Storage size does not match data type size: 2`, and direct live `set_function_this_type` calls on `FreeBuffer`, `SyncGlobalValueAndDispatch`, `Destroy`, `Save`, and `Load` hit the same storage-size mismatch.
- Retried live `run_write_script(...)` with and without explicit target selectors on `CRUSADER.EXE`, but the route still returned `404 No context found for request`, so there is still no live in-session fallback for forcing the dynamic-storage rewrite on the context methods.
- Verified fifth batch landed through local PyGhidra on 2026-04-06 after the live write-side routes still blocked the context pass.
- Updated `1420:0eec Remorse::EntityVmContext::CreateFromSlotIndex` so the first parameter is now `EntityVmContext * this` while preserving the existing `UsecodeProcess *` return type until the constructor/factory semantics are tighter.
- Updated `1420:10b6 FreeBuffer`, `1420:10da SyncGlobalValueAndDispatch`, `1420:1162 Destroy`, `1420:118f Save`, and `1420:1278 Load` so the first parameter is now `EntityVmContext * this` instead of `UsecodeProcess *`.
- That local fallback confirms the newer dynamic-storage rewrite is sufficient for the context lifecycle cluster when applied outside the live GUI session. The remaining MCP issue is deployment/session parity, not whether the typing model itself works.
- Verified sixth analysis-only live batch on 2026-04-06.
- Exercised the new storage-aware prototype route against the two known 16-bit repair cases (`1000:42e2` and `1420:1499`) through the active MCP session. The checked-in source has the new route wiring, but the live GUI plugin still answered with legacy behavior: `/set_function_prototype_storage` returned the old `set_function_prototype` failure body, and `/set_storage_aware_prototype` returned `404 No context found for request`. That confirms the remaining issue is live deployment parity, not endpoint design.
- Rechecked the direct callers of `CreateFromSlotIndex`: `Usecode_ItemCallEvent` plus two `Interpreter_NextUsecodeOp` call sites. The `Usecode_ItemCallEvent` path explicitly calls `CreateFromSlotIndex((EntityVmContext *)0x0,0,...)` as an allocate-and-return factory, and the current caller-side uses immediately consume only base `Process`-style fields such as `procid` and termination flags. The two interpreter call sites likewise just store the returned far pointer in `DX:AX` scratch pairs before later base-process handling.
- That caller evidence is enough to keep the current conservative return type for now: `CreateFromSlotIndex` is clearly manufacturing an `EntityVmContext`, but promoting the return to `EntityVmContext *` before the inheritance/base-process datatype story is explicit would probably make current caller decompilation less clear rather than more clear.
- Verified seventh live batch landed on 2026-04-06 after the refreshed MCP build came up.
- Re-exercised `set_function_prototype_storage(...)` in-session on the two known 16-bit repair cases. The route now reaches the real storage-aware implementation and can preserve the explicit `AX:DX` return storage in-session, but two live issues remain: stack offsets at `10` and above currently need `0x` prefixes to avoid landing at `0x10`/`0x12`/`0x14`/`0x16`, and `calling_convention='__cdecl16far'` still normalizes the repaired functions to plain `__cdecl`.
- Updated `/Remorse/EntityVmSlotEntry` one step deeper from the `InitSlotOwnerBuffers` and `EnsureSlotChunkLoaded` evidence:
-`+0x00 match_key_farptr`
-`+0x0a owner_chunk_count`
-`+0x12 owner_data_base`
- retained the earlier `+0x1e..+0x24` owner-buffer and chunk-state pointer pairs
- Updated local variable typing so `AcquireSlotForEntity` now carries `EntityVmSlotEntry *` locals for the current slot cursor/free-slot candidate lane, and `InitSlotOwnerBuffers` now carries an `EntityVmSlotEntry *` local for the owner-metadata scratch object.
- The decompiler payoff is immediate: `InitSlotOwnerBuffers` now shows `owner_chunk_count`, `owner_buffer_*`, and `chunk_state_*` directly, and `EnsureSlotChunkLoaded` now shows `owner_data_base` where the slot metadata seeds the later owner-data window.
- Tried the stronger storage-aware `Create(this: /Remorse/EntityVmRuntime * @ stack:0x4:4, ...)` model through the new endpoint, but it still fails with `Storage size does not match data type size: 2`. That makes the remaining blocker more precise again: the live MCP route is now good enough to express the desired 4-byte storage, but the current `EntityVmRuntime *` datatype in this 16-bit NE session still resolves to a 2-byte pointer type.
- Verified eighth live batch landed on 2026-04-06.
- Reloaded the live plugin and re-verified `set_function_prototype_storage(...)` on the two known 16-bit proof cases. The route now works in-session and preserves explicit `AX:DX` return storage cleanly, but `calling_convention='__cdecl16far'` still normalizes both `1000:42e2` and `1420:1499` to plain `__cdecl`.
- Renamed `1420:1d72` to `entity_vm_runtime_get_slot_chunk_ptr_at_offset` after confirming from `CreateFromSlotIndex`, `Load`, and `FUN_1418_035f` that it is just a wrapper over `EnsureSlotChunkLoaded` plus a caller-supplied offset.
- Renamed `1420:1d8d` to `entity_vm_runtime_release_slot_chunk_ref` after confirming from the `Interpreter_NextUsecodeOp` caller that it decrements one live chunk-state refcount and asserts if the chunk was not retained.
- Renamed `1420:1e17` to `entity_vm_runtime_try_unload_slot_chunk` after confirming from `entity_vm_runtime_apply_to_matching_owner_rows` that it only unloads a chunk when the chunk-state count has reached zero, restoring the owner-buffer entry and freeing runtime budget during cleanup/eviction.
- Added short decompiler comments to those three helpers so the slot-entry ownership story stays visible in the live database.
- Verified ninth live batch landed on 2026-04-06.
- Created provisional datatype `/Remorse/EntityVmLoadedChunkRecord` with the current stable cleanup/iterator anchors:
-`+0x06 next_offset`
-`+0x08 next_segment`
-`+0x0e saved_chunk_offset`
-`+0x10 saved_chunk_segment`
-`+0x12 slot_index`
-`+0x14 chunk_index`
- Updated `1420:1e17 entity_vm_runtime_try_unload_slot_chunk` so the second parameter is now `EntityVmLoadedChunkRecord * loaded_chunk_record`, and then tightened the return to `byte __cdecl16far` with explicit `AL` storage after caller disassembly at `1420:1f50` and `1420:1fc1` showed both call sites consume only `AL`.
- Updated the iterator local `uStack_6` in `1420:1f24 entity_vm_runtime_apply_to_matching_owner_rows` to `EntityVmLoadedChunkRecord *`, so the owner-row cleanup walk now renders `next_*`, `slot_index`, and `chunk_index` directly instead of anonymous stack-pair traffic.
- Confirmed the interpreter-side release helper caller at `1418:3330` pushes the live chunk record's `slot_index` / `chunk_index` pair from `ES:[BX+0x32]` / `ES:[BX+0x34]` together with the runtime far pointer before calling `entity_vm_runtime_release_slot_chunk_ref`, which makes the loaded-chunk record a real shared runtime record rather than a one-off cleanup scratch blob.
- Verified tenth live batch landed on 2026-04-06.
- Renamed local helper `1418:003c` to `interpreter_pop_saved_farptr` after confirming from its only caller in `Interpreter_NextUsecodeOp` that it decrements a saved-farptr stack count at `+0x80` and returns the far pointer stored at the new top entry.
- Added short decompiler comments at `1418:003c` and `1418:3330` so the interpreter-side release/restore lane stays visible without overcommitting the restored far pointer to a stronger semantic than the current evidence supports.
- Verified eleventh live batch landed on 2026-04-06.
- Created class owner `Remorse::EntityVmSlotEntry` in the live database and moved `1420:2040` under it as `CreateOrClear`.
- Tightened `Remorse::EntityVmSlotEntry::CreateOrClear` so the single parameter is now named `this` and the explicit far return storage is restored to `AX` for the `EntityVmSlotEntry *` result.
- Moved the previously global runtime cleanup helpers under `Remorse::EntityVmRuntime` as real methods:
-`1420:1d72` -> `GetSlotChunkPtrAtOffset`
-`1420:1d8d` -> `ReleaseSlotChunkRef`
-`1420:1cca` -> `DebugDumpSlotMemory`
-`1420:1e17` -> `TryUnloadSlotChunk`
-`1420:1f24` -> `ApplyToMatchingOwnerRows`
- Tightened the `ReleaseSlotChunkRef` parameter names to `runtime_farptr`, `slot_index`, and `chunk_index`, and renamed the `DebugDumpSlotMemory` far-pointer argument to `runtime_farptr` so the runtime-owned chunk/refcount lane reads more like method code than detached helper code.
- Verified twelfth live batch landed on 2026-04-06.
- Tightened `Remorse::EntityVmRuntime::GetSlotChunkPtrAtOffset` to `dword __stdcall16far GetSlotChunkPtrAtOffset(dword runtime_farptr, int slot_index, int chunk_index, dword intra_chunk_offset)` after re-checking the `CreateFromSlotIndex` and `Load` callers. The current best read is: load/ensure one slot chunk through the runtime, then add a caller-supplied intra-chunk offset pair to the returned far pointer.
- Tightened `Remorse::EntityVmRuntime::ApplyToMatchingOwnerRows` to `byte __cdecl16far ApplyToMatchingOwnerRows(dword runtime_farptr, int slot_index_filter, int chunk_index_filter)` after re-checking the `AcquireSlotForEntity` and `EnsureSlotChunkLoaded` callers. The current best read is: iterate the runtime-owned loaded-chunk list either broadly (`-1/-1`) or for one current slot/chunk pair.
- Restored explicit return storage after the storage-aware retype pass so `GetSlotChunkPtrAtOffset` still returns its far pointer in `DX:AX` and `ApplyToMatchingOwnerRows` still returns its boolean result in `AL`.
- Verified thirteenth live batch landed on 2026-04-06.
- Lifted the grouped runtime methods from split-word `runtime_farptr` parameters to explicit 4-byte `EntityVmRuntime * this` storage using `/Remorse/EntityVmRuntime *32` in-session. The live signatures now read as real methods for:
-`Create`
-`InitSlots`
-`ReleaseSlots`
-`DebugDumpSlotMemory`
-`ReleaseSlotChunkRef`
-`GetSlotChunkPtrAtOffset`
-`TryUnloadSlotChunk`
-`ApplyToMatchingOwnerRows`
-`EnsureSlotChunkLoaded`
-`Remorse::EntityVmRuntime::Create` is the biggest change in that batch: it no longer needs the old split-word placeholder form and now holds `dword __cdecl16far Create(EntityVmRuntime * this, word owner_type, word owner_id)` with the original `AX:DX` return preserved.
-`EnsureSlotChunkLoaded` now also carries the clearer `EntityVmRuntime * this, short slot_index, short chunk_index` signature with the original far-pointer return preserved in `DX:AX`.
-`AcquireSlotForEntity` and `InitSlotOwnerBuffers` are now fully over that hurdle too: `AcquireSlotForEntity` returns `EntityVmSlotEntry *32` in `DX:AX`, and `InitSlotOwnerBuffers` now carries `EntityVmSlotEntry *32 slot_entry` as its third parameter.
- Verified fourteenth live batch landed on 2026-04-06.
- Finished the remaining straightforward VM pointer cleanup outside the hottest runtime helper cluster:
-`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`
-`dword pitemno_farptr`
-`word mode_flags`
-`word slot_index`
-`word value_add_offset`
-`word intra_chunk_offset`
-`dword ucparam_farptr`
-`uint ucparamsize`
- Restored explicit far return storage on `CreateFromSlotIndex` to `AX:DX` after the storage-aware apply briefly dropped it.
- The same live pass also made the remaining endpoint weakness more concrete again: once the caller-backed custom-storage pack is applied, the endpoint still textualizes the function as plain `dword __cdecl` instead of preserving the earlier higher-level `UsecodeProcess *` / `__stdcall16far` surface, even though the decompiler now keeps the correct argument boundaries and the return really is back in `AX:DX`.
- Current best caller-backed read for `CreateFromSlotIndex` is now narrower and more useful than the old placeholder form:
-`owner_source_farptr` is a real far-pointer input that is persisted to context `+0x11b/+0x11d`
-`ucparam_farptr` is a real far-pointer input copied into the backward-growing buffer at `+0x102`
-`slot_index`, `value_add_offset`, and `intra_chunk_offset` are distinct scalar inputs rather than one collapsed anonymous pack
- the conservative semantic story is still `factory/setup bridge that returns a far process/context pointer`, not `final inheritance-clean constructor signature`
- 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/EntityVmSlotEntry` now exists both as a bounded helper datatype and as a live `Remorse` class owner. Its current authored surface is intentionally small: one constructor/clear method plus the stable `match_key_farptr`, `owner_chunk_count`, `owner_data_base`, and owner-buffer / chunk-state pointer anchors.
-`/Remorse/EntityVmLoadedChunkRecord` now exists as the shared cleanup/iteration record for the chunk-release and conditional-unload lane, with the currently stable next-link, saved-owner-buffer, slot-index, and chunk-index fields named.
-`/Remorse/EntityVmContext` now exists and matches the current owned lifecycle cluster, but it still only records the safest field anchors rather than the full embedded mini-VM layout.
-`apply_class_layout` succeeded for `Remorse::EntityVmOwnerResource` but failed for `Remorse::EntityVmRuntime` when the binder tried to apply a `this` type, even though plain ownership moves worked.
- The old `apply_class_layout` dry-run null failure for `Remorse::EntityVmContext` no longer reproduces on the current live server, but the actual write-side `this` typing path is still effectively old-build behavior: the real apply and direct `set_function_this_type` calls still fail on the existing `UsecodeProcess *` lifecycle signatures with `Storage size does not match data type size: 2`.
- The `EntityVmContext` lifecycle signatures are now locally repaired through PyGhidra: `CreateFromSlotIndex` plus `FreeBuffer` / `SyncGlobalValueAndDispatch` / `Destroy` / `Save` / `Load` all carry `EntityVmContext * this` as their first parameter.
-`CreateFromSlotIndex` should still keep a conservative semantic return in the notes for the moment. The active live endpoint now textualizes it as `dword __cdecl` after the caller-packed custom-storage cleanup, but the allocate-and-return behavior is clear, the real return storage is back in `AX:DX`, and the known callers still consume the result through base-process fields rather than through an inheritance-aware `EntityVmContext : UsecodeProcess` datatype model.
- The runtime lane is now split more accurately: `InitSlots` and `ReleaseSlots` can carry a direct `EntityVmRuntime * this`, while `Create` still needs the split-word custom-storage form to avoid hidden return-storage breakage.
- The runtime lane is grouped more accurately too: the chunk-access, chunk-ref release, debug-dump, conditional-unload, and owner-row iterator helpers now sit under `Remorse::EntityVmRuntime` instead of remaining global free functions.
- The runtime lane is also typed more accurately now: the chunk accessor is no longer a five-word anonymous wrapper, and the owner-row iterator no longer pretends its runtime pointer is two independent split-word parameters.
- The authored VM lane is now much closer to a real class surface than a namespace grouping: `EntityVmRuntime`, `EntityVmOwnerResource`, `EntityVmContext`, `EntityVmSlotEntry`, and the helper `EntityVmLoadedChunkRecord` all now participate in a mostly far-pointer-correct live type model, with `CreateFromSlotIndex` as the main remaining signature outlier.
- The slot-entry model is tighter again: beyond the earlier `owner_buffer_*` and `chunk_state_*` tails, the datatype now also exposes `owner_chunk_count` and `owner_data_base`, which makes the allocator/count path in `InitSlotOwnerBuffers` and the owner-data window math in `EnsureSlotChunkLoaded` read as object state rather than anonymous offset pairs.
- The adjacent helper map is tighter too: the slot-entry consumer side now has one pointer-plus-offset accessor, one chunk-ref release helper, one conditional-unload helper, and one named loaded-chunk iterator record instead of a mix of anonymous `1420:` placeholders and anonymous stack-pair scratch state.
- 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
Best immediate next moves after this landed:
- inspect `EnsureSlotChunkLoaded` and adjacent `1420:` helpers again now that `AcquireSlotForEntity` returns `EntityVmSlotEntry *`, and push the slot-entry type one step deeper only where the resulting local/object read is genuinely clearer
- decide whether `CreateFromSlotIndex` can safely promote its return type from `UsecodeProcess *` to `EntityVmContext *`, or whether it should stay a factory-style bridge that only types `this`
- if the context/base-process inheritance story becomes explicit in datatypes, revisit `CreateFromSlotIndex` return typing then; until that point, keep the current `UsecodeProcess *` return even though the body itself clearly builds an `EntityVmContext`
- decide whether `match_key_farptr` at `+0x00` should stay as a neutral far-pointer field or can now be promoted to a stronger entity/owner key name from caller-side evidence
- recover a storage-aware `this`-typing path for `Create` specifically; the live route now works well enough to test explicit 4-byte storage, but the remaining blocker is the 2-byte `EntityVmRuntime *` datatype itself rather than endpoint reachability
- inspect the broader `Interpreter_NextUsecodeOp` lane around `1418:3330` now that the release call and `interpreter_pop_saved_farptr` are anchored, and decide whether the loaded-chunk record can absorb any more of the surrounding save/restore stack traffic without overfitting transient interpreter locals
- redeploy or otherwise verify the live storage-fallback `set_function_this_type` / `apply_class_layout` build, then retry the `EntityVmContext` lifecycle typing pass in-session before dropping back to local PyGhidra
- identify one or two additional strongly owned runtime or owner-resource helpers if the live session exposes them cleanly
- decide whether `ApplyToMatchingOwnerRows` should keep its current generic split-word parameters under `Remorse::EntityVmRuntime` or whether the first argument pair is now well enough understood to collapse into a typed runtime `this`
- decide whether the newly clarified `runtime_farptr` argument on `GetSlotChunkPtrAtOffset` and `ApplyToMatchingOwnerRows` is enough to justify a safe typed-`this` experiment on those methods, or whether the current `EntityVmRuntime *` pointer-size issue still makes the explicit `dword runtime_farptr` form the least misleading representation
- use the now-recovered `CreateFromSlotIndex` caller pack as the baseline for any next cleanup, and only chase a prettier return type once the base-process inheritance story is explicit enough to make that promotion a real readability win
If emitted as provisional C++ later, safest early skeleton is:
-`EntityVmOwnerResource` with explicit loader/index fields and placeholder virtual/helper methods
-`EntityVmRuntime` with fixed-size slot table, owner pointer, category-base fields, and create/destroy methods
-`EntityVmContext` with exact saved-field placeholders and a distinct embedded mini-VM state member
Avoid in the first skeleton:
- speculative opcode enums presented as final
- collapsing the owner-resource helper into plain runtime fields
- flattening the source/control stream pair into one host-only pointer abstraction if Track A remains active
## Bottom Line
The VM lane now supports a real class model, but it should start with ownership and layout rather than with overconfident script-semantic names.
The most defensible current model is `runtime owns helper and slot state; contexts are short-lived objects built from slot selection plus owner-loaded source rows`.