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