Friday 8 May 2020

3: Tomb Raider 3 source code notes

The savegame structure will be studied in this post.


The game is saved in the ControlPhase() routine found in CONTROL.C.
CreateSaveGameInfo();
S_SaveGame(&savegame, sizeof(SAVEGAME_INFO), Inventory_ExtraData[1]);
S_SaveSettings();
CreateSaveGameInfo() populates the “savegame” SAVEGAME_INFO struct found in SAVEGAME.H.

The SAVEGAME_INFO struct

START_INFO start[25]; //[MAXIMUM_LEVELS+1]
uint32 timer;
uint32 ammo_used, ammo_hit;
uint32 distance_travelled;
uint16 kills;
unsigned char secrets; // bit packed
unsigned char health_used; // in units of small medi paks
/* Stuff for current position. */
uint32 cp_timer;
uint32 cp_ammo_used, cp_ammo_hit;
uint32 cp_distance_travelled;
uint16 cp_kills;
unsigned char cp_secrets; // bit packed
unsigned char cp_health_used; // in units of small medi paks
sint16 current_level;
unsigned char bonus_flag;
unsigned char num_pickup1, num_pickup2;
unsigned char num_puzzle1, num_puzzle2, num_puzzle3, num_puzzle4;
unsigned char num_key1, num_key2, num_key3, num_key4;
signed char checksum;
signed char GameComplete;
uint32 mid_level_save:1;
uint32 AfterAdventureSave:1;
uint32 WorldRequired:3;
uint32 IndiaComplete:1;
uint32 SPacificComplete:1;
uint32 LondonComplete:1;
uint32 NevadaComplete:1;
uint32 AntarcticaComplete:1;
uint32 PeruComplete:1;
uint32 AfterIndia:3;
uint32 AfterSPacific:3;
uint32 AfterLondon:3;
uint32 AfterNevada:3;
char buffer[12*1024+128]; //[MAX_SAVEGAME_BUFFER]
The SAVEGAME_INFO struct begins with twenty five START_INFO structs. Only one of the START_INFO structs is populated via CreateStartInfo(level_number) in SAVEGAME.C.

Note that since the retail TR3 savegame has no padding in the structs discussed here, when building the source code go to the project’s settings and set Configuration Properties> C/C++> Code Generation> Struct Member Alignment to 1 Byte.

The size of the SAVEGAME_INFO struct is 13750 bytes.

START_INFO struct

uint16 pistol_ammo, magnum_ammo, uzi_ammo, shotgun_ammo, m16_ammo;
uint16 rocket_ammo, harpoon_ammo, grenade_ammo;
unsigned char num_medis, num_big_medis, num_scions, num_flares;
unsigned char num_sgcrystals;
char gun_status, gun_type;
uint16 available:1;
uint16 got_pistols:1;
uint16 got_magnums:1;
uint16 got_uzis:1;
uint16 got_shotgun:1;
uint16 got_m16:1;
uint16 got_rocket:1;
uint16 got_grenade:1;
uint16 got_harpoon:1;
uint16 secrets; // actually records whether player has secrets at end of level
unsigned char num_icon1, num_icon2, num_icon3, num_icon4;
uint32 timer;
uint32 ammo_used, ammo_hit;
uint32 distance_travelled;
uint16 kills;
unsigned char secrets_found; // bit packed
unsigned char health_used; // in units of small medi paks
The size of the START_INFO struct is 51 bytes.

buffer


The SAVEGAME_INFO struct ends with a buffer where other info pertaining to the current level, its objects and Lara is written in CreateSaveGameInfo() starting at line 368.



Since each level will contain different objects in a different order the location of particular items in the buffer data will differ between levels (see the loop through all the items) which is why creating a savegame editor or position editor is so difficult.

Also, creating a level with many animated objects may mean the buffer is too small for all the data. 

The SAVEGAME_INFO struct is passed to S_SaveGame() found in GAME.CPP. It is here that the savegame is written to disk.

First the level name is written, 75 bytes.

Then the save count is written, 4 bytes (sint32).

Finally the SAVEGAME_INFO struct is written, 13750 bytes.

So the total size of a savegame file is 13829 bytes from the tomb21 code.

This released source code is not the final version as we have stated before but further proof is that a savegame file from the retail TR3 (GOG version) is 13913 bytes, 84 bytes larger.

It seems that these unknown 84 bytes are located between the save count field and the beginning of the SAVEGAME_INFO struct. They may be part of the struct but their meaning is unknown. The 84 bytes are all zero in the savegames I studied.

The tomb21 code has routines to calculate and validate a checksum for the SAVEGAME_INFO struct in the savegame file but they are not called anywhere which appears to be the case also with the retail version since the checksum field is always zero.

1 comment:

  1. Great post , thanks .
    I'm going to study on the source .
    please go on and share your studies result with us , sapper

    ReplyDelete