Crusader_Decomp/docs/usecode/flictest-investigation.md
2026-04-05 09:55:21 +02:00

20 KiB

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:
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:

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.

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:

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:

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:

Offset 0x1FF55
Retail : 17 0A
Patched: 20 0A

Or with surrounding context:

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 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:

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:

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:

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:

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.