TR4

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
};