This is an old revision of the document!
Table of Contents
Scripting in TR2/TR3 for PC/PSX
Overview
The internal gameflow, which levels come in what order, what item(s) Lara has at the beginning of each level, the filenames of the level and cut-scene files, all the visible text (e.g. “Save Game,” “Rusty Key,” etc.), and various other options are controlled by a script file called TOMBPC.DAT
/TOMBPSX.DAT
. The scripts were compiled using a utility known as GAMEFLOW.EXE
which was distributed by Eidos in the German release of Tomb Raider II Gold. Both TR2 and TR3 use these script files. From both games the format remained unchanged. TR1’s gameflow is hardcoded thus there is no external file controlling this resulting in loss of flexibility.
uint32_t Version; // The Script Version (Always 3 for TR2/3) uint8_t Description[256]; // Null-terminated string describing the script copyright info etc. Not encrypted. uint16_t GameflowSize; // Size in bytes of the game flow data, always 128 bytes int32_t FirstOption; // What to do when the game starts int32_t TitleReplace; // What to do when ExitToTitle is requested and "TitleDisabled" flag is set int32_t OnDeathDemoMode; // What to do when Lara dies during the demo mode int32_t OnDeathInGame; // What to do when Lara dies during the game uint32_t DemoTime; // Time in game ticks (1/30th of a second) to wait before starting a demo int32_t OnDemoInterrupt; // What to do when the demo mode is interrupted int32_t OnDemoEnd; // What to do when the demo mode ends uint8_t Unknown1[36]; // Filler uint16_t NumLevels; // Number of levels in the game, including the training level, not including the title level. uint16_t NumChapterScreens; // Chapter screens (Present in TR2, first used in TR3) uint16_t NumTitles; // Number of title elements (TITLE.TR2 level + the legal/title pictures in *.PCX format) uint16_t NumFMVs; // Number of FMV cutscenes PC - (*.RPL), PSX - (*.STR) uint16_t NumCutscenes; // Number of in-game (engine-rendered) cutscenes (CUT*.TR2) uint16_t NumDemoLevels; // Number of demo levels uint16_t TitleSoundID; // ID of title soundtrack (see below) uint16_t SingleLevel; // If doing only a single level, the level ID (starting at 1). -1 means disabled. uint8_t Unknown2[32]; // Filler uint16_t Flags; // Various flags, see below uint8_t Unknown3[6]; // Filler uint8_t XORKey; // Key used to encrypt/decrypt strings uint8_t LanguageID; // Script Language ID, see below uint16_t SecretSoundID; // ID of soundtrack to play when a secret is found (see below) uint8_t Unknown4[4]; // Filler // If Flags & UseXor true each character (except null-terminator) must be ^ XORKey to decrypt the string. TPCStringArray[NumLevels] LevelStrings; // level name strings TPCStringArray[NumChapterScreens] ChapterScreenStrings; // chapter screen strings TPCStringArray[NumTitles] TitleStrings; // title strings TPCStringArray[NumFMVs] FMVStrings; // FMV path strings TPCStringArray[NumLevels] LevelPathStrings; // level path strings TPCStringArray[NumCutscenes] CutscenePathStrings; // cutscene path strings uint16_t SequenceOffsets[NumLevels + 1]; // Relative offset to sequence info (the +1 is because the first one is the FrontEnd sequence, for when the game starts) uint16_t SequenceNumBytes; // Size of SequenceOffsets in bytes uint16_t[] Sequences[NumLevels + 1]; // Sequence info see explanation below (SIZE is dependant on first opcode) uint16_t DemoLevelIDs[NumDemoLevels]; #if PSX PSXFMVInfo[NumFMVs]; #endif uint16_t NumGameStrings; TPCStringArray[NumGameStrings] GameStrings; #if PSX TPCStringArray[size] PSXStrings; // size is 79 for the TR2 beta, 80 for all other versions #else TPCStringArray[41] PCStrings; #endif TPCStringArray[NumLevels] PuzzleStrings[4]; #if PSX && TR2_BETA TPCStringArray[NumLevels] SecretsStrings[4]; TPCStringArray[NumLevels] SpecialStrings[2]; #endif TPCStringArray[NumLevels] PickupStrings[2]; TPCStringArray[NumLevels] KeyStrings[4];
String arrays
struct TPCStringArray // (variable length) { uint16_t Offsets[Count]; // List containing for each string an offset in the Data block (Count * 2 bytes) uint16_t TotalSize; // Total size, in bytes (2 bytes) uint8_t Data[TotalSize]; // Strings block, usually encrypted (XOR-ed with XORKey, see above) }
Accent | Code | becomes |
---|---|---|
Acute | )e | é |
Circumflex | (e | ê |
Grave | $e | è |
Diaeresis | ~e | ë |
The following non-ASCII characters are also replaced:
Character | becomes | |
---|---|---|
Eszett (German) | ß | = |
There are also some exceptions (strings that were badly encoded by Core):
String | becomes |
---|---|
Red)marrer un niveau (“e” missing after parenthesis, )e implied) | Redémarrer un niveau |
PSX FMV Info
struct PSXFMVInfo // 8 bytes { uint32_t Start; // Start frame uint32_t End; // End frame };
This specific info is exclusive to TOMBPSX.DAT
.
Script Flags
Bit | Hex | Name | Description |
---|---|---|---|
0 | 0x01 | DemoVersion | Indicates that the game is a demo distribution. |
1 | 0x02 | TitleDisabled | Indicates that the game has no Title Screen. |
2 | 0x04 | CheatModeCheckDisabled | Indicates that the game does not look for the cheat sequence keystrokes and events. |
3 | 0x08 | NoInputTimeout | Indicates that the game waits forever if there is no input (won’t enter demo mode). |
4 | 0x10 | LoadSaveDisabled | Indicates that the game does not allow save games. |
5 | 0x20 | ScreenSizingDisabled | Indicates that the game does not allow screen resizing (with the function keys). |
6 | 0x40 | LockoutOptionRing | Indicates that the user has no access to the Option Ring while playing the game. |
7 | 0x80 | DozyCheatEnabled | Indicates that the game has the DOZY cheat enabled (flag only set in the final build of TR2 on PSX, but has no effect in-game). |
8 | 0x100 | UseXor | Indicates that a cypher byte was used to encrypt the strings in the script file, and is stored in the XorKey field. |
9 | 0x200 | GymEnabled | Is Gym available on title screen. |
10 | 0x400 | SelectAnyLevel | Enables level select when New Game is selected. |
11 | 0x800 | EnableCheatCode | It apparently has no effect on the PC game. |
Script Language
- 0 — English
- 1 — French
- 2 — German
- 3 — American
- 4 — Japanese
TR3 only:
- 5 — Italian
- 6 — Spanish
Script Sequencing & Opcodes/Operands
Each script has “sequence information”, Opcodes and Operands are all stored as uint16_t
. Sequences contain a set of commands to execute where an additional value (operand) is usually passed as a parameter to the function the command needs to call.
Script Opcodes
ID | Name | Description | Operand |
---|---|---|---|
0 | Picture | Unused. Compiles but does not show in-game. Maybe PSX. | Picture ID |
1 | ListStart | Unused. Maybe PSX. | |
2 | ListEnd |
||
3 | FMV | Display Full Motion Video. | FMV ID |
4 | Level | Start a playable level. | Level ID |
5 | Cine | Display cut scene sequence. | Cutscene ID |
6 | Complete | Display level-completion statistics panel. | |
7 | Demo | Display demo sequence. | Demo level ID |
8 | JumpToSequence | Jump to another sequence. | Sequence ID |
9 | End | Close script sequence. | |
10 | Track | Play Soundtrack (it precedes opcodes of associated levels). | Track ID |
11 | Sunset | Unknown. Nothing changes in-game. Used in Bartoli’s Hideout. Maybe not-implemented ancestor of TR4 LensFlare ? | |
12 | LoadPic | Show chapter screen. | Picture ID |
13 | DeadlyWater | Unknown. Nothing changes in-game. Used in Temple of Xian. Maybe not-implemented ancestor of TR3 Death_by_Drowning ? | |
14 | RemoveWeapons | Lara starts the level with no weapons. | |
15 | GameComplete | End of game, show the final statistics and start the credits sequence. | |
16 | CutAngle | Match the North-South orientation of the Room Editor and the North-South orientation of the 3D animated characters from a CAD application. | Horizontal rotation ($\text{angle in degrees} \times 65536 \div 360$) |
17 | NoFloor | Lara dies when her feet reach the given depth. If falling, 4 to 5 extra blocks are added to Depth. | Depth ($\text{blocks} \times 1024$), relative to where Lara starts the level |
18 | StartInv / Bonus | Give item to lara at level-start (StartInv ) or at all-secrets-found (Bonus ). | Item ID |
19 | StartAnim | Lara starts the level with the given animation. | Animation ID |
20 | Secrets | If zero, the level does not account for secrets. Non-zero value means the level must be accounted for secrets. | |
21 | KillToComplete | Kill all enemies to finish the level. | |
22 | RemoveAmmo | Lara starts the level without ammunition or medi packs. |
The correct way to parse a sequence is to first read a uint16_t
opcode specifying what this command within the sequence does. In reference to the list above, certain commands MUST have an additional uint16_t
read from the sequence data directly after the opcode that’s the pairing operand to this opcode. Not all opcodes have an operand so this must be done correctly. The original games execute each sequence command 1 by 1 until it reaches End
(9), where it then runs the next sequence.
Opcode-18 StartInv and Bonus
(repeat means give another)
By default, the item is given at level start (StartInv
). Adding 1000 to the item ID means it will be given when all secrets are found (Bonus
).
ID | Tomb Raider 2 | Tomb Raider 3 |
---|---|---|
0 | Pistols | |
1 | Shotgun | |
2 | Automatic pistols | Desert Eagle |
3 | Uzis | |
4 | Harpoon gun | |
5 | M-16 | MP5 |
6 | Grenade launcher | Rocket launcher |
7 | Pistol clip (no effect, infinite by default) | Grenade launcher |
8 | Shotgun-shell box (adds 2 shells) | Pistol clip (no effect, infinite by default) |
9 | Automatic-pistol clip (adds 2 shells) | Shotgun-shell box (adds 2 shells) |
10 | Uzi clip (adds 2 shells) | Desert eagle clip (adds 5 shells) |
11 | Harpoon bundle (adds 2 harpoons) | Uzi clip (adds 2 shells) |
12 | M-16 clip (add 2 shells) | Harpoon bundle (adds 2 harpoons) |
13 | Grenade pack (adds 1 grenade) | MP5 clip (add 2 shells) |
14 | Flare box (adds 1 flare) | Rocket pack (adds 1 rocket) |
15 | Small medipack (adds 1 pack) | Grenade pack (adds 1 grenade) |
16 | Big medipack (adds 1 pack) | Flare box (adds 1 flare) |
17 | Pickup 1 | Small medipack (adds 1 pack) |
18 | Pickup 2 | Big medipack (adds 1 pack) |
19 | Puzzle 1 | Pickup 1 |
20 | Puzzle 2 | Pickup 2 |
21 | Puzzle 3 | Puzzle 1 |
22 | Puzzle 4 | Puzzle 2 |
23 | Key 1 | Puzzle 3 |
24 | Key 2 | Puzzle 4 |
25 | Key 3 | Key 1 |
26 | Key 4 | Key 2 |
27 | / | Key 3 |
28 | / | Key 4 |
29 | / | Save crystal |
Tomb Raider 2 Identifications
FMV IDs
- 0 — LOGO (everybody’s corporate logos)
- 1 — ANCIENT (monks vs. dragon)
- 2 — MODERN (Lara drops in from helicopter)
- 3 — LANDING (Seaplane lands at rig)
- 4 — MS (Lara hitchhikes on a minisub)
- 5 — CRASH (Lara goes to Tibet and has a rough landing there)
- 6 — JEEP (Lara steals it and outruns Bartoli’s goons)
- 7 — END (Lara escaping the collapsing lair)
Cutscene IDs
- 0 — CUT1 (At the end of the Great Wall)
- 1 — CUT2 (Lara the stowaway)
- 2 — CUT3 (Bartoli vs. goon)
- 3 — CUT4 (Bartoli stabs himself)
Soundtrack IDs
- 0 — BLANK (no sound)
- 3 — CUT1 (“at the fancy door” soundtrack)
- 4 — CUT2 (“Lara the stowaway” soundtrack)
- 5 — CUT3 (“Bartoli vs. goon” soundtrack)
- 30 — CUT4 (“Bartoli stabs himself” soundtrack)
- 31 — DERELICT (eerie choppy/echo-y synths)
- 32 — WATER (dripping/pouring water sounds)
- 33 — WIND (Blowing wind)
- 34 — HEARTBT (musical embellishment of one)
- 52 — SHOWER (that infamous shower scene)
- 58 — MACHINES (in the offshore rig)
- 59 — FLOATING (wispy synths)
Tomb Raider 3 Identifications
FMV IDs
- 0 — LOGO (everybody’s corporate logos)
- 1 — INTR (“The Meteorite”)
- 2 — SAIL (Lara meets Willard, after the India levels)
- 3 — CRSH (Plane crash, before the Antartica levels)
- 4 — ENDGAME
Cutscene IDs
- 0 — CUT6 (Lara at Tony's camp)
- 1 — CUT9 (Lara meets Tony again)
- 2 — CUT1 (Lara Meets Amputated Mercenary)
- 3 — CUT4 (Lara confronts tribesman about artifact)
- 4 — CUT2 (Saved by the Bell)
- 5 — CUT5 (Lara meets the leader of the Damned)
- 6 — CUT11 (Lara meets Sophia Leigh)
- 7 — CUT7 (Lara Drives Over Fence)
- 8 — CUT8 (Lara the stowaway in a delivery truck)
- 9 — CUT3 (Lara gets the artifacts to Willard)
- 10 — CUT12 (Lara watches Willard transform)
Other Script Commands
FirstOption
, TitleReplace
, OnDeathDemoMode
, OnDeathInGame
, OnDemoInterrupt
and OnDemoEnd
can also be setup to perform specific actions. For example, OnDeathInGame
will be set to “0x500” which loads the title screen when Lara dies in-game.
Name | Code | Operand | Description |
---|---|---|---|
Level | 0x00000000 | Level | Load specified level (0 means do nothing, 1 means first level (Great Wall or Jungle), 2 means second level) |
Demo | 0x00000400 | None | Load a random demo level |
ExitToTitle | 0x00000500 | Exit to Title Screen (or do action specified in TitleReplace if TitleDisabled flag is set) |
|
ExitGame | 0x00000700 | Exit the game | |
(anything else) | Exit the game |
Commands with operand are used like this: if you want to load the 2nd level, then you are going to do 0x000
(Level) + 2 which gives 0x003
.
0xFFFFFFFF
is often used by Core in fields to denotate that it should not happen. For example, in the standard (not demo)
games' scripts, the TitleReplace
field contains the value 0xFFFFFFFF
(which, as mentioned before, will crash-exit the game quietly) because this field should never be used, as the TitleDisabled
flag should never be set in standard games.