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