225 lines
No EOL
9.3 KiB
Markdown
225 lines
No EOL
9.3 KiB
Markdown
# Editor-Object Visibility In Retail Crusader
|
|
|
|
## Question
|
|
|
|
Investigate whether the retail game explicitly hides editor-only map objects during gameplay, and whether any built-in debug, cheat, or command-line switch can enable those objects in the normal in-game renderer.
|
|
|
|
Active analysis target for the binary side was live `CRUSADER.EXE` in the Ghidra MCP session.
|
|
|
|
## Result
|
|
|
|
Current best answer:
|
|
|
|
- Yes, the retail executable explicitly hides editor-tagged shapes in the normal world-item render path, with at least two recovered `SI_EDITOR` gates.
|
|
- No built-in retail flag, cheat, or command-line switch has been recovered so far that re-enables those editor objects in the normal in-game renderer.
|
|
- The closest confirmed "show editor items" toggle is in ScummVM/Pentagram, not in the original retail executable.
|
|
|
|
## Binary Findings In `CRUSADER.EXE`
|
|
|
|
### 1. The downstream sprite painter explicitly skips `SI_EDITOR`
|
|
|
|
The strongest direct proof is live function `Item_PaintSprite` at `1198:02e4`.
|
|
|
|
Verified decompile behavior:
|
|
|
|
- It loads the current item's shape number from `g_itemShapeNos`.
|
|
- It resolves the matching `ShapeData` entry from `g_shapeData`.
|
|
- It immediately checks `local_8->flags2 & 1`.
|
|
- When that bit is set, the function returns before caching or drawing the shape.
|
|
|
|
Relevant instruction window at `1198:0332..033d`:
|
|
|
|
```text
|
|
1198:0332 MOV AL,byte ptr ES:[BX + 0x6]
|
|
1198:0336 AND AX,0x1
|
|
1198:0339 OR AX,AX
|
|
1198:033b JZ 1198:0340
|
|
1198:033d JMP 1198:0959
|
|
```
|
|
|
|
`1198:0959` is the function epilogue, so the branch is an unconditional early-out for bit `0` of shape byte `6`.
|
|
|
|
This aligns with the existing `SI_EDITOR` comment at `1198:0339` and with the open-source Crusader typeflag decoders, where Crusader `TYPEFLAG.DAT` byte `6`, bit `0` maps to `SI_EDITOR`.
|
|
|
|
### 2. The paired setup path still processes those items
|
|
|
|
The neighboring helper `Item_SetScreenBoxAndWorldCoords` at `1198:095d` still computes:
|
|
|
|
- world coordinates,
|
|
- projected screen coordinates,
|
|
- bounding boxes,
|
|
- and weapon-overlay extents.
|
|
|
|
That function does **not** perform an `SI_EDITOR` check.
|
|
|
|
Current safest read after the first pass was:
|
|
|
|
- editor objects remain present in the live item/cache/display-list setup state,
|
|
- but the normal sprite paint stage refuses to draw them.
|
|
|
|
That read turned out to be incomplete rather than wrong.
|
|
|
|
### 3. The active world-item render-list builder also skips `SI_EDITOR` earlier
|
|
|
|
Follow-up tracing from the live camera redraw path into segment `1180` found a more important upstream gate in the world-item render-list builder.
|
|
|
|
Relevant instruction window at `1180:0951..095c`:
|
|
|
|
```text
|
|
1180:0951 MOV AL,byte ptr ES:[BX + 0x6]
|
|
1180:0955 AND AX,0x1
|
|
1180:0958 OR AX,AX
|
|
1180:095a JZ 1180:095f
|
|
1180:095c JMP 1180:0bec
|
|
```
|
|
|
|
This is the same Crusader `TYPEFLAG.DAT` byte `6`, bit `0` test, but here the consequence is stronger than in `Item_PaintSprite`: the builder skips the world-item draw-node allocation path entirely before that item can ever reach the normal draw dispatch.
|
|
|
|
That closes the runtime discrepancy from the first patch attempt. Patching only the later `1198:033b` branch changed a real live draw method, but it did **not** make editor objects visible in-game because the active renderer had already filtered them out earlier at `1180:0951..095c`.
|
|
|
|
### 4. Why the first executable patch appeared to do nothing
|
|
|
|
The first public patch only flipped the downstream `Item_PaintSprite` branch:
|
|
|
|
```text
|
|
1198:033b JZ 1198:0340 -> EB 03
|
|
```
|
|
|
|
That patch was not dead code. Export relocation evidence still shows the draw-node method slot at `1478:2c1f` dispatching to `1198:02e4`, so the downstream painter is a real live node callback.
|
|
|
|
But the active world-item renderer never reached that callback for editor-tagged shapes, because the upstream builder branch at `1180:095a..095c` had already skipped node creation.
|
|
|
|
Current patching implication:
|
|
|
|
- patching `1198:033b` alone is insufficient,
|
|
- the upstream `1180:095a` branch is the controlling visibility gate for normal world-item rendering,
|
|
- and a practical visibility patch should flip both recovered `SI_EDITOR` branches together.
|
|
|
|
### 5. The recovered item-flag helpers match the same shape-bit model
|
|
|
|
`Item_GetTypeFlagCrusader` reads arbitrary shape/typeflag bits from the per-shape `ShapeData` records.
|
|
|
|
Additional direct flag helpers nearby include:
|
|
|
|
- `Item_IsTargetable`, which reads `flags2 & 0x10`
|
|
- `Item_IsShapeFlagOccl`, which reads `flags0 & 0x10`
|
|
|
|
That supports the interpretation that `Item_PaintSprite` is not testing some temporary per-item state. It is reading a stable per-shape editor flag.
|
|
|
|
## Negative Search For A Retail "Show Editor Items" Toggle
|
|
|
|
I checked the known retail debug/cheat/control lanes already documented in the live database and repo notes.
|
|
|
|
### `-debug`
|
|
|
|
Retail `-debug` is live at `1048:0a93`, but the current closed behavior is:
|
|
|
|
- raise `g_debugMsgLevel` to `10`
|
|
- print `Debugging mode ON.`
|
|
- set `1478:0845`
|
|
- enable the seg1468 video-player timing overlay via `1478:0859`
|
|
|
|
No recovered link from `-debug` reaches the `SI_EDITOR` skip in `Item_PaintSprite` or enables a parallel item-paint path.
|
|
|
|
### Cheat/debug hotkeys
|
|
|
|
Recovered cheat/debug hotkeys do toggle several overlays, but the confirmed ones are unrelated to editor-shape rendering:
|
|
|
|
- `Ctrl+F7` at `13e8:1a20` toggles `1478:0ee0`, the egg-hatcher trigger-range overlay.
|
|
- `Alt+F7` at `13e8:1a50` toggles `1478:2bc9`, another cheat-gated overlay lane.
|
|
- The remaining F7-family toggle uses `1478:2bca` for the coarse grid overlay.
|
|
|
|
Those paths force camera redraws, but the recovered behavior is overlay drawing, not "draw editor-tagged world items."
|
|
|
|
### Hidden usecode debugger / Laurie lane
|
|
|
|
The hidden seg109/seg1408 usecode-debugger lane remains real, and `-laurie` plus the cheat/debug latches still enable related hidden behavior. But no recovered debugger-side state or menu action currently bypasses the `Item_PaintSprite` editor skip, and no candidate global has been found that matches ScummVM's later `showEditorItems` concept.
|
|
|
|
### Event `0x410`
|
|
|
|
This lane remains unrelated.
|
|
|
|
Current live conclusion is unchanged:
|
|
|
|
- retail `0x410` toggles the CD transfer display state,
|
|
- it is not the immortality toggle,
|
|
- and it is not an editor-object visibility switch.
|
|
|
|
## Cross-Check Against Open-Source Engine Behavior
|
|
|
|
ScummVM and Pentagram both independently preserve the same semantic meaning for the Crusader editor flag.
|
|
|
|
### Flag mapping
|
|
|
|
In both codebases, Crusader `TYPEFLAG.DAT` byte `6`, bit `0` maps to `SI_EDITOR`.
|
|
|
|
ScummVM `type_flags.cpp`:
|
|
|
|
```cpp
|
|
if (data[6] & 0x01) si._flags |= ShapeInfo::SI_EDITOR;
|
|
```
|
|
|
|
Pentagram `TypeFlags.cpp`:
|
|
|
|
```cpp
|
|
if (data[6] & 0x01) si.flags |= ShapeInfo::SI_EDITOR;
|
|
```
|
|
|
|
### Runtime treatment
|
|
|
|
ScummVM `game_map_gump.cpp` shows the exact higher-level behavior one would expect from the retail binary finding:
|
|
|
|
```cpp
|
|
if (!showEditorItems && item->getShapeInfo()->is_editor())
|
|
continue;
|
|
```
|
|
|
|
Pentagram documents the same meaning directly in `docs/u8typeflag.txt`:
|
|
|
|
- `bit 4 : editor shape (don't render in-game)`
|
|
|
|
For Crusader, the bit location differs in the later 9-byte Crusader format, but the semantic meaning is the same.
|
|
|
|
### Important limit of this cross-check
|
|
|
|
ScummVM exposes its own engine variable and debugger command:
|
|
|
|
- engine field: `_showEditorItems`
|
|
- setter: `setShowEditorItems(bool flag)`
|
|
- debugger command: `showEditorItems [on|off]`
|
|
|
|
That is useful as behavioral confirmation, but it is **not** evidence that retail `CRUSADER.EXE` ships with an equivalent built-in toggle.
|
|
|
|
## Conclusion
|
|
|
|
The retail executable does contain explicit logic to hide editor-only objects during ordinary gameplay rendering.
|
|
|
|
Current best model:
|
|
|
|
1. map/editor objects exist in the same broad world-item/runtime data flow as ordinary items,
|
|
2. the active world-item builder in segment `1180` checks `SI_EDITOR` and skips draw-node allocation when that bit is set,
|
|
3. if such an item still reached the standard sprite painter, `Item_PaintSprite` would also early-out on the same bit,
|
|
4. and no recovered retail debug/cheat/argument lane currently bypasses either gate.
|
|
|
|
I did **not** recover any retail equivalent of:
|
|
|
|
- a `showEditorItems` global,
|
|
- a command-line switch that enables editor-object rendering,
|
|
- a cheat/debug hotkey that enables editor-object rendering,
|
|
- or a hidden debugger command that obviously bypasses the `SI_EDITOR` skip.
|
|
|
|
So the present evidence supports:
|
|
|
|
- explicit retail hiding behavior exists,
|
|
- but no official or currently recovered internal toggle exists to reveal those objects in the shipped game.
|
|
|
|
If the goal is to see them in the original executable, the most direct current path is no longer searching cheats or args first. It is either:
|
|
|
|
- finding a second, debug-only display-list builder that bypasses the `1180:0951` gate, or
|
|
- patching both recovered `SI_EDITOR` branches in a writable copy.
|
|
|
|
## Next RE Follow-Up If Revisited
|
|
|
|
- Recover the exact containing function and wider caller chain for the `1180:0951` world-item builder skip and check whether any alternate debug-only builder exists.
|
|
- Inspect the segment `1180` redraw/display-list builders for a second path that draws world items outside the normal world-item allocation branch.
|
|
- If a runtime proof is wanted, patch both recovered `SI_EDITOR` branches only on a writable executable copy and test whether editor shapes become visible without destabilizing collision or selection. |