1 //----------------------------------------------------------------------------
2 // EDGE New SaveGame Handling (Loading)
3 //----------------------------------------------------------------------------
4 //
5 // Copyright (c) 1999-2009 The EDGE Team.
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License
9 // as published by the Free Software Foundation; either version 2
10 // of the License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
16 //
17 //----------------------------------------------------------------------------
18 //
19 // See the file "docs/save_sys.txt" for a complete description of the
20 // new savegame system.
21 //
22
23 #include "i_defs.h"
24
25 #include "dm_state.h"
26 #include "e_main.h"
27 #include "g_game.h"
28 #include "f_interm.h"
29 #include "m_math.h"
30 #include "m_random.h"
31 #include "p_local.h"
32 #include "p_spec.h"
33 #include "r_state.h"
34 #include "sv_chunk.h"
35 #include "sv_main.h"
36 #include "w_wad.h"
37 #include "z_zone.h"
38
39
40 static savestruct_t *loaded_struct_list;
41 static savearray_t *loaded_array_list;
42
43 bool sv_loading_hub;
44
45
46 //----------------------------------------------------------------------------
47 //
48 // ADMININISTRATION
49 //
50
AddLoadedStruct(savestruct_t * S)51 static void AddLoadedStruct(savestruct_t *S)
52 {
53 S->next = loaded_struct_list;
54 loaded_struct_list = S;
55 }
56
AddLoadedArray(savearray_t * A)57 static void AddLoadedArray(savearray_t *A)
58 {
59 A->next = loaded_array_list;
60 loaded_array_list = A;
61 }
62
SV_LookupLoadedStruct(const char * name)63 savestruct_t *SV_LookupLoadedStruct(const char *name)
64 {
65 savestruct_t *S;
66
67 for (S = loaded_struct_list; S; S = S->next)
68 if (strcmp(S->struct_name, name) == 0)
69 return S;
70
71 // not found
72 return NULL;
73 }
74
SV_LookupLoadedArray(const char * name)75 savearray_t *SV_LookupLoadedArray(const char *name)
76 {
77 savearray_t *A;
78
79 for (A = loaded_array_list; A; A = A->next)
80 if (strcmp(A->array_name, name) == 0)
81 return A;
82
83 // not found
84 return NULL;
85 }
86
87
88 //----------------------------------------------------------------------------
89 //
90 // LOADING STUFF
91 //
92
SV_BeginLoad(bool is_hub)93 void SV_BeginLoad(bool is_hub)
94 {
95 sv_loading_hub = is_hub;
96
97 /* Prepare main code for loading, e.g. initialise some lists */
98
99 savestruct_t *S;
100 savearray_t *A;
101
102 L_WriteDebug("SV_BeginLoad...\n");
103
104 loaded_struct_list = NULL;
105 loaded_array_list = NULL;
106
107 // clear counterpart fields
108 for (S=sv_known_structs; S; S=S->next)
109 S->counterpart = NULL;
110
111 for (A=sv_known_arrays; A; A=A->next)
112 A->counterpart = NULL;
113 }
114
LoadFreeStruct(savestruct_t * S)115 static void LoadFreeStruct(savestruct_t *S)
116 {
117 SV_FreeString(S->struct_name);
118 SV_FreeString(S->marker);
119
120 delete[] S->fields;
121
122 delete S;
123 }
124
LoadFreeArray(savearray_t * A)125 static void LoadFreeArray(savearray_t *A)
126 {
127 SV_FreeString(A->array_name);
128
129 delete A;
130 }
131
SV_FinishLoad(void)132 void SV_FinishLoad(void)
133 {
134 // Finalise all the arrays, and free some stuff after loading
135 // has finished.
136
137 L_WriteDebug("SV_FinishLoad...\n");
138
139 while (loaded_struct_list)
140 {
141 savestruct_t *S = loaded_struct_list;
142 loaded_struct_list = S->next;
143
144 LoadFreeStruct(S);
145 }
146
147 while (loaded_array_list)
148 {
149 savearray_t *A = loaded_array_list;
150 loaded_array_list = A->next;
151
152 if (A->counterpart &&
153 (!sv_loading_hub || A->counterpart->allow_hub))
154 {
155 (* A->counterpart->finalise_elems)();
156 }
157
158 LoadFreeArray(A);
159 }
160 }
161
StructFindField(savestruct_t * info,const char * name)162 static savefield_t *StructFindField(savestruct_t *info, const char *name)
163 {
164 savefield_t *F;
165
166 for (F = info->fields; F->type.kind != SFKIND_Invalid; F++)
167 {
168 if (strcmp(name, F->field_name) == 0)
169 return F;
170 }
171
172 return NULL;
173 }
174
StructSkipField(savefield_t * field)175 static void StructSkipField(savefield_t *field)
176 {
177 char marker[6];
178 int i;
179
180 switch (field->type.kind)
181 {
182 case SFKIND_Struct:
183 SV_GetMarker(marker);
184 //!!! compare marker with field->type.name
185 SV_SkipReadChunk(marker);
186 break;
187
188 case SFKIND_String:
189 SV_FreeString(SV_GetString());
190 break;
191
192 case SFKIND_Numeric:
193 case SFKIND_Index:
194 for (i=0; i < field->type.size; i++)
195 SV_GetByte();
196 break;
197
198 default:
199 I_Error("SV_LoadStruct: BAD TYPE IN FIELD.\n");
200 }
201 }
202
SV_LoadStruct(void * base,savestruct_t * info)203 bool SV_LoadStruct(void *base, savestruct_t *info)
204 {
205 // the savestruct_t here is the "loaded" one.
206
207 char marker[6];
208
209 SV_GetMarker(marker);
210
211 if (strcmp(marker, info->marker) != 0 || ! SV_PushReadChunk(marker))
212 return false;
213
214 for (savefield_t *F = info->fields; F->type.kind != SFKIND_Invalid; F++)
215 {
216 savefield_t *actual = F->known_field;
217
218 // if this field no longer exists, ignore it
219 if (! actual)
220 {
221 for (int i=0; i < F->count; i++)
222 StructSkipField(F);
223 continue;
224 }
225
226 SYS_ASSERT(actual->field_get);
227 SYS_ASSERT(info->counterpart);
228
229 int offset = actual->offset_p - info->counterpart->dummy_base;
230
231 char *storage = ((char *) base) + offset;
232
233 for (int i=0; i < F->count; i++)
234 {
235 // if there are extra elements in the savegame, ignore them
236 if (i >= actual->count)
237 {
238 StructSkipField(F);
239 continue;
240 }
241 switch (actual->type.kind)
242 {
243 case SFKIND_Struct:
244 case SFKIND_Index:
245 (* actual->field_get)(storage, i, (char *)actual->type.name);
246 break;
247
248 default:
249 (* actual->field_get)(storage, i, NULL);
250 break;
251 }
252 }
253 }
254
255 SV_PopReadChunk();
256
257 return true;
258 }
259
SV_LoadSTRU(void)260 static bool SV_LoadSTRU(void)
261 {
262 savestruct_t *S = new savestruct_t;
263
264 Z_Clear(S, savestruct_t, 1);
265
266 int numfields = SV_GetInt();
267
268 S->struct_name = SV_GetString();
269 S->counterpart = SV_MainLookupStruct(S->struct_name);
270
271 // make the counterparts refer to each other
272 if (S->counterpart)
273 {
274 SYS_ASSERT(S->counterpart->counterpart == NULL);
275 S->counterpart->counterpart = S;
276 }
277
278 S->marker = SV_GetString();
279
280 if (strlen(S->marker) != 4)
281 I_Error("LOADGAME: Corrupt savegame (STRU bad marker)\n");
282
283 S->fields = new savefield_t[numfields+1];
284
285 Z_Clear(S->fields, savefield_t, numfields+1);
286
287 //
288 // -- now load in all the fields --
289 //
290
291 int i;
292 savefield_t *F;
293
294 for (i=0, F=S->fields; i < numfields; i++, F++)
295 {
296 F->type.kind = (savefieldkind_e) SV_GetByte();
297 F->type.size = SV_GetByte();
298 F->count = SV_GetShort();
299 F->field_name = SV_GetString();
300
301 if (F->type.kind == SFKIND_Struct ||
302 F->type.kind == SFKIND_Index)
303 {
304 F->type.name = SV_GetString();
305 }
306
307 F->known_field = NULL;
308
309 if (S->counterpart)
310 F->known_field = StructFindField(S->counterpart, F->field_name);
311
312 // ??? compare names for STRUCT and INDEX
313 }
314
315 // terminate the array
316 F->type.kind = SFKIND_Invalid;
317
318 AddLoadedStruct(S);
319
320 return true;
321 }
322
SV_LoadARRY(void)323 static bool SV_LoadARRY(void)
324 {
325 savearray_t *A = new savearray_t;
326
327 Z_Clear(A, savearray_t, 1);
328
329 A->loaded_size = SV_GetInt();
330
331 A->array_name = SV_GetString();
332 A->counterpart = SV_MainLookupArray(A->array_name);
333
334 // make the counterparts refer to each other
335 if (A->counterpart)
336 {
337 SYS_ASSERT(A->counterpart->counterpart == NULL);
338 A->counterpart->counterpart = A;
339 }
340
341 const char *struct_name = SV_GetString();
342
343 A->sdef = SV_LookupLoadedStruct(struct_name);
344
345 if (A->sdef == NULL)
346 I_Error("LOADGAME: Coding Error ! (no STRU `%s' for ARRY)\n", struct_name);
347
348 SV_FreeString(struct_name);
349
350 // create array
351 if (A->counterpart &&
352 (!sv_loading_hub || A->counterpart->allow_hub))
353 {
354 (* A->counterpart->create_elems)(A->loaded_size);
355 }
356
357 AddLoadedArray(A);
358
359 return true;
360 }
361
SV_LoadDATA(void)362 static bool SV_LoadDATA(void)
363 {
364 const char *array_name = SV_GetString();
365
366 savearray_t *A = SV_LookupLoadedArray(array_name);
367
368 if (! A)
369 I_Error("LOADGAME: Coding Error ! (no ARRY `%s' for DATA)\n", array_name);
370
371 SV_FreeString(array_name);
372
373 for (int i=0; i < A->loaded_size; i++)
374 {
375 //??? check error too ???
376 if (SV_RemainingChunkSize() == 0)
377 return false;
378
379 if (A->counterpart &&
380 (!sv_loading_hub || A->counterpart->allow_hub))
381 {
382 sv_current_elem = (* A->counterpart->get_elem)(i);
383
384 if (! sv_current_elem)
385 I_Error("SV_LoadDATA: FIXME: skip elems\n");
386
387 if (! SV_LoadStruct(sv_current_elem, A->sdef))
388 return false;
389 }
390 else
391 {
392 // SKIP THE WHOLE STRUCT
393 char marker[6];
394 SV_GetMarker(marker);
395
396 if (! SV_SkipReadChunk(marker))
397 return false;
398 }
399 }
400
401 /// if (SV_RemainingChunkSize() != 0) //???
402 /// return false;
403
404 return true;
405 }
406
SV_LoadEverything(void)407 bool SV_LoadEverything(void)
408 {
409 bool result;
410
411 for (;;)
412 {
413 if (SV_GetError() != 0)
414 break; /// FIXME: set error !!
415
416 char marker[6];
417
418 SV_GetMarker(marker);
419
420 if (strcmp(marker, DATA_END_MARKER) == 0)
421 break;
422
423 // Structure Area
424 if (strcmp(marker, "Stru") == 0)
425 {
426 SV_PushReadChunk("Stru");
427 result = SV_LoadSTRU();
428 result = SV_PopReadChunk() && result;
429
430 if (! result)
431 return false;
432
433 continue;
434 }
435
436 // Array Area
437 if (strcmp(marker, "Arry") == 0)
438 {
439 SV_PushReadChunk("Arry");
440 result = SV_LoadARRY();
441 result = SV_PopReadChunk() && result;
442
443 if (! result)
444 return false;
445
446 continue;
447 }
448
449 // Data Area
450 if (strcmp(marker, "Data") == 0)
451 {
452 SV_PushReadChunk("Data");
453 result = SV_LoadDATA();
454 result = SV_PopReadChunk() && result;
455
456 if (! result)
457 return false;
458
459 continue;
460 }
461
462 I_Warning("LOADGAME: Unexpected top-level chunk [%s]\n", marker);
463
464 if (! SV_SkipReadChunk(marker))
465 return false;
466 }
467
468 return true;
469 }
470
471
472 //--- editor settings ---
473 // vi:ts=4:sw=4:noexpandtab
474