more docs
This commit is contained in:
parent
1ad746ba82
commit
a70ec15899
21 changed files with 1357 additions and 25 deletions
|
|
@ -151,14 +151,76 @@ What is now materially tighter:
|
|||
- it uses the token as the `path` component to `Filespec_GetFullPath`
|
||||
- it uses the mutable filename template at `1478:07a0`, which is `eusecode.flx`, as the fixed `filename` component
|
||||
- it forces that template's first byte to `'e'` before the existence probe and final load call
|
||||
- the path builder uses DOS-style backslashes via the literal `"\\"` string from `FILE\\FILESPEC.C`
|
||||
- the existence probe goes through the DOS file-search path in `File_Exists`, so the override must resolve inside the game's DOS-visible filesystem
|
||||
- the parser copies the token into the `0x1e`-byte buffer at `1478:065a`, so the practical maximum is `29` visible characters plus the terminator
|
||||
|
||||
So the safest current retail read is:
|
||||
|
||||
- `-u <arg>` expects a directory/resource-root style path argument for the standard `eusecode.flx` archive family
|
||||
- it does **not** currently look like a free-form arbitrary filename override
|
||||
- the safest token spelling uses DOS-style backslashes, not forward slashes
|
||||
|
||||
That gives a direct explanation for failed attempts like:
|
||||
|
||||
- `-u USECODE/FLICTEST.FLX`
|
||||
- `-u FLICTEST.FLX`
|
||||
- `-u C:\MADDOCODE` when that path only exists on the Windows host and not inside the guest DOS drive mapping
|
||||
|
||||
Those forms fit the recovered helper badly because the token is used as the `Filespec_GetFullPath` path component while the filename remains the fixed template `eusecode.flx`. Current safest interpretation is therefore:
|
||||
|
||||
- pass a directory or resource-root to `-u`
|
||||
- put the replacement archive at `<that root>/EUSECODE.FLX`
|
||||
- do not expect `-u` to open `FLICTEST.FLX` directly as the final archive name
|
||||
|
||||
The current safest syntax examples are:
|
||||
|
||||
- `-u MADDOCODE`
|
||||
- `-u .\MADDOCODE`
|
||||
- `-u \MADDOCODE`
|
||||
- `-u C:\MADDOCODE`
|
||||
|
||||
But for real DOS-facing installs, the safer practical examples are the `8.3`-safe variants:
|
||||
|
||||
- `-u MADDOC~1`
|
||||
- `-u .\MADDOC~1`
|
||||
|
||||
with these current best path interpretations:
|
||||
|
||||
- `MADDOCODE` = relative to the game's current DOS working directory
|
||||
- `.\MADDOCODE` = explicit relative DOS path
|
||||
- `\MADDOCODE` = root-relative path on the current DOS drive
|
||||
- `C:\MADDOCODE` = absolute DOS path on drive `C:` as seen by the DOS game, not automatically the Windows host path
|
||||
|
||||
In the current GOG install used for this investigation, Windows reports the short alias:
|
||||
|
||||
- `MADDOCODE` -> `MADDOC~1`
|
||||
|
||||
That makes `MADDOC~1` the highest-probability next token for this specific setup.
|
||||
|
||||
Current safest negative guidance:
|
||||
|
||||
- avoid forward slashes for `-u`
|
||||
- avoid long path tokens because silent truncation at `1478:065a` can change the real lookup target
|
||||
- do not use the archive filename itself as the `-u` argument
|
||||
- prefer `8.3`-safe directory names or explicit DOS short aliases when testing under DOSBox-like environments
|
||||
|
||||
But the important uncertainty is now only `exact naming rules`, not `whether the switch is real`. In the regular non-Japanese `CRUSADER.EXE`, the switch is clearly still live.
|
||||
|
||||
## Expected Console Output For `-u`
|
||||
|
||||
Current best answer: probably none.
|
||||
|
||||
The recovered `-u` parser case does not print a banner, and the startup helper that probes and swaps the override path also has no recovered `ConsolePrintf` call. So unlike `-debug`, `-warp`, `-skill`, `-mapoff`, `-egg`, or `-demo`, `-u` does not currently appear to advertise itself on the console before the game loads.
|
||||
|
||||
The safest current expectation is:
|
||||
|
||||
- success: no dedicated `-u` console message
|
||||
- missing path/file: no dedicated `-u` console message, likely silent fallback to stock usecode
|
||||
- malformed but existing replacement: no dedicated `-u` status line from the parser/helper path; later behavior depends on the deeper loader/runtime path
|
||||
|
||||
This also sharpens the practical diagnosis for the current `MADDOCODE` setup. In the tested GOG install, only `EUSECODE.FLX` differs between `USECODE` and `MADDOCODE`; the sidecar files are byte-identical. So if replacing `USECODE\EUSECODE.FLX` with the hacked file produces the expected obvious no-usecode-style breakage, but `-u MADDOCODE` behaves like stock gameplay instead, the strongest current read is that the override path was probably not accepted and startup silently fell back to the stock usecode root.
|
||||
|
||||
This also aligns with the already-stronger JP Win32 result, where the matching `-u` lane was recovered as the same kind of usecode override.
|
||||
|
||||
For the deeper runtime-side investigation of whether `-u` replaces or augments the stock usecode root, and what game systems that replacement feeds, see [docs/usecode-startup-override.md](docs/usecode-startup-override.md).
|
||||
|
|
|
|||
|
|
@ -10,9 +10,29 @@ This pass widened the renderer research beyond egg and NPC spawner objects and f
|
|||
- `0x005A`, `0x005B`, `0x005C`, `0x005D`, `0x0066`-`0x0069`: invisible/editor wall objects
|
||||
- `0x01B8`: `camera`
|
||||
- `0x0290`, `0x0336`: `LIGHT_BRIDGE_*`
|
||||
- `0x0251`, `0x0318`, `0x0337`, `0x0361`: placeholder cubes and placeholder UI/editor markers
|
||||
- `0x0337`, `0x0361`: placeholder cubes and placeholder UI/editor markers
|
||||
- `0x0108`, `0x0113`, `0x01B9`, `0x01BA`, `0x025F`, `0x0260`, `0x02F0`, `0x0373`, `0x0399`, `0x03A1`, `0x04C8`: `wallgun_shape_*` helper cluster
|
||||
|
||||
## `0x0251` Frame `0`: `VALUEBOX`
|
||||
|
||||
- The recovered usecode corpus now closes `0x0251` directly as `VALUEBOX` in both retail games, not as a generic placeholder cube.
|
||||
- Current safest read is `local payload box`, not `world prop`: the object stores numeric or text-selection data for nearby authored controllers instead of behaving like a visible gameplay pickup.
|
||||
- The caller side is much clearer than `VALUEBOX.slot_20(...)` itself. Checked bodies that explicitly scan nearby `shape=0x0251` items include:
|
||||
- `MONITNS` (`0x0102`) and `MONITEW` (`0x0165`)
|
||||
- `WALLMNS` (`0x0367`) and Regret `WALLMEW` (`0x0436`)
|
||||
- Regret `SECURNS` (`0x03FB`) and `SECUREW` (`0x043D`)
|
||||
- Regret `WATCHNS` (`0x04C6`) and `WATCHEW` (`0x04DE`)
|
||||
- `KEYPAD` (`0x0A0E` in Remorse, `0x0A0D` in Regret)
|
||||
- Those families usually match the box on shared `QLo`, then call `VALBOX.slot_20(valueBox)` to recover the stored value. Monitor, wall-display, and security-terminal paths also feed `Item.getQHi(valueBox)` into `TEXTFILE.slot_23(...)`, so `QHi` behaves like a second text/value selector rather than dead metadata.
|
||||
- `VALUEBOX::cachein` also hints at a small self-initialization lane: when the decoded value is zero it calls `FREE.slot_20(0x0383)` and writes a replacement through `VALUEBOX.slot_20(...)`. The slot-20 decompilation is still weak, so the conservative read is `possible value initialization`, not a fully closed random-number claim.
|
||||
- Decompressed `.cache` scenes back the helper role strongly:
|
||||
- Remorse retail caches currently expose `299` frame-0 placements. The strongest nearby controller families in the local scan are `MONITEW` (`90` hits), `MONITNS` (`65`), and `WATCHEW` (`18`).
|
||||
- Regret retail caches currently expose `171` frame-0 placements. The strongest nearby controller families are `MONITEW` (`32`), `MONITNS` (`21`), `WATCHEW` (`20`), `SECURNS` (`11`), `SECUREW` (`6`), and `WALLMNS` (`6`).
|
||||
- Most frame-0 boxes still carry `quality = 0`, which is consistent with the common `QLo == 0` default-link path in the callers.
|
||||
- Rare authored payloads do exist: Regret map `29` has a `VALUEBOX` with `quality 23` (`QLo 23`), and Remorse maps `25`, `26`, and `141` all contain a nonzero example at `53118,30558,96` with `quality 828` (`QLo 60`, `QHi 3`, `npcNum 2`).
|
||||
- Many placements occur in small chained local clusters through `nextItem`, which fits a backing-store/helper role better than a standalone scene-prop interpretation.
|
||||
- Practical renderer implication: `0x0251` should be labeled `VALUEBOX`, should keep `QLo`, `QHi`, and `nextItem` visible in tooltips, and should be treated as a local controller payload object rather than as a generic placeholder cube.
|
||||
|
||||
## What This Means For The Renderer
|
||||
|
||||
- A lot of the useful information is already present without more reverse-engineering. The main problem was presentation, not raw data availability.
|
||||
|
|
@ -46,7 +66,26 @@ The tooltip now exposes generalized metadata for editor/helper objects instead o
|
|||
|
||||
- Add dedicated filters or list views for helper subfamilies such as invisible walls, camera markers, light bridges, and placeholder cubes.
|
||||
- Add shape-family frequency summaries so repeated helper markers can be audited across a map.
|
||||
- Decode more shape-specific field semantics for the still-unresolved editor objects, especially the remaining non-promoted invisible-wall, camera/helper, and music-controller families, and keep folding any new results back into the dedicated USECODE-link note.
|
||||
- Decode more shape-specific field semantics for the still-unresolved editor objects, especially the remaining non-promoted invisible-wall, camera/helper, music-controller, and secret-door-switch families, and keep folding any new results back into the dedicated USECODE-link note.
|
||||
- Find the No Regret replacement for the Remorse `0x024F` monster-egg workflow instead of assuming the same shape is reused.
|
||||
|
||||
## Newly Promoted Regret-Only Controllers
|
||||
|
||||
- `0x0318` is now promoted as `CRUMORPH`, not a blank placeholder cube. The recovered `equip` body scans nearby NPCs for a shared internal control key derived from the item's `QLo`, temporarily transfers player control to the first live match, and then brackets `TRIGGER.slot_20` with success or failure lanes.
|
||||
- `0x0366` remains `NPC_ONLY`, but the latest decompressed `.cache` sweep tightens its practical viewer behavior: actor-target arrows are still not justified, while cautious local `NPC_ONLY -> 0x04B1` same-`QLo` arrows are now strong enough to expose.
|
||||
- `0x04c6` / `0x04de` are now promoted as `WATCHNS` / `WATCHEW`, not generic editor leftovers. Their recovered `slot_20` bodies scan nearby `0x0510` posts by shared `QLo` and then bracket `TRIGGER.slot_20` around a watcher-specific follow-up lane.
|
||||
- `0x0510` is now better treated as a `SECRET_DOOR_POST` helper target rather than an unresolved standalone controller. The strongest current viewer behavior is a cautious local arrow from `WATCHNS` / `WATCHEW` plus tooltip decoding of its `QLo`/`QHi` bytes.
|
||||
- `0x05e1` is closed as `CRYOBOX`, and nearby `0x05df` / `0x05e0` pressure-barrier faces are now promoted out of the unresolved bucket as local arrow targets keyed by shared `QLo`.
|
||||
- `0x0451` / `0x05ae` are now closed as `CRAZYEW` / `CRAZYNS`, small Regret-only hit-driven NPC wake-up relays rather than vague contextual map labels.
|
||||
- `0x056d` is now closed as `VIDEOBOX`, a gated controller with a direct `equip` body, even though its higher-level gameplay meaning is still less explicit than the watcher and cryobox lanes.
|
||||
|
||||
## Actor-Key Family Follow-Up
|
||||
|
||||
- The latest actor-link follow-up did not justify exporting a stable `NPC_ONLY -> actor` or `CRUMORPH -> actor` overlay from static map/cache data alone.
|
||||
- Current best read is that the compared value is mutable actor field `0x63`, not a DTABLE row and not a field that the current scene export already carries.
|
||||
- A direct Regret DTABLE row check at byte offset `0x63` is not enough to rescue that idea: sampled rows still read as zero there, so the actor key is not simply a persistent `NPCDat` attribute that can be copied through the existing preview pipeline.
|
||||
- The same corpus also shows why the field is unstable: `TRIGGER.slot_29` / `slot_2B` can rewrite actor field `0x63` on nearby matched NPCs, so the relevant actor-side link id can change after startup.
|
||||
- The broader family using this same hidden actor-key mechanism is now clearer though: `CRUMORPH`, `NPC_ONLY`, `WATCHNS`, `WATCHEW`, `THRMBCKN`, `THRMBCKE`, `SURCAMNS`, and `SURCAMEW` all compare controller-local bytes against actor field `0x63` in one of their recovered lanes.
|
||||
- Practical renderer stance stays conservative: keep only the already-evidenced local helper arrows in the overlay, and surface the actor-key behavior in metadata/tooltips until a runtime or spawn-time export can close field `0x63` directly.
|
||||
|
||||
`0x04B1` now has a stable `CMD_LINK -> TRIGGER.slot_20` viewer target, and `0x04E3` is already promoted as `SKILLBOX::equip`, so they no longer belong in the unresolved editor-object bucket here.
|
||||
|
|
@ -223,12 +223,15 @@ Scene evidence lines up with that lane in No Remorse map `1`:
|
|||
|
||||
So the public renderer now treats same-map `shape:542` frame-`0` placements as elevator-link sources when their checked `QLo` values map to a local destination egg id.
|
||||
|
||||
## Current Elevator Gap
|
||||
No Regret now closes one additional elevator lane that the earlier Remorse-only rule missed.
|
||||
|
||||
- No Regret map `3` destination egg `102` does not sit on the same currently verified `ELEVATOR` (`shape:542`) or `LIFT` (`shape:307`) lanes.
|
||||
- Current scene exports for that map show no nearby `shape:542` or `shape:307` objects at all.
|
||||
- A nearby editor/helper record `item:1056:fixed:1201:0:41758:33694:0` carries `mapNum 102`, but there is not yet enough executable-side evidence to promote that into a viewer arrow rule.
|
||||
- The map renderer therefore leaves egg `102` unresolved for now instead of hardcoding a speculative elevator source pattern.
|
||||
- Regret also ships a separate elevator family at `shape:400` (`0x0190`), which the current shape catalog leaves unnamed but the recovered usecode closes as `ELEVATOR` through `ELEVATOR::slot_20`'s local `nearby_items(shape=0x0190, origin=global[0x001e])` scan.
|
||||
- In Regret `ELEVATOR::gotHit`, the gate is different from the Remorse `shape:542` body: the object must have `QLo >= 100`, and the generic local-elevator lane is `QLo < 0x00c8`.
|
||||
- That generic Regret lane dispatches `ELEVATOR::slot_20` with the current map and the source object's `QLo` as the destination egg id, while `QHi` still carries the direction/state argument.
|
||||
- Regret map `3` now has a concrete same-map source for destination egg `102`: `item:664:fixed:400:0:44030:9662:0` with `quality 614` (`QLo 102`, `QHi 2`).
|
||||
- Current scene exports for that map still show no `shape:542` or `shape:307` objects at all near destination egg `102`, and the nearby `0x04B1` cmd-link helpers instead carry unrelated local keys such as `7`, `9`, `41`, and `60`.
|
||||
|
||||
So the old Regret map-`3` egg-`102` gap is now closed: it is a same-map Regret `shape:400` elevator destination, not an unresolved `shape:542`/`shape:307` case and not a `0x04B1` helper lane.
|
||||
|
||||
## Adjacent Editor Objects: `0x04E3` And `0x04B1`
|
||||
|
||||
|
|
@ -601,9 +604,9 @@ Current best read:
|
|||
- `0x0366` frame `0` is an idle NPC-only trigger pad
|
||||
- `QLo` is the author-selected NPC-group key, but the executable compares it against actor field `0x63`, not against the scene-export `npcNum` or a simple DTABLE row
|
||||
- current scene exports do not expose that actor-side field directly, so there is not yet enough evidence to draw trustworthy `NPC_ONLY -> actor` arrows in the renderer
|
||||
- a follow-up scene-cache sweep also failed to produce a convincing generic `NPC_ONLY -> 0x04B1` helper pattern; shared-`QLo` local matches look incidental rather than like a dedicated authored link lane
|
||||
- a decompressed `.cache` scene sweep does show repeated nearby same-`QLo` `0x04B1` cmd helpers in authored Regret maps, so the renderer can now safely expose cautious `NPC_ONLY -> cmd QLo` arrows while still keeping `NPC_ONLY -> actor` arrows out of the overlay
|
||||
|
||||
That means the safe editor improvement here is naming and field emphasis, not a target-arrow rule yet.
|
||||
That means the safe editor improvement here is `NPC_ONLY` naming plus local `cmd QLo` arrows only; the actor-target lane still remains tooltip-only.
|
||||
|
||||
### `0x0403` frame `0`: `FLAMEBOX`
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ The implementation uses extracted `class_event_index.tsv` results plus existing
|
|||
| `MONITNS` (`0x0102`) | `MONITNS::use` (`slot 0x01`) | Existing gameplay notes tie shape `258` / `0x0102` to a live monitor/computer-adjacent use handler, making it a strong non-editor first-view script target. |
|
||||
| `MONITEW` (`0x0165`) | `MONITEW::use` (`slot 0x01`) | Disasm crosswalks shape `0x0165` to the east-west monitor variant, which keeps the same live computer-adjacent use handler family. |
|
||||
| `PANELNS` (`0x00A1`) | `PANELNS::use` (`slot 0x01`) | Verified panel-switch wrapper for the same nearby trigger-helper chain. |
|
||||
| `CRUMORPH` (`0x0318`) | `CRUMORPH::equip` (`slot 0x0A`) | Recovered control-transfer pad body scans nearby NPCs for a local-`QLo` control key match, temporarily hands control to the first live hit, and then dispatches `TRIGGER.slot_20` lane `0` or `1`. |
|
||||
| `NPCTRIG` (`0x0363`) | `NPCTRIG::equip` (`slot 0x0A`) | Crosswalked shape/class match; the compact slot-`0x0A` body is still the strongest active-event frontier for this trigger family. |
|
||||
| `CRUZTRIG` (`0x0365`) | `CRUZTRIG::gotHit` (`slot 0x06`) | Disasm crosswalks shape `0x0365` to CRUZTRIG, and `gotHit` is the recovered live body for this trigger/helper family. |
|
||||
| `VMAIL` (`0x0367`) | `VMAIL::slot_0a` (`slot 0x0A`) | Disasm crosswalks shape `0x0367` to VMAIL; slot `0x0A` is the active helper body even though its final semantic label is still weaker than the slot number. |
|
||||
|
|
@ -27,7 +28,7 @@ The implementation uses extracted `class_event_index.tsv` results plus existing
|
|||
| `TIMER` (`0x04C9`) | `TIMER::enterFastArea` (`slot 0x0F`) | Fast-area timer helper; the first active body arms slot `0x20` from qHi enter/leave flags and the packed `mapNum:npcNum` delay payload. |
|
||||
| `SPECIAL` (`0x04CA`) | `SPECIAL::enterFastArea` (`slot 0x0F`) | Fast-area phase helper; the active entry body reads `mapNum` / `npcNum` as phase bytes and `qHi` as the delay byte before fanning out through `TRIGGER.slot_20` and `SPECIAL.slot_21`. |
|
||||
| `TRIGPAD` (`0x04CD`) | `TRIGPAD::gotHit` (`slot 0x06`) | Occupancy/surface-gated trigger-pad logic lives in the recovered `gotHit` body. |
|
||||
| `NPC_ONLY` (`0x0366`) | `NPC_ONLY::gotHit` (`slot 0x06`) | Active hit-driven helper lane from the extracted class/event table. |
|
||||
| `NPC_ONLY` (`0x0366`) | `NPC_ONLY::gotHit` (`slot 0x06`) | Active hit-driven helper lane; the body gates on an NPC-only actor key, then brackets `TRIGGER.slot_20` lane `0` / `1` from the pad itself. |
|
||||
| `FLAMEBOX` (`0x0403`) | `FLAMEBOX::equip` (`slot 0x0A`) | Recovered flame-controller body scans nearby flame helper shapes by shared `QLo` and can swap helper markers into live flame actors. |
|
||||
| `SFXTRIG` (`0x04E2`) | `SFXTRIG::slot_0a` (`slot 0x0A`) | Disasm crosswalks shape `0x04E2` to the compact event-bearing SFXTRIG helper; slot `0x0A` is the stable active body even though a precise semantic label is still weaker than the slot number. |
|
||||
| `DEATHBOX` (`0x04E7`) | `DEATHBOX::slot_0a` (`slot 0x0A`) | The recovered helper body matches death-link `QLo` and forwards NPC death events into `TRIGGER` lanes, so opening the helper body is now more useful than leaving the shape unmapped. |
|
||||
|
|
@ -52,8 +53,26 @@ That is why the viewer opens `TRIGGER.slot_20` for pinned `0x04B1` helpers inste
|
|||
- Pinned controller objects and the small set of promoted gameplay objects now expose a `USECODE` action in the tooltip.
|
||||
- The action switches the workspace to the USECODE tab.
|
||||
- The USECODE viewer resolves the exact class/slot target against the generated cache index instead of relying on fuzzy filename search.
|
||||
- `CRUMORPH` and `NPC_ONLY` now also participate in the same cautious local `... -> cmd QLo ...` overlay rule used for other `TRIGGER.slot_20` controller families, but only for nearby `0x04B1` helpers that actually share the source object's low `quality` byte.
|
||||
- `0x0011` usecode-trigger eggs now decode their `npcNum` nibble-packed X/Y ranges, resolve `QLo` into the authored family-4 class, open the matching subtype body in the USECODE tab, and draw arrows only for the narrower subtype families whose local target scans are actually recovered.
|
||||
|
||||
## Actor-Key Family Blocker
|
||||
|
||||
- The current static scene/cache export still cannot support trustworthy `controller -> actor` arrows for the Regret actor-key family.
|
||||
- The strongest current reason is that the compared value is mutable actor field `0x63`, not a stable DTABLE row or an already-exported scene field.
|
||||
- A direct Regret DTABLE byte check on record offset `0x63` is not enough to close that gap: sampled rows are still zero there, so the actor key is not just a plain `NPCDat` byte copied into the runtime actor.
|
||||
- The same recovered corpus shows why the value is unstable: `TRIGGER.slot_29` / `slot_2B` can rewrite actor field `0x63` on nearby matched NPCs, which means the practical link id can change after the map loads.
|
||||
- Current safest viewer stance is therefore: keep actor-key families named and tooltip-decoded, allow only the already-evidenced local helper arrows, and leave actor-target arrows disabled until a runtime or spawn-time export closes field `0x63` directly.
|
||||
|
||||
### Known Actor-Key Families
|
||||
|
||||
- `CRUMORPH` (`0x0318`) compares nearby actor field `0x63` against the pad `QLo` before transferring control and bracketing `TRIGGER.slot_20`.
|
||||
- `NPC_ONLY` (`0x0366`) compares the incoming NPC-like source's actor field `0x63` against the pad `QLo` before bracketing `TRIGGER.slot_20` lane `0` / `1`.
|
||||
- `WATCHNS` / `WATCHEW` (`0x04c6` / `0x04de`) have a stronger current local `0x0510` post lane in the viewer, but their deeper watcher body also checks nearby actor field `0x63` against controller `QLo`.
|
||||
- `THRMBCKN` / `THRMBCKE` (`0x0566` / `0x0567` classes) compare nearby Thermatron actor field `0x63` against controller `QLo`.
|
||||
- `SURCAMNS` / `SURCAMEW` also scan nearby NPCs by actor field `0x63` and controller `QLo` in their camera/control lane.
|
||||
- `TRIGGER.slot_29` / `slot_2B` are part of the same ecosystem because one subcommand explicitly rewrites actor field `0x63` on matched nearby NPCs.
|
||||
|
||||
`0x04F8` remains intentionally outside the `USECODE` target list for now. The current evidence says it is a destroyable-door helper scanned by `DOOR.slot_23`, not a proven standalone usecode class the viewer should open directly.
|
||||
|
||||
## Newly Decoded Field Notes
|
||||
|
|
@ -157,14 +176,24 @@ No currently unresolved Remorse-only editor rows remain in this note after the `
|
|||
|---|---|---|
|
||||
| `0x00cf` | `HAND` | Needs examination for usecode-link integration |
|
||||
| `0x01d6` | `MUTANT_HOOK_CONTROL` | Needs examination for usecode-link integration |
|
||||
| `0x0451` | `GIMP_DISPENSER` | Needs examination for usecode-link integration |
|
||||
| `0x0510` | `SECRET_DOOR_POST` | Needs examination for usecode-link integration |
|
||||
| `0x0451` | `CRAZYEW` | Integrated: `CRAZYEW::gotHit` as a Regret-only NPC wake-up relay; tooltip now treats it as a hit-driven controller rather than a generic editor placeholder. |
|
||||
| `0x0510` | `SECRET_DOOR_POST` | Integrated as a local arrow target for nearby `WATCHNS` / `WATCHEW` controllers that match it by `QLo`; no separate direct usecode body promoted yet. |
|
||||
| `0x0548` | `SECRET_DOOR_SWITCH` | Needs examination for usecode-link integration |
|
||||
| `0x056d` | `STEAM_COLLISION_SWITCH` | Needs examination for usecode-link integration |
|
||||
| `0x05ae` | `VOLCANO_CONTROLLER` | Needs examination for usecode-link integration |
|
||||
| `0x05df` | `PRESSURE_BARRIER_V` | Needs examination for usecode-link integration |
|
||||
| `0x05e0` | `PRESSURE_BARRIER_H` | Needs examination for usecode-link integration |
|
||||
| `0x05e1` | `PRESSURE_BARRIER_SWITCH` | Needs examination for usecode-link integration |
|
||||
| `0x056d` | `VIDEOBOX` | Integrated: `VIDEOBOX::equip` as the recovered Regret-only gated controller body. |
|
||||
| `0x05ae` | `CRAZYNS` | Integrated: `CRAZYNS::gotHit` as a Regret-only NPC wake-up relay; tooltip now treats it as a hit-driven controller rather than a generic editor placeholder. |
|
||||
| `0x05df` | `PRESSURE_BARRIER_V` | Integrated as a local arrow target for nearby `CRYOBOX` controllers that match it by `QLo`. |
|
||||
| `0x05e0` | `PRESSURE_BARRIER_H` | Integrated as a local arrow target for nearby `CRYOBOX` controllers that match it by `QLo`. |
|
||||
| `0x05e1` | `CRYOBOX` | Integrated: `CRYOBOX::equip` plus local `QLo` arrows to nearby `0x05DF` / `0x05E0` pressure-barrier faces. |
|
||||
|
||||
### Regret-Only Batch: Watchers, Cryobox, And Wake-Up Relays
|
||||
|
||||
- `0x04c6` and `0x04de` are no longer anonymous shared editor rows in Regret scenes. The recovered corpus names them `WATCHNS` and `WATCHEW`, and both `slot_20` bodies scan nearby `shape=0x0510` placements before bracketing `TRIGGER.slot_20` around their watcher-specific follow-up lane.
|
||||
- The scene-cache cross-check supports a cautious viewer arrow rule here. Across Regret maps `1`, `10`, `13`, `14`, `15`, `16`, `18`, `200`, `201`, `215`, `29`, `30`, and others, placed `WATCHNS` / `WATCHEW` objects repeatedly sit within local helper range of `0x0510` posts and share the same low quality byte even when the raw 16-bit quality differs.
|
||||
- `0x0510` therefore belongs in the editor as a local secret-door post/helper target, not as an unresolved generic editor placeholder. The recovered watcher body only treats `qHi == 0` posts as the text/door-side lane, so the current viewer promotion stays conservative and only adds the local arrow plus tooltip decoding.
|
||||
- `0x05e1` is now closed as `CRYOBOX`, not a vague pressure-barrier switch. Its `equip` body matches nearby `0x05DF` and `0x05E0` shapes by shared `QLo`, then hands off into `slot_20` / `slot_21` worker lanes that wait on animation state, flip `ITEM` control slots, and spawn the steam worker path.
|
||||
- The paired faces `0x05DF` and `0x05E0` remain useful human-facing labels as `PRESSURE_BARRIER_V` and `PRESSURE_BARRIER_H`, but they no longer belong in the unresolved bucket. In the viewer they are now arrow targets of nearby `CRYOBOX` controllers instead of unlabeled editor debris.
|
||||
- `0x0451` and `0x05AE` are now closed as `CRAZYEW` and `CRAZYNS`. The recovered `gotHit` bodies are small but concrete: when the incoming hit source is an actor handle (`>= 0x00FF`), they check `NPC.slot_2A` and, unless the target is already in activity `12`, spawn `NPC.slot_2C` to wake or re-arm that actor. That is enough to classify them as hit-driven NPC wake-up relays rather than dispensers or volcano-only map art.
|
||||
- `0x056D` is also no longer an unresolved `STEAM_COLLISION_SWITCH`. The recovered class is `VIDEOBOX`, and its `equip` body is a thin global-latch gate that either falls straight into `ITEM.slot_21` or runs a short scripted helper loop first. That is enough for a direct usecode-view target even though the higher-level gameplay meaning is still thinner than the `WATCH*` and `CRYOBOX` lanes.
|
||||
|
||||
## Remaining Steps
|
||||
|
||||
|
|
@ -173,14 +202,14 @@ The next map-viewer USECODE passes should stay evidence-backed and prioritize it
|
|||
### Highest Priority
|
||||
|
||||
1. Extend the `0x0011` subtype table beyond the currently promoted `TRIGEGG` / `ONCEEGG`, `FLOOR1`, `MHATCHER`, `DOOREGG`, `MISS1*`, and `VIDEOEGG` lanes only when the recovered pseudocode justifies a reusable viewer target or arrow rule.
|
||||
2. Do the `SURCAMNS` / `SURCAMEW` placement crosswalk so the renderer can decide whether placed `0x04c6` / `0x04de` objects deserve a direct USECODE jump or should remain callback-holder-only families.
|
||||
2. Revisit the remaining Regret-only door-side helpers around `WATCHNS` / `WATCHEW`, especially `0x0548`, to decide whether they form a second stable secret-door lane beyond the now-promoted `0x0510` post targets.
|
||||
3. Finish the remaining `CMD_LINK` field write-up: the current tooltip now decodes `quality`, `mapNum`, and `npcNum`, but `nextItem` still lacks a stable standalone semantic beyond appearing in authored controller records.
|
||||
|
||||
### Catalog And Viewer Cleanup
|
||||
|
||||
1. Sweep the remaining shared editor/controller shapes in the catalog table and promote the next solid names instead of leaving `(unnamed)` placeholders where the disasm or extracted corpus already gives a stable class anchor.
|
||||
2. Revisit the `0x05b2`-`0x05be` music-controller cluster and decide whether those shapes belong in the USECODE viewer backlog, a scene-audio note, or both.
|
||||
3. Recheck the Regret-only controller shapes (`HAND`, `MUTANT_HOOK_CONTROL`, `GIMP_DISPENSER`, `SECRET_DOOR_*`, `STEAM_COLLISION_SWITCH`, `VOLCANO_CONTROLLER`, `PRESSURE_BARRIER_*`) against the extracted class/event table before adding any direct links.
|
||||
3. Recheck the remaining unresolved Regret-only controller shapes (`HAND`, `MUTANT_HOOK_CONTROL`, `SECRET_DOOR_SWITCH`) against the extracted class/event table before adding any direct links.
|
||||
|
||||
### Gameplay Coverage Extensions
|
||||
|
||||
|
|
|
|||
|
|
@ -137,8 +137,8 @@ The broader Remorse catalog does contain more unused-looking or obviously non-re
|
|||
|
||||
### Placeholder cube family
|
||||
|
||||
- The strongest additional item-adjacent candidates from the catalog/scene pass are placeholder cube entries:
|
||||
- `0x0251` = `PLACEHOLDER_KEY_CUBE`
|
||||
- `0x0251` no longer fits the generic placeholder bucket well. Current best read is `VALUEBOX`, a local data/helper box used by monitors, watcher panels, security displays, and keypads.
|
||||
- The strongest remaining placeholder-cube entries from the catalog/scene pass are:
|
||||
- `0x0318` = `PLACEHOLDER_CUBE`
|
||||
- `0x0337` = `PLACEHOLDER_CUBE_BIG`
|
||||
- `0x0361` = `PLACEHOLDER_CUBE_RED_BLACK`
|
||||
|
|
|
|||
|
|
@ -26,6 +26,76 @@ Purpose:
|
|||
| Constructor writes `0x65ab` to object `+0` | `1408:0024..0028` |
|
||||
| `1478:65ab` is method 0 and `1478:65af` is method 1 of the same vtable | `CALLF [BX]` and `CALLF [BX+4]` dispatch paths |
|
||||
|
||||
## Minimum Viable Patch Floor (2026-04-03)
|
||||
|
||||
Fresh live-Ghidra re-checks tighten the lower bound on what an executable-side debugger-unlock patch must do.
|
||||
|
||||
What the live binary still proves:
|
||||
- `1408:0000` is only a constructor. It allocates/initializes the seg1408 debugger object, seeds object `+0` with `0x65ab`, and returns the far pointer in `DX:AX`; it does **not** store that pointer into `1478:659c/659e`.
|
||||
- current instruction searches still show reads of `1478:659c/659e` in seg13a0 and seg1418, but no recovered retail writer that seeds those globals before the interpreter hook runs.
|
||||
- raw debugger-adjacent data at `1478:65ab/65af` still resolves to the two inert retail callbacks `1408:046f` and `1408:0474`; there is no hidden already-live UI-opening target sitting behind the stock vtable.
|
||||
- the interpreter-side pre-call guard at `1418:049e..04b5` only checks whether `1478:659c/659e` is non-null before calling `1408:0053`. The one-shot breakpoint/step gating lives inside `1408:0053`, not at the callsite itself.
|
||||
- both UI wrappers still inherit caller words at `13a0:008f` / `13a0:024a`, so any deferred entry that reuses those wrappers from a non-native caller still needs wrapper-argument sanitization.
|
||||
|
||||
That rules out the common "there must be one tiny direct jump" theories:
|
||||
- a constructor-only retarget is insufficient because the returned far pointer still has to be stored into `1478:659c/659e` somewhere.
|
||||
- a vtable-dword-only patch is insufficient because the retail callbacks at `1478:65ab/65af` are inert shared stubs, and the previously tested shared-slot rewrites already crashed at startup.
|
||||
- a direct `1418:04b5 -> 13a0:020d/0086` retarget is insufficient because once `1478:659c/659e` becomes non-null, that callsite would fire on every eligible interpreter pass unless a private one-shot stub preserves the original `1408:0053` gating semantics.
|
||||
|
||||
Current best conclusion:
|
||||
- the smallest structurally defensible patch family is still the current interpreter-callsite-retarget design (Candidates O/P): one embedded `13e8:230d..232d` body that lazily creates/stores/arms the debugger object, one retarget of `1418:04b5` into the private stub, and one wrapper-argument sanitization site (`13a0:024a` or `13a0:008f`).
|
||||
- anything smaller than that is currently missing at least one required behavior: object creation, global pointer seeding, one-shot deferred gating, or safe wrapper arguments.
|
||||
|
||||
## Practical Candidate Byte Maps
|
||||
|
||||
These are the current live-candidate edits in practical patching terms.
|
||||
|
||||
Important NE note:
|
||||
- for internal far calls, the on-disk opcode bytes stay as placeholder `FF FF 00 00`
|
||||
- the real old/new target change is in the NE fixup entry, not in the immediate bytes themselves
|
||||
- so for those sites below, `raw bytes` may be unchanged while the `fixup target` changes
|
||||
|
||||
### Common edits shared by Candidate O and Candidate P
|
||||
|
||||
| File offset | Live address | What changes | Old value | New value |
|
||||
|-------------|--------------|--------------|-----------|-----------|
|
||||
| `0x0C970D` | `13e8:230d` | Replace the retail `0x410` body with the corrected private bootstrap/stub body | `A0 4F 60 B4 00 F7 D8 1B C0 40 A2 4F 60 80 3E 4F 60 00 74 47 6A FF 6A FF C4 1E D0 4C 26 8A 47 05 50 1E 68 D2 60 6A 00 6A 00 83 EC 06 C7 86 76 FF 00 00 8B 86 76 FF F7 D0 89 86 78 FF C6 86 7A FF 00 6A 00 6A 00 9A FF FF 00 00 83 C4 14 52 50 9A FF FF 00 00 83 C4 08 5F 5E C9 CB 6A FF 6A FF C4 1E D0 4C 26 8A 47 05 50 1E 68 EE 60 6A 00 6A 00 83 EC 06 C7` | `A1 9C 65 0B 06 9E 65 74 10 C4 1E 9C 65 C6 47 75 00 C6 47 74 01 5F 5E C9 CB 6A 00 6A 00 E9 25 00 55 8B EC A1 9C 65 39 46 06 75 16 A1 9E 65 39 46 08 75 0E C4 5E 06 C7 47 74 00 00 6A 00 6A 00 EB 0E 5D CB 90 90 9A FF FF 00 00 83 C4 04 EB 0A 9A FF FF 00 00 83 C4 04 5D CB 0B C2 74 13 A3 9C 65 89 16 9E 65 89 C3 8E C2 C6 47 75 00 C6 47 74 01 5F 5E C9 CB` |
|
||||
| `0x0C9753` | `13e8:2352` | First reused far-call fixup inside the patched `0x410` body | fixup target `1350:0046` | fixup target `1408:0000` |
|
||||
| `0x0CFAB5` | `1418:04b5` | Interpreter debugger callsite retarget | raw bytes `9A FF FF 00 00`; fixup target `1408:0053` | raw bytes `9A FF FF 00 00`; fixup target `13e8:232d` |
|
||||
|
||||
### Candidate O only
|
||||
|
||||
| File offset | Live address | What changes | Old value | New value |
|
||||
|-------------|--------------|--------------|-----------|-----------|
|
||||
| `0x0C975D` | `13e8:235c` | Second reused far-call fixup inside the patched `0x410` body | raw bytes `FF FF 00 00`; fixup target `1320:1588` | raw bytes `FF FF 00 00`; fixup target `13a0:020d` |
|
||||
| `0x0B9C48` | `13a0:0248` | Zero inherited modal-wrapper caller words while preserving the leading local default `PUSH 0` | `6A 00 FF 76 08 FF 76 06` | `6A 00 6A 00 6A 00 90 90` |
|
||||
|
||||
Practical meaning:
|
||||
- the patched `0x410` body now creates/stores the debugger object if needed, or reuses the live one and arms break-next
|
||||
- the interpreter callback at `1418:04b5` no longer enters `1408:0053` directly; it enters the private stub at `13e8:232d`
|
||||
- that private stub eventually uses the repointed second far-call slot at `13e8:235c` to open `usecode_debugger_open_modal`
|
||||
|
||||
### Candidate P only
|
||||
|
||||
| File offset | Live address | What changes | Old value | New value |
|
||||
|-------------|--------------|--------------|-----------|-----------|
|
||||
| `0x0C975D` | `13e8:235c` | Second reused far-call fixup inside the patched `0x410` body | raw bytes `FF FF 00 00`; fixup target `1320:1588` | raw bytes `FF FF 00 00`; fixup target `13a0:0086` |
|
||||
| `0x0B9A8D` | `13a0:008d` | Zero inherited current-unit-wrapper caller words while preserving the leading mode byte `PUSH 1` | `6A 01 FF 76 08 FF 76 06` | `6A 01 6A 00 6A 00 90 90` |
|
||||
|
||||
Practical meaning:
|
||||
- same bootstrap and interpreter-callsite retarget as Candidate O
|
||||
- the only behavioral difference is the final UI target: this one routes to `usecode_debugger_open_for_current_unit` instead of the generic modal wrapper
|
||||
|
||||
### Restore values for the current live-candidate family
|
||||
|
||||
If reverting O/P back to retail, these are the practical targets:
|
||||
- `0x0C970D` / `13e8:230d`: restore the original retail `0x410` body bytes shown above
|
||||
- `0x0C9753` / `13e8:2352`: restore fixup target `1350:0046`
|
||||
- `0x0CFAB5` / `1418:04b5`: restore fixup target `1408:0053`
|
||||
- `0x0C975D` / `13e8:235c`: restore fixup target `1320:1588`
|
||||
- `0x0B9C48` / `13a0:0248`: restore `6A 00 FF 76 08 FF 76 06` if Candidate O was active
|
||||
- `0x0B9A8D` / `13a0:008d`: restore `6A 01 FF 76 08 FF 76 06` if Candidate P was active
|
||||
|
||||
## Attempt Log
|
||||
|
||||
| ID | Patch shape | Mechanical result | Runtime result | Verdict |
|
||||
|
|
|
|||
|
|
@ -842,4 +842,12 @@ That gets to a reversible editor sooner than waiting for a full semantic VM reco
|
|||
- **Renderer Fix:**: [src/lib/usecode-decompiler.js](k:/ghidra/crusader_map_viewer/map_renderer/src/lib/usecode-decompiler.js) now recognizes that stacked-shape whitelist selector and emits readable loops such as `for roof in nearby_items(shapes=[...], distance=(100 * 32), origin=arg_06)` instead of collapsing back to `while (condition)`.
|
||||
- **Readability Impact:**: The cached Remorse and Regret `CHANGER.slot_07` pseudocode bodies now expose the actual nearby-roof selector inputs directly: the hardcoded roof-shape whitelist, the recovered `3200`-unit range, and the egg-origin scan. That makes the later `Item.getQLo(...) == eggId` destroy branch legible without a raw-byte fallback pass.
|
||||
- **Editor Impact:**: The same selector close justified promoting Regret `QLo 8 -> CHANGER` from tooltip-only metadata into the map editor overlay. The viewer can now expose the same local roof-target lane for Regret that was already proven for Remorse, using the recovered Regret whitelist and the same `3200`-unit scan distance.
|
||||
- **Regression Coverage:**: [scripts/test-usecode-structuring.mjs](k:/ghidra/crusader_map_viewer/map_renderer/scripts/test-usecode-structuring.mjs) now adds one synthetic regression for the `loopscr 0x24/0x4c` stacked-shape selector and one real-data regression for Regret `CHANGER.slot_07`, so future renderer changes fail if this selector family falls back to opaque loop output again.
|
||||
- **Regression Coverage:**: [scripts/test-usecode-structuring.mjs](k:/ghidra/crusader_map_viewer/map_renderer/scripts/test-usecode-structuring.mjs) now adds one synthetic regression for the `loopscr 0x24/0x4c` stacked-shape selector and one real-data regression for Regret `CHANGER.slot_07`, so future renderer changes fail if this selector family falls back to opaque loop output again.
|
||||
|
||||
## **Recent Renderer Work (2026-04-03, switch literal normalization)**
|
||||
|
||||
- **Root Cause Closed:**: `FLICTEST.slot_20` exposed a literal-formatting seam in the JS renderer. The compare ladder crosses the byte/word immediate boundary at `127 -> 128`, so the decompiler was structuring the ladder into one `switch` but was preserving the raw operand spelling from each compare. That produced mixed case labels such as `case 127:` followed by `case 0x0080:` and `case 0x0081:` even though the selector is one small ordinal lane.
|
||||
- **Renderer Fix:**: [src/lib/usecode-decompiler.js](k:/ghidra/crusader_map_viewer/map_renderer/src/lib/usecode-decompiler.js) now normalizes switch case labels after selector-chain recovery. When every case label is a non-negative integer in the small ordinal range (`0..255`), the renderer canonicalizes the labels to decimal before emitting the final `switch`.
|
||||
- **Heuristic Boundary:**: The normalization is intentionally narrow. It applies only to structured `switch` output, and only when every case label parses as a small ordinal integer. Larger identifier-like constants such as shape ids (`0x053a`) are left in hex so the renderer does not erase domain-significant formatting.
|
||||
- **Readability Impact:**: FLICTEST-style keypad/movie dispatch ladders now render as consistent ordinal switches across the byte/word immediate boundary, so cases `128+` appear as `128`, `129`, and so on rather than width-leaking `0x0080`, `0x0081`, etc.
|
||||
- **Regression Coverage:**: [scripts/test-usecode-structuring.mjs](k:/ghidra/crusader_map_viewer/map_renderer/scripts/test-usecode-structuring.mjs) now includes one synthetic regression for the mixed `127/0x0080/0x0081` selector ladder and one guard that keeps larger ID-style switch cases in hex.
|
||||
|
|
@ -53,6 +53,145 @@ The remaining uncertainty is narrower now:
|
|||
- whether it can also be a loader alias/resource root that `Filespec_GetFullPath` understands
|
||||
- whether relative, absolute, and CD-backed forms are all accepted equally
|
||||
|
||||
What is now materially tighter from the live `Filespec_GetFullPath` and `File_Exists` implementations:
|
||||
|
||||
- the path is interpreted with DOS file APIs, not Windows host-path APIs
|
||||
- the builder uses the literal separator string `"\\"`, not `"/"`
|
||||
- the parser-side `s_usecode` buffer is only `0x1e` bytes long, so the raw `-u` token is truncated to at most `29` visible characters plus the terminator
|
||||
|
||||
That makes the safest current syntax guidance:
|
||||
|
||||
- prefer DOS-style backslashes, not forward slashes
|
||||
- prefer short path tokens
|
||||
- assume the path must exist inside the game's DOS-visible filesystem view
|
||||
|
||||
If the game is running under DOSBox or a similar DOS environment, `C:\MADDOCODE` refers to the guest DOS `C:` drive, not automatically to the Windows host `C:` drive.
|
||||
|
||||
One more practical constraint is now strong enough to call out explicitly:
|
||||
|
||||
- the `-u` existence probe uses an old DOS find-first style path, so `8.3`-safe directory names are the safest override roots
|
||||
|
||||
For the current GOG install used in this investigation, Windows reports the short alias:
|
||||
|
||||
- `MADDOCODE` -> `MADDOC~1`
|
||||
|
||||
So the highest-probability next token for this specific setup is:
|
||||
|
||||
- `-u MADDOC~1`
|
||||
|
||||
or the explicit relative form:
|
||||
|
||||
- `-u .\MADDOC~1`
|
||||
|
||||
## Why `-u FLICTEST.FLX` Fails
|
||||
|
||||
The live helper shape is now tight enough to explain the failed `FLICTEST` attempts directly.
|
||||
|
||||
What retail `CRUSADER.EXE` does at `1420:0cf0..0d33` is:
|
||||
|
||||
- read the copied argv token from `1478:065a`
|
||||
- load the fixed filename template through `1478:06d6/06d8 -> 1478:07a0`
|
||||
- use that template as `eusecode.flx`
|
||||
- call `Filespec_GetFullPath(0, <copied token>, "eusecode.flx", 0)`
|
||||
|
||||
The raw data bytes at `1478:079a` confirm the fixed template directly:
|
||||
|
||||
- `65 75 73 65 63 6f 64 65 2e 66 6c 78 00`
|
||||
- ASCII = `eusecode.flx\0`
|
||||
|
||||
So these example invocations do **not** mean `load this exact archive file`:
|
||||
|
||||
- `-u USECODE/FLICTEST.FLX`
|
||||
- `-u FLICTEST.FLX`
|
||||
|
||||
They instead mean, in the current safest static interpretation:
|
||||
|
||||
- use `USECODE/FLICTEST.FLX` as the `path` component and still append the fixed filename `eusecode.flx`
|
||||
- or use `FLICTEST.FLX` as the `path` component and still append the fixed filename `eusecode.flx`
|
||||
|
||||
That makes both attempts wrong for a direct single-file override. The game is not being told to open your `FLICTEST.FLX` archive as the final filename at all.
|
||||
|
||||
## Safest Current Experiment Shape
|
||||
|
||||
The current safest static-model experiment is therefore:
|
||||
|
||||
1. create a directory that will act as the override root
|
||||
2. place a structurally valid replacement archive in that directory
|
||||
3. name that archive `EUSECODE.FLX`
|
||||
4. pass the directory path to `-u`, not the archive filename
|
||||
|
||||
Examples that now fit the recovered helper better are:
|
||||
|
||||
- `-u USECODE`
|
||||
- `-u .\\USECODE`
|
||||
- `-u \USECODE`
|
||||
- `-u C:\USECODE`
|
||||
- `-u MOD_UC`
|
||||
|
||||
with the important condition that the target directory must contain `EUSECODE.FLX`, not `FLICTEST.FLX`.
|
||||
|
||||
The safest current path interpretations for those examples are:
|
||||
|
||||
- `-u MADDOCODE` -> tries `MADDOCODE\EUSECODE.FLX` relative to the game's current DOS working directory
|
||||
- `-u .\MADDOCODE` -> tries `.\MADDOCODE\EUSECODE.FLX`
|
||||
- `-u \MADDOCODE` -> tries `\MADDOCODE\EUSECODE.FLX` on the current DOS drive
|
||||
- `-u C:\MADDOCODE` -> tries `C:\MADDOCODE\EUSECODE.FLX` on DOS drive `C:`
|
||||
|
||||
The current safest negative guidance is:
|
||||
|
||||
- do not use forward slashes like `USECODE/FLICTEST.FLX`
|
||||
- do not pass the archive filename itself as the `-u` token
|
||||
- do not assume a long absolute host path will survive the `0x1e`-byte parser buffer intact
|
||||
- do not assume a directory name longer than classic `8.3` DOS spelling will be accepted exactly as typed
|
||||
|
||||
## Expected Visible Output
|
||||
|
||||
There is currently no evidence that retail `-u` prints a success or failure banner before the game loads.
|
||||
|
||||
What the live code shows:
|
||||
|
||||
- the `-u` parser case at `1048:0a46` only copies the token into `1478:065a`
|
||||
- unlike `-debug`, `-warp`, `-skill`, `-mapoff`, `-egg`, and `-demo`, that parser case does **not** call `ConsolePrintf`
|
||||
- `startup_apply_u_override_if_present` also does not call `ConsolePrintf`
|
||||
- if `File_Exists` says the constructed path is missing, the helper just returns and startup continues
|
||||
|
||||
So the safest current user-facing expectation is:
|
||||
|
||||
- no dedicated `-u` console line on success
|
||||
- no dedicated `-u` console line on file-not-found failure
|
||||
- if the override path is wrong, the game most likely falls back silently to stock usecode
|
||||
|
||||
That means `no message appeared before the game loaded` is currently normal and does **not** prove the override worked.
|
||||
|
||||
## Strongest Current Diagnosis For The `MADDOCODE` Test Case
|
||||
|
||||
The current install-side evidence makes this case much tighter than a generic `-u` experiment.
|
||||
|
||||
What is now verified in the GOG install:
|
||||
|
||||
- `MADDOCODE` contains `EUSECODE.FLX`, `OVERLOAD.DAT`, `UNKCOFF.DAT`, and `UNKDS.DAT`
|
||||
- `OVERLOAD.DAT`, `UNKCOFF.DAT`, and `UNKDS.DAT` in `MADDOCODE` are byte-identical to the working copies in `USECODE`
|
||||
- only `EUSECODE.FLX` differs between the stock `USECODE` folder and `MADDOCODE`
|
||||
|
||||
That makes the A/B interpretation unusually clean.
|
||||
|
||||
If:
|
||||
|
||||
- replacing `USECODE\EUSECODE.FLX` with the hacked file produces the expected obvious gameplay breakage
|
||||
- but launching with `-u MADDOCODE` produces stock gameplay instead
|
||||
|
||||
the strongest current read is **not** `the alternate root loaded but behaved differently`. The stronger current read is:
|
||||
|
||||
- the alternate root was probably never selected
|
||||
- startup most likely fell back silently to stock usecode after the override path probe failed
|
||||
|
||||
Given the recovered code and the current install layout, the leading practical failure mode is now:
|
||||
|
||||
- the DOS-side file probe did not accept `MADDOCODE` exactly as typed
|
||||
- the short alias `MADDOC~1` is the next highest-probability working token
|
||||
|
||||
This is still a static-analysis conclusion rather than a runtime-tested acceptance matrix, so the exact relative-path and alias forms remain open. But the filename side is no longer the main uncertainty: the recovered helper is strongly telling us `directory/root override for EUSECODE.FLX`, not `arbitrary filename override`.
|
||||
|
||||
External engine code reinforces the fixed-filename part of this read. Both Pentagram and ScummVM build the default Crusader main usecode path as `usecode/` + language letter + `usecode.flx`, and for Remorse/Regret the language-usecode letter is `'e'`, which matches the retail helper's forced `eusecode.flx` template.
|
||||
|
||||
## Why This Looks Like Replacement, Not Addition
|
||||
|
|
@ -284,6 +423,8 @@ For current tooling planning, the defensive assumption should be:
|
|||
- prepare a structurally complete replacement source that satisfies the loader's normal expectations
|
||||
- do not assume the game will merge one partial class file into the stock runtime for us
|
||||
|
||||
That also means a standalone archive like `FLICTEST.FLX` is currently a poor first experiment even if its internals are valid. The live retail loader path is much more likely to accept a complete replacement `EUSECODE.FLX` rooted under a directory passed to `-u`.
|
||||
|
||||
## Best Current Usefulness For Decompilation Work
|
||||
|
||||
This path is interesting for the current usecode-decompilation lane for two reasons.
|
||||
|
|
|
|||
402
docs/usecode/flictest-investigation.md
Normal file
402
docs/usecode/flictest-investigation.md
Normal file
|
|
@ -0,0 +1,402 @@
|
|||
# FLICTEST usecode investigation
|
||||
|
||||
## Verdict
|
||||
|
||||
`FLICTEST` is real shipped usecode, and it is absolutely movie-related.
|
||||
|
||||
The strongest current reading is:
|
||||
|
||||
- `FLICTEST` is a movie playback helper/jukebox class, not a normal authored world-object class that maps place directly on maps.
|
||||
- It is reachable through other shipped usecode classes.
|
||||
- Reachability differs by game: Remorse keeps a much larger movie-browser helper, while Regret keeps a smaller helper and uses it through a handful of specific caller scripts.
|
||||
- In Regret specifically, the strongest surviving route is not just a generic helper call. It sits behind a hidden `KEYPADNS` menu path that explicitly exposes both a `Cheaters Menu` branch and a `VIDEO PLAYER` branch.
|
||||
|
||||
I did not find evidence that any placed map item or egg resolves directly to the `FLICTEST` class itself. The evidence instead points to `FLICTEST` being spawned as a helper from other classes.
|
||||
|
||||
## Core class evidence
|
||||
|
||||
Remorse extracted class metadata:
|
||||
|
||||
- `USECODE/EUSECODE_extracted/class_event_index.tsv`
|
||||
- `entry_index = 402`, `object_index = 0xA22`, `class_id = 0x0A20`, `class_name_hint = FLICTEST`
|
||||
- only slots `0x20` and `0x21` are populated
|
||||
- slot `0x20`: body `0x00E0..0x279F`, length `9919`
|
||||
- slot `0x21`: body `0x279F..0x4E4C`, length `9901`
|
||||
|
||||
Regret extracted class metadata:
|
||||
|
||||
- `USECODE/REGRET/REGRET_USECODE_extracted/class_event_index.tsv`
|
||||
- `entry_index = 456`, `object_index = 0xA0C`, `class_id = 0x0A0A`, `class_name_hint = FLICTEST`
|
||||
- only slots `0x20` and `0x21` are populated
|
||||
- slot `0x20`: body `0x00E0..0x0CA4`, length `3012`
|
||||
- slot `0x21`: body `0x0CA4..0x0CAF`, length `11`
|
||||
|
||||
That already looks less like an ordinary object family and more like a dedicated helper with anonymous high-slot entry points.
|
||||
|
||||
## What the bodies do
|
||||
|
||||
### Remorse
|
||||
|
||||
`map_renderer/.cache/usecode/remorse/EUSECODE/pseudocode/FLICTEST/slot_20_slot_20.txt` is a keypad-driven movie browser.
|
||||
|
||||
- It calls `KeypadGump.showKeypad(0)`.
|
||||
- It switches on the entered number.
|
||||
- Nearly every case calls `playFlic(...)` with a short movie name such as `01a`, `04h`, `m03`, `mva01`, `o01`, `t02`, `test`, `z1`, or `wec`.
|
||||
- The helper includes special names such as `hideoutx`, many `mva*` clips, and an explicit `test` clip.
|
||||
|
||||
`map_renderer/.cache/usecode/remorse/EUSECODE/pseudocode/FLICTEST/slot_21_slot_21.txt` is a second movie dispatcher that takes the movie selector from `arg_10` instead of reading a keypad itself. In other words, Remorse ships both:
|
||||
|
||||
- an interactive movie-picker entry point in `slot_20`
|
||||
- a parameter-driven dispatcher in `slot_21`
|
||||
|
||||
That is consistent with a general movie test or movie browser utility.
|
||||
|
||||
### Regret
|
||||
|
||||
Regret still ships `FLICTEST`, but the helper is cut down.
|
||||
|
||||
- `slot_20` remains a real body.
|
||||
- `slot_21` is only:
|
||||
|
||||
```c
|
||||
function flictest_slot_21()
|
||||
{
|
||||
set_info(1, *(arg_06));
|
||||
return;
|
||||
}
|
||||
```
|
||||
|
||||
So Regret preserves the helper class but no longer keeps the large second dispatch body that Remorse has.
|
||||
|
||||
## Where it is used
|
||||
|
||||
### Remorse: `EVENT::equip`
|
||||
|
||||
The clearest Remorse use is inside the shipped `EVENT` controller family.
|
||||
|
||||
Evidence:
|
||||
|
||||
- `USECODE/EUSECODE_extracted/class_event_index.tsv` shows `EVENT` slot `0x0A` as the single large active body.
|
||||
- `docs/map_renderer/trigger-usecode-links.md` already treats `EVENT` as a shipped controller family and maps shape `0x0361` to `EVENT::equip`.
|
||||
- `map_renderer/Catalogs/usecode_shape_catalog_remorse.csv` names shape `0x0361` as `EVENT_CONTROLLER`.
|
||||
|
||||
Direct caller snippet from `map_renderer/.cache/usecode/remorse/EUSECODE/pseudocode/EVENT/slot_0A_equip.txt`:
|
||||
|
||||
```c
|
||||
case 0x00fa:
|
||||
spawn FLICTEST.slot_20(pid, flicMan);
|
||||
suspend;
|
||||
return;
|
||||
```
|
||||
|
||||
This is strong evidence that Remorse's event-controller ecosystem can launch `FLICTEST` as a helper, using an event-local `flicMan` value as the selector.
|
||||
|
||||
Current best read: Remorse uses `FLICTEST` as a generic movie-playback utility behind at least some `EVENT`-driven scripted situations.
|
||||
|
||||
### Remorse: current access status
|
||||
|
||||
The newer comparison pass makes Remorse look much less recoverable than Regret.
|
||||
|
||||
- The only confirmed Remorse `spawn FLICTEST.slot_20(...)` call found in the extracted pseudocode is the `EVENT::equip` `case 0x00fa` branch above.
|
||||
- Remorse `KEYPADNS::use` is only the standard keypad wrapper. It has no `VIDEO PLAYER` or `Cheaters Menu` string path.
|
||||
- Remorse `DATALINK::use` does not spawn `FLICTEST`. Instead it plays the campaign MVA clips directly with `playFlicsomething(...)` and then shows mission-objective text.
|
||||
|
||||
That means the current best No Remorse answer is:
|
||||
|
||||
- `FLICTEST` exists and its keypad browser body is real.
|
||||
- But I did not find a shipped player-facing keypad, monitor, or datalink route that opens that browser directly.
|
||||
- The only current workspace evidence for launching the browser in Remorse is the generic `EVENT` controller case `0x00fa`, and I do not yet have a verified placed world route for that case.
|
||||
|
||||
The latest map pass strengthens that answer with actual `KEYPADNS` placement data:
|
||||
|
||||
- In the generated Remorse scene export, the only placed `shape:1099` (`0x044b`, `KEYPADNS`) records I found are all on map 1.
|
||||
- Those three records are ordinary `frame 0` keypad objects with full quality values `267`, `832`, and `41997`.
|
||||
- Their local low bytes are therefore `0x0b`, `0x40`, and `0x0d`, not the Regret-style hidden selector values `0` / `1`.
|
||||
- Remorse `KEYPADNS::use` has no `VIDEO PLAYER` or `Cheaters Menu` branch anyway, so these map-1 placements currently read as normal gameplay keypads rather than hidden `FLICTEST` terminals.
|
||||
- A broader exported-map scan also found no placed `shape:1236` (`0x04d4`, `DATALINK`) records in the rendered Remorse map exports, which fits the usecode-side picture that the shipped datalink path is inventory/startup-facing rather than a placed world terminal route.
|
||||
- The exported `shape:865` (`0x0361`, `EVENT_CONTROLLER`) placements I found are also all on map 1, and sampled controllers are not colocated with the three `KEYPADNS` objects above.
|
||||
|
||||
So, unlike Regret, No Remorse currently looks like `movie-browser helper present in data, but no confirmed shipped user-facing access path`.
|
||||
|
||||
## Remorse datalink hack-in patch
|
||||
|
||||
The minimal reversible Remorse hack is a 2-byte `EUSECODE.FLX` edit, not an executable rewrite.
|
||||
|
||||
### Why the patch moved out of `CRUSADER.EXE`
|
||||
|
||||
The first executable-side `Item_Use` hook was functionally plausible, but it changed too many bytes in a relocation-sensitive path and the user reported that build crashed on startup.
|
||||
|
||||
That made the right bar much stricter:
|
||||
|
||||
- avoid `CRUSADER.EXE` entirely if possible
|
||||
- avoid far-call rewrites
|
||||
- change the smallest stable token already present in shipped data
|
||||
|
||||
The better target turned out to be the tail of Remorse `DATALINK::use` inside `USECODE\EUSECODE.FLX`.
|
||||
|
||||
### Why I did not use a direct class-event row swap
|
||||
|
||||
I checked the raw Remorse `EUSECODE.FLX` class-event rows directly.
|
||||
|
||||
- `DATALINK slot 0x01 use` row bytes are `B4 13 01 00 00 00`
|
||||
- `FLICTEST slot 0x20` row bytes are `BF 26 01 00 00 00`
|
||||
|
||||
Those are not global cross-class code pointers. The extracted parser and raw headers show they are class-local metadata:
|
||||
|
||||
- each row is parsed as `<u16 raw_event_entry_word, u32 raw_code_offset>`
|
||||
- the code body is resolved relative to the current class chunk
|
||||
- copying a `FLICTEST` row into `DATALINK` would still resolve inside the `DATALINK` chunk
|
||||
|
||||
So a row swap would not produce a real `DATALINK -> FLICTEST` redirect.
|
||||
|
||||
### Final patch shape
|
||||
|
||||
The validated patch site is the final spawned callee token in Remorse `DATALINK::use`.
|
||||
|
||||
Pseudocode evidence from the extracted Remorse cache:
|
||||
|
||||
```c
|
||||
spawn TEXTFILE.slot_20(pid, textFile, arg_06);
|
||||
suspend;
|
||||
```
|
||||
|
||||
Raw file evidence from the installed retail `USECODE\EUSECODE.FLX` near the end of the `DATALINK` body:
|
||||
|
||||
```text
|
||||
04 59 41 FE 40 06 57 02 02 17 0A 20 00 65 00 6E FE 5E 54 01 01 12 53 5C
|
||||
```
|
||||
|
||||
The important token is the class id in the final spawn:
|
||||
|
||||
- `17 0A` = `TEXTFILE` (`0x0A17`)
|
||||
- `20 0A` = `FLICTEST` (`0x0A20`)
|
||||
|
||||
So the effective patch is simply:
|
||||
|
||||
- file: `USECODE\EUSECODE.FLX`
|
||||
- installed retail offset resolved by signature scan: `0x1FF55`
|
||||
- retail bytes: `17 0A`
|
||||
- patched bytes: `20 0A`
|
||||
|
||||
That preserves the existing `slot_20` dispatch and only swaps the target class.
|
||||
|
||||
So the effective behavior becomes:
|
||||
|
||||
- normal Remorse datalink tail: `spawn TEXTFILE.slot_20(...)`
|
||||
- patched Remorse datalink tail: `spawn FLICTEST.slot_20(...)`
|
||||
|
||||
This exposes the shipped keypad-driven `FLICTEST` movie browser with only a 2-byte usecode-file change.
|
||||
|
||||
### Raw hex patch
|
||||
|
||||
At the resolved installed retail site:
|
||||
|
||||
```text
|
||||
Offset 0x1FF55
|
||||
Retail : 17 0A
|
||||
Patched: 20 0A
|
||||
```
|
||||
|
||||
Or with surrounding context:
|
||||
|
||||
```text
|
||||
Retail : 57 02 02 17 0A 20 00 65 00 6E FE
|
||||
Patched: 57 02 02 20 0A 20 00 65 00 6E FE
|
||||
```
|
||||
|
||||
### Patcher integration
|
||||
|
||||
I updated `Crusader-Map-Viewer/patch_crusader_map_load.ps1` so the Remorse-only movie-browser option now patches `USECODE\EUSECODE.FLX` instead of the executable.
|
||||
|
||||
- CLI enable: `-Choice flictest-enable`
|
||||
- CLI restore: `-Choice flictest-default`
|
||||
- interactive enable: `6`
|
||||
- interactive restore: `7`
|
||||
|
||||
The script resolves the site by scanning for the full DATALINK tail signature, then applies only the 2-byte token swap. It refuses to write if the current bytes are neither the expected retail bytes nor the already-enabled bytes.
|
||||
|
||||
### Current validation state
|
||||
|
||||
The rewritten PowerShell script was copied into the installed No Remorse folder and validated with `-Choice status`.
|
||||
|
||||
Current retail detection from the live install:
|
||||
|
||||
- `CRUSADER.EXE` startup selector resolved normally
|
||||
- both editor-visibility sites resolved normally
|
||||
- Remorse datalink movie-browser site resolved at `USECODE\EUSECODE.FLX` offset `0x1FF55`
|
||||
- current state reported as `Retail default`
|
||||
|
||||
So the signature-scan and offset-resolution logic for the minimal patch is now confirmed against the installed retail files.
|
||||
|
||||
### Regret: `DATALINK::use`
|
||||
|
||||
Regret has a shipped, clearly reachable `DATALINK` path into `FLICTEST`.
|
||||
|
||||
Direct caller snippet from `map_renderer/.cache/usecode/regret/EUSECODE/pseudocode/DATALINK/slot_01_use.txt`:
|
||||
|
||||
```c
|
||||
else {
|
||||
spawn FLICTEST.slot_20(pid, global[0x0001], local_02);
|
||||
suspend;
|
||||
}
|
||||
```
|
||||
|
||||
This branch sits at the tail of the normal `DATALINK::use` body, after the map-specific mission/objective text handling.
|
||||
|
||||
Why this matters for reachability:
|
||||
|
||||
- `docs/scummvm-crusader-reference.md` notes that `games/start_crusader_process.cpp` seeds inventory with shape `0x4d4` `datalink` at game start.
|
||||
- That means `DATALINK` is not hypothetical leftover content. It is part of the live shipped startup flow.
|
||||
|
||||
So even if the exact branch condition still needs fuller runtime narrowing, `FLICTEST` is attached to a definitely shipped and definitely player-facing object family in Regret.
|
||||
|
||||
### Regret: `KEYPADNS::use`
|
||||
|
||||
Regret also has an even more explicit movie-browser route through `KEYPADNS`.
|
||||
|
||||
This is also where the strongest hidden/debug-style evidence lives. Unlike Remorse, whose `KEYPADNS::use` is just the normal keypad wrapper that either spawns `KEYPAD.slot_20` or forwards to `TRIGGER.slot_20`, Regret adds two special map-gated branches ahead of the ordinary keypad logic.
|
||||
|
||||
Direct caller snippet from `map_renderer/.cache/usecode/regret/EUSECODE/pseudocode/KEYPADNS/slot_01_use.txt`:
|
||||
|
||||
```c
|
||||
local_06 = "VIDEO PLAYER^_____________^^Mission video 0-72^Mission MVAs 73-89^Game Flicks 90-102";
|
||||
ComputerGump.readComputer(local_06);
|
||||
local_02 = KeypadGump.showKeypad(0);
|
||||
...
|
||||
spawn FLICTEST.slot_20(pid, local_02, local_04);
|
||||
```
|
||||
|
||||
This is the single strongest semantic clue in the whole pass. `KEYPADNS` literally presents a `VIDEO PLAYER` menu and then hands the user-selected number to `FLICTEST.slot_20`.
|
||||
|
||||
The same body also contains a second literal branch:
|
||||
|
||||
```c
|
||||
local_06 = "Cheaters Menu^_____________^^Select a mission^number (1-10)^and Col. Shepherd^will assign it to you.";
|
||||
```
|
||||
|
||||
That changes the interpretation materially. In Regret, `KEYPADNS` is not just a generic keypad helper that happens to call a movie routine. It preserves an overt hidden-menu path with both mission-cheat and movie-browser behavior.
|
||||
|
||||
That makes `FLICTEST` look less like a purely abandoned developer test and more like a retained hidden utility that still sits behind shipped object logic, at least for this Regret keypad/computer path.
|
||||
|
||||
#### Recoverability status
|
||||
|
||||
This is where the evidence gets better than the first pass.
|
||||
|
||||
- Remorse contrast: `map_renderer/.cache/usecode/remorse/EUSECODE/pseudocode/KEYPADNS/slot_01_use.txt` has no `Cheaters Menu` or `VIDEO PLAYER` branch. It is only the standard keypad wrapper.
|
||||
- Regret demo persistence: `map_renderer/.cache/usecode/regret-demo/EUSECODE/pseudocode/KEYPADNS/slot_01_use.txt` still contains both the `Cheaters Menu` string and the `VIDEO PLAYER` string. So this was not a one-build accident unique to one retail extract.
|
||||
- Shape alignment: Regret extracted metadata gives `KEYPADNS` class id `0x044b`, and the Regret shape catalog also has a placed but unnamed base entry at `0x044b`.
|
||||
- Map placement: a structured scan of the Regret scene cache found an actual placed `shape:1099` (`0x044b`) object on map 26 at world position `(32746, 32798, 48)` with `quality = 1` and `frame = 1`.
|
||||
- Stable locator: in the viewer, that map-26 object resolves to fixed scene id `fixed:123` and runtime id `item:757:fixed:1099:1:32746:32798:48`.
|
||||
- Adjacent-state persistence: the same visible keypad also exists on Regret map 27 at the same world coordinates with the same `quality = 1` / `frame = 1` signature, so this is not unique to a single cache variant.
|
||||
|
||||
That last point matters because the hidden movie-browser branch checks for the exact pair `Actor.getMap(...) == 26` and `Item.getQLo(arg_06) == 1` before showing `VIDEO PLAYER` and spawning `FLICTEST.slot_20`.
|
||||
|
||||
So the movie-browser route is no longer just semantically plausible. There is concrete shipped placement evidence for the exact object signature that the branch expects.
|
||||
|
||||
The remaining gap is narrower now:
|
||||
|
||||
- I did not find a matching base `shape:1099` object with `quality = 0` in the map-26 scene cache, so the `Cheaters Menu` branch is still not proven to be exposed by a shipped interactable in the same way.
|
||||
- I did find a `shape:1099` helper-geometry record with `quality = 0` on map 26, but it is flagged invisible in the scene cache, so it is not strong enough to claim a player-facing cheat terminal without more runtime or map-authoring evidence.
|
||||
|
||||
#### Practical locator
|
||||
|
||||
For the visible Regret movie keypad route, the current best locator set is:
|
||||
|
||||
- Map 26 placed item: `fixed:123`
|
||||
- Map 26 runtime id: `item:757:fixed:1099:1:32746:32798:48`
|
||||
- World coordinates: `(32746, 32798, 48)`
|
||||
- Disk coordinates used by the built-in warp path: `(16373, 16399, 48)`
|
||||
- Direct debug warp into map 26: `-warp 0 16373 16399 48 -mapoff 26`
|
||||
|
||||
Because map 27 is a base mission map in the executable warp table and carries the same keypad placement, there is also a second practical route:
|
||||
|
||||
- Map 27 runtime id: `item:755:fixed:1099:1:32746:32798:48`
|
||||
- Direct mission warp into the same keypad position on the adjacent state: `-warp 14 16373 16399 48`
|
||||
|
||||
Current best access read:
|
||||
|
||||
- `VIDEO PLAYER` / `FLICTEST` keypad: plausibly reachable from shipped content, and now backed by a visible placed keypad record.
|
||||
- `Cheaters Menu` keypad: branch definitely exists, but I have not found a matching visible `map 26 + QLo 0` placement. The only current map-26 `QLo 0` sibling is invisible helper geometry, so normal-player access to the cheat branch is still unproven.
|
||||
- If someone wants to force-test the cheat path, the cleanest current experiment would be to duplicate or retag the visible `0x044b` keypad so its local quality byte is `0` instead of `1`, then use that object on map 26.
|
||||
|
||||
#### Runtime confirmation
|
||||
|
||||
A live runtime test now closes the practical behavior:
|
||||
|
||||
- Warping to map 26 and using the visible `QLo 1` keypad does open the `FLICTEST` movie browser.
|
||||
- Talking to the nearby mission-giver NPC instead produces cheat-menu instructions and accepts mission numbers for the mission-assignment warp behavior.
|
||||
- Warping to map 27 does not reproduce that movie-browser route: the matching keypad placement there is nonfunctional for `FLICTEST`, and the nearby cheat-menu interaction is absent in that state.
|
||||
|
||||
So the Regret hidden branch is no longer only static-analysis evidence. The visible movie keypad behavior is runtime-confirmed, and the map-27 comparison now suggests that the hidden menu path is state-gated rather than broadly enabled on every neighboring camp variant.
|
||||
|
||||
#### Is map 26 reachable without warp codes?
|
||||
|
||||
Current best answer: probably not through the normal campaign flow.
|
||||
|
||||
The strongest reasons are:
|
||||
|
||||
- Regret map 26 scene metadata marks the map as `offset-only-or-unmapped`, while map 25 and map 27 are the neighboring campaign-facing camp states.
|
||||
- The live No Regret mission table uses base maps `0, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 40`; map 26 is absent from that table.
|
||||
- The local map-25, map-26, and map-27 scene files all expose the same two local teleport-destination eggs (`teleport-id` `30` and `250`), but I did not find any teleporter helper or destination record in those scenes that explicitly names map 26 as the player-facing destination.
|
||||
- A broader Regret scene-cache search for `mapNum 26` does return many hits, but sampled hits resolve mostly to family-4 usecode-trigger egg records whose `labelId` happens to be `26`, not to a confirmed cross-map destination lane into map 26.
|
||||
|
||||
That does not prove there is no obscure script-only handoff anywhere in the executable, but the current workspace evidence favors `debug or offset-only camp variant` rather than `normally entered campaign map`.
|
||||
|
||||
### Regret: `ELEVATOR::slot_20`
|
||||
|
||||
Regret also contains a smaller scripted launch from an elevator body.
|
||||
|
||||
Direct caller snippet from `map_renderer/.cache/usecode/regret/EUSECODE/pseudocode/ELEVATOR/slot_20_slot_20.txt`:
|
||||
|
||||
```c
|
||||
if (arg_12 == 6) {
|
||||
...
|
||||
setUnkFlagA4();
|
||||
spawn FLICTEST.slot_20(pid, 19, local_15);
|
||||
suspend;
|
||||
clearUnkFlagA4();
|
||||
}
|
||||
```
|
||||
|
||||
This path fades to black, stops SFX, sets a temporary flag, and then spawns `FLICTEST`. That looks like a cutscene or scripted transition, not a debug-only leftover.
|
||||
|
||||
## What I did not find
|
||||
|
||||
- No direct evidence that map placements instantiate the `FLICTEST` class itself.
|
||||
- No direct shape-catalog or egg-subtype crosswalk that points a placed object straight at `FLICTEST`.
|
||||
- No ScummVM engine-side special case named `FLICTEST`; it looks like data-driven usecode, not a hardcoded engine mode.
|
||||
- The remaining uncertainty is now concentrated in the `Cheaters Menu` side of Regret `KEYPADNS`, not the `VIDEO PLAYER` side. The movie-browser branch has stronger placement evidence than the cheat branch.
|
||||
|
||||
That absence matters. The current evidence is much stronger for `helper class spawned by other scripts` than for `standalone world object that level designers placed directly`.
|
||||
|
||||
## Engine-side movie context
|
||||
|
||||
The ScummVM Crusader reference supports the general movie interpretation.
|
||||
|
||||
- `docs/scummvm-crusader-reference.md` notes that Crusader intro playback requires the `FLICS` directory.
|
||||
- The same note lists `MovieGump::I_playMovieOverlay` among the Crusader-side USECODE bridge intrinsics.
|
||||
- It also notes that Crusader movie playback uses assets under `flics/`.
|
||||
|
||||
That does not prove every `playFlic` signature detail in the original DOS binary, but it does support the high-level claim that `FLICTEST` is using the normal Crusader movie subsystem rather than a separate hidden debugger subsystem.
|
||||
|
||||
## Current best answer
|
||||
|
||||
Yes: `FLICTEST` really is a movie-test/movie-browser style usecode class.
|
||||
|
||||
More specifically:
|
||||
|
||||
- In Remorse it looks like a fairly full-featured movie selector/dispatcher helper.
|
||||
- In Regret it survives in reduced form, but one surviving route is a clearly hidden `KEYPADNS` menu that still advertises `VIDEO PLAYER` and `Cheaters Menu` behaviors.
|
||||
- It is not obviously placed directly on maps as its own authored class.
|
||||
- It is used indirectly by shipped content.
|
||||
- The hidden `VIDEO PLAYER` route in Regret is plausibly recoverable from shipped content because map 26 contains a placed `0x044b` object with the exact local quality value the branch checks.
|
||||
- The hidden `Cheaters Menu` route is still only partially proven. The branch exists, but I did not yet find a matching player-facing `map 26 + QLo 0` object.
|
||||
|
||||
Most defensible live uses found in this pass:
|
||||
|
||||
- Remorse: `EVENT::equip` can spawn `FLICTEST.slot_20`.
|
||||
- Regret: `DATALINK::use` can spawn `FLICTEST.slot_20`.
|
||||
- Regret: `KEYPADNS::use` exposes a literal `VIDEO PLAYER` UI and then spawns `FLICTEST.slot_20`.
|
||||
- Regret: `ELEVATOR::slot_20` has at least one cutscene-style spawn of `FLICTEST.slot_20`.
|
||||
|
||||
So the answer is not just `dead test script`. The better reading is `retained movie helper that still participates in shipped scripted flows`, even if its original name probably came from development/test usage.
|
||||
Loading…
Add table
Add a link
Reference in a new issue