1 #ifndef H_SAVEGAME
2 #define H_SAVEGAME
3 
4 #include "utils.h"
5 
6 #define MAX_FLIPMAP_COUNT     32
7 #define MAX_TRACKS_COUNT      256
8 
9 #define SAVE_FILENAME       "savegame.dat"
10 #define SAVE_MAGIC          FOURCC("OLS2")
11 
12 enum SaveResult {
13     SAVE_RESULT_SUCCESS,
14     SAVE_RESULT_ERROR,
15     SAVE_RESULT_WAIT,
16 };
17 
18 struct SaveItem {
19     uint16 type;
20     uint16 count;
21 };
22 
23 struct SaveStats {
24     uint32 level:31;
25     uint32 checkpoint:1;
26     uint32 time;
27     uint32 distance;
28     uint32 secrets;
29     uint32 pickups;
30     uint32 mediUsed;
31     uint32 ammoUsed;
32     uint32 kills;
33 };
34 
35 struct SaveEntity {
36 // base
37     int32  x, y, z;
38     uint16 rotation;
39     uint16 type;
40     uint16 flags;
41     int16  timer;
42 // animation
43     uint16 animIndex;
44     uint16 animFrame;
45 // common
46     uint16 room;
47     uint16 extraSize;
48     union Extra {
49         struct {
50             float  velX, velY, velZ;
51             float  angleX;
52             float  health;
53             float  oxygen;
54             float  stamina;
55             float  poison;
56             float  freeze;
57             uint16 reserved;
58             uint16 itemWeapon;
59             uint16 itemHands;
60             uint16 itemBack;
61             uint16 itemHolster;
62             union {
63                 struct { uint16 wet:1, burn:1; };
64                 uint16 value;
65             } spec;
66         } lara;
67         struct {
68             float  health;
69             uint16 targetBox;
70             union {
71                 struct { uint16 mood:3; };
72                 uint16 value;
73             } spec;
74         } enemy;
75     } extra;
76 };
77 
78 struct SaveState {
79     struct ByteFlags {
80         uint8 once:1, active:5, :2;
81     };
82 
83     ByteFlags flipmaps[MAX_FLIPMAP_COUNT];
84     ByteFlags tracks[MAX_TRACKS_COUNT];
85 
86     union {
87         struct { uint32 track:8, flipped:1; };
88         uint32 value;
89     } flags;
90 };
91 
92 struct SaveSlot {
93     uint32 size;
94     uint8  *data;
95 
getLevelIDSaveSlot96     TR::LevelID getLevelID() const {
97         return TR::LevelID(((SaveStats*)data)->level);
98     }
99 
isCheckpointSaveSlot100     bool isCheckpoint() const {
101         return ((SaveStats*)data)->checkpoint;
102     }
103 
cmpSaveSlot104     static int cmp(const SaveSlot &a, const SaveSlot &b) {
105         uint32 ia = *(uint32*)a.data; // level + checkpoint flag
106         uint32 ib = *(uint32*)b.data;
107         if (ia < ib) return -1;
108         if (ia > ib) return +1;
109         return 0;
110     }
111 };
112 
113 Array<SaveSlot> saveSlots;
114 SaveResult      saveResult;
115 int             loadSlot;
116 SaveStats       saveStats;
117 
freeSaveSlots()118 void freeSaveSlots() {
119     for (int i = 0; i < saveSlots.length; i++)
120         delete[] saveSlots[i].data;
121     saveSlots.clear();
122 }
123 
readSaveSlots(Stream * stream)124 void readSaveSlots(Stream *stream) {
125     uint32 magic;
126     if (stream->size < 4 || stream->read(magic) != SAVE_MAGIC)
127         return;
128 
129     freeSaveSlots();
130 
131     SaveSlot slot;
132     while (stream->pos < stream->size) {
133         stream->read(slot.size);
134         stream->read(slot.data, slot.size);
135         saveSlots.push(slot);
136     }
137 }
138 
writeSaveSlots(int & size)139 uint8* writeSaveSlots(int &size) {
140     size = 4;
141     for (int i = 0; i < saveSlots.length; i++)
142         size += 4 + saveSlots[i].size;
143 
144     uint8 *data = new uint8[size];
145     uint8 *ptr  = data;
146 
147     uint32 *magic = (uint32*)ptr;
148     ptr += sizeof(*magic);
149     *magic = SAVE_MAGIC;
150 
151     for (int i = 0; i < saveSlots.length; i++) {
152         SaveSlot &s = saveSlots[i];
153         memcpy(ptr + 0, &s.size,  4);
154         memcpy(ptr + 4, s.data,   s.size);
155         ptr += 4 + s.size;
156     }
157 
158     return data;
159 }
160 
removeSaveSlot(TR::LevelID levelID,bool checkpoint)161 void removeSaveSlot(TR::LevelID levelID, bool checkpoint) {
162     TR::Version version = TR::getGameVersionByLevel(levelID);
163 
164     for (int i = 0; i < saveSlots.length; i++) {
165         SaveSlot &slot = saveSlots[i];
166 
167         TR::LevelID id = slot.getLevelID();
168 
169         if (TR::getGameVersionByLevel(id) != version)
170             continue;
171 
172         if (slot.isCheckpoint() || (!checkpoint && levelID == id)) {
173             delete[] slot.data;
174             saveSlots.remove(i);
175             i--;
176             continue;
177         }
178     }
179 }
180 
getSaveSlot(TR::LevelID levelID,bool checkpoint)181 int getSaveSlot(TR::LevelID levelID, bool checkpoint) {
182     TR::Version version = TR::getGameVersionByLevel(levelID);
183 
184     for (int i = 0; i < saveSlots.length; i++) {
185         SaveSlot &slot = saveSlots[i];
186 
187         TR::LevelID id = slot.getLevelID();
188 
189         if (TR::getGameVersionByLevel(id) != version)
190             continue;
191 
192         if ((checkpoint && slot.isCheckpoint()) || (!checkpoint && levelID == id))
193             return i;
194     }
195 
196     return -1;
197 }
198 
199 #endif
200