Skip to content

Unmangling and decoding algorithms

ninjatobob edited this page Sep 26, 2025 · 16 revisions

Game files are obfuscated in the release versions of Fatal Racing and Whiplash using one or both of the following processes:

  • Mangling: "RLE algorithm on steroids"
  • Encoding: Fibonacci-like XOR cipher stream.

No obfuscation is used in the Fatal Racing demo or beta.

File unmangling

The Whiptools unmangler is adapted from this gist by @samunders-core.

Original implementation

The original unmangler (UNMANGLE.C) is part of the Actua Soccer/UEFA Euro 96 England source code which was accidentally distributed on the cover disc of issue 71 (May 1996) of French computer magazine Joystick. The source code was later made available on the Gremlin Archive under a CC BY-NC-ND 4.0 licence.

Note that UNMANGLE.C ignores the 4-byte header containing the unmangled file length and instead terminates the output when a zero control byte is found. However, the header is used by SOUND.C in Fatal Racing/Whiplash (loadfile and getcompactedfilelength).

Mangled file specification

File header

4-byte header denoting the length of the unmangled file.

Control byte

One of eight functions is performed depending on the control byte (next unread byte). Gremlin's original unmangler implementation uses a bitwise decision tree to determine the required function as follows:

Literal / Diff / Repeat
0xxxxxxx
Block
1xxxxxxx
Literal
00xxxxxx
Diff / Repeat
01xxxxxx
Short block
10xxxxxx
Medium / Long block
11xxxxxx
Diff
010xxxxx
Repeat
011xxxxx
Medium block
110xxxxx
Long block
111xxxxx
Byte diff
0100xxxx
Word diff
0101xxxx
Byte repeat
0110xxxx
Word repeat
0111xxxx
0x01 to 0x3F 0x40 to 0x4F 0x50 to 0x5F 0x60 to 0x6F 0x70 to 0x7F 0x80 to 0xBF 0xC0 to 0xDF 0xE0 to 0xFF

0x00 is reserved for signalling the end of the mangled data.

Literal (0x01 to 0x3F)

Copy raw bytes from input to output.

  • Length: 1 to 63 bytes (value of the control byte).

Byte difference (0x40 to 0x4F)

Generate bytes based on the delta of the last 2 bytes in output. Each next byte = previous byte + delta.

  • Length: (control & 0x0F) + 3 (3 to 18 bytes).

Word difference (0x50 to 0x5F)

Generate words based on the delta of the last 2 words in output. Each new word = previous word + delta.

  • Length: (control & 0x0F) + 2 (4 to 34 bytes).

Byte repeat (0x60 to 0x6F)

Repeat the last byte in output multiple times.

  • Length: (control & 0x0F) + 3 (3 to 18 bytes).

Word repeat (0x70 to 0x7F)

Repeat the last word in output multiple times.

  • Length: (control & 0x0F) + 2 (4 to 34 bytes).

Short block (0x80 to 0xBF)

Clone 3 bytes from an earlier position in output.

  • Offset: (control & 0x3F) + 3 (3 to 66 bytes).
  • Length: 3 bytes.

Medium block (0xC0 to 0xDF)

Clone multiple bytes from an earlier position in output, with offset and length from the next input byte.

  • Offset: ((control & 0x03) << 8) + nextByte + 3 (3 to 1026 bytes).
  • Length: ((control >> 2) & 0x07) + 4 (4 to 11 bytes).

Long block (0xE0 to 0xFF)

Clone multiple bytes from an earlier position in output, with offset and length from the next 2 input bytes.

  • Offset: ((control & 0x1F) << 8) + nextByte1 + 3 (3 to 8194 bytes).
  • Length: nextByte2 + 5 (5 to 260 bytes).

Mangler compression efficiency

The Whiptools mangler achieves a ~0.2% better overall compression ratio than the original mangled files from the US release of Whiplash (282 files):

Mangler Total file size (bytes) Compression ratio
Whiptools 1.4.5 13,454,194 40.90%
Original mangled files 13,521,972 41.11%
Whiptools 1.3.1 15,855,211 48.20%
Unmangled 32,892,331 100.00%

Similar results are achieved using the English and Brazilian Portuguese installations of Fatal Racing (276 files each).

File decoding

Encoding is applied to .KC files (cheat audio activated by TOPTUNES), FATAL.INI (configuration file) and PASSWORD.INI (cheat names). To decode these, apply a bitwise XOR against the Fibonacci-like sequence $a_n = a_{n-1} + a_{n-2}$ with the first byte XORed against $a_2$, defined by the following initial constants:

  • .KC files: $a_0 = 115$, $a_1 = 150$

    Note .KC files must be unmangled first, then decoded.

  • FATAL.INI: $a_0 = 77$, $a_1 = 101$

  • PASSWORD.INI: $a_0 = 23$, $a_1 = 37$

    By decoding PASSWORD.INI in Fatal Racing V2.0 builds or Whiplash we can find two undocumented cheat names:

    • PROCESS: performs a Pentium FDIV bug check and changes to TYPE A if the bug is found or TYPE B if not.
    • CLONES: not implemented.

FRONTEND.C also calls this function with $a_0 = 43$ and $a_1 = 87$ to decode/re-encode the list of cheat names.