original research by sapper, minor improvements by zdimension
struct TR4Savegame { uint8_t levelName[75]; // CP437 C string; null-terminated (data after \0 is ignored) int32_t saveNumber; uint16_t days; uint16_t hours; uint16_t minutes; uint16_t seconds; int16_t laraIndex; int16_t laraArmAction; int16_t currentItemInHand; int16_t changeToItemInHand; int16_t activeWeapon; uint8_t unknown2[6]; int16_t laraAir; uint8_t unknown3[20]; int16_t weaponOnBack; uint8_t unknown3b[2]; int16_t laraPoison1; int16_t laraPoison2; uint8_t unknown3a[16]; uint8_t laraSpecialStatus; // 0x4 = Normal; 0xC = Burning Lara; Anything other = save doesn't appear uint8_t unknown4[95]; int16_t laraFacing; // Matches data for item below uint8_t unknown4a[108]; uint8_t pistols; // 0 = missing; 9 = present uint8_t uzis; // 0 = missing; 1 = present uint8_t shotgun; // 0 = missing; 9 = present uint8_t crossbow; // 0 = missing; 9 = present; 13 = combined with lasersight uint8_t grenadeGun; // 0 = missing; 9 = present uint8_t revolver; // 0 = missing; 1 = present; 5 = combined with lasersight uint8_t laserSight; // 0 = missing or combined; 1 = present separated uint8_t binoculars; uint8_t crowbar; uint8_t mechanicalScarab; uint8_t smallWaterSkin; int8_t largeWaterSkin; uint8_t examine[3]; uint8_t puzzleItems[12]; uint8_t puzzleItem1_4Combo; uint8_t puzzleItem5_8Combo; uint8_t keyItem1_8; uint8_t keyItem9_12; uint8_t keyItem1_4Combo; uint8_t keyItem5_8Combo; uint8_t pickupItem1_4; uint8_t unknown5; uint8_t pickupItem1_4Combo; uint8_t unknown6; uint8_t questItem1_6; uint8_t unknown7; int16_t smallMediPacks; // 65535 means unlimited int16_t largeMediPacks; // 65535 means unlimited int16_t flares; // 65535 means unlimited int16_t pistolAmmo; int16_t uziAmmo; int16_t revolverAmmo; int16_t shotgunAmmo[2]; // normal; wide, divide by 6 for real value int16_t grenadeAmmo[3]; // normal; super; flash int16_t crossbowAmmo[3]; // normal; poison; explosive int16_t mechanicalScarabRemaining; uint8_t unknown8[57]; uint8_t levelNumber; uint8_t unknown9; // clock?? uint32_t timeTaken; // in game ticks (1/30th of a second), so divide by 30 for time in seconds uint32_t distanceTravelled; // Divide by 419.15 for value in meters int32_t ammoUsed; int32_t hits; uint8_t unknown10[2]; uint8_t secrets; uint8_t healthPacksUsed; uint8_t unknown11[16]; uint16_t kills; uint8_t unknown11a[11]; struct { uint8_t unused; uint8_t blue; uint8_t green; uint8_t red; } fogColour; int8_t autoAimingForEnemy; uint8_t unknown12[172]; uint8_t theGreatUnknown[]; // When reading a TR4 savegame, first jump to EOF - 8 and check if the string "NGLE" // is present. If that is the case, then it is a NGLE savegame. // It means that the "standard" TR4 save info (theGreatUnknown) will have // a length of (file length) - (pos of theGreatUnknown) - sizof(ngHeader) #if NGLE struct { uint8_t data[size - 8]; uint8_t signature[4]; // ASCII string; always "NGLE" uint32_t size; // size of the file - size of ngHeader } ngHeader; #endif };