1 //----------------------------------------------------------------------------
2 //  EDGE New SaveGame Handling (Globals)
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 // TODO HERE:
23 //   + implement ReadWADS and ReadVIEW.
24 //
25 
26 #include "i_defs.h"
27 
28 #include <limits.h>
29 
30 #include "sv_chunk.h"
31 #include "sv_main.h"
32 #include "z_zone.h"
33 
34 // forward decls:
35 static void GV_GetInt(const char *info, void *storage);
36 static void GV_GetString(const char *info, void *storage);
37 static void GV_GetCheckCRC(const char *info, void *storage);
38 static void GV_GetLevelFlags(const char *info, void *storage);
39 static void GV_GetImage(const char *info, void *storage);
40 
41 static const char *GV_PutInt(void *storage);
42 static const char *GV_PutString(void *storage);
43 static const char *GV_PutCheckCRC(void *storage);
44 static const char *GV_PutLevelFlags(void *storage);
45 static const char *GV_PutImage(void *storage);
46 
47 
48 static saveglobals_t dummy_glob;
49 static saveglobals_t *cur_globs = NULL;
50 
51 typedef struct
52 {
53 	// global name
54 	const char *name;
55 
56 	// parse function.  `storage' is where the data should go (for
57 	// routines that don't modify the cur_globs structure directly).
58 	void (* parse_func)(const char *info, void *storage);
59 
60 	// stringify function.  Return string must be freed.
61 	const char * (* stringify_func)(void *storage);
62 
63 	// field offset (given as a pointer within dummy struct)
64 	const char *offset_p;
65 }
66 global_command_t;
67 
68 
69 #define GLOB_OFF(field)  ((const char *) &dummy_glob.field)
70 
71 static const global_command_t global_commands[] =
72 {
73 	{ "GAME",  GV_GetString, GV_PutString, GLOB_OFF(game) },
74 	{ "LEVEL", GV_GetString, GV_PutString, GLOB_OFF(level) },
75 	{ "FLAGS", GV_GetLevelFlags, GV_PutLevelFlags, GLOB_OFF(flags) },
76 	{ "HUB_TAG", GV_GetInt, GV_PutInt, GLOB_OFF(hub_tag) },
77 	{ "HUB_FIRST", GV_GetString, GV_PutString, GLOB_OFF(hub_first) },
78 
79 	{ "GRAVITY", GV_GetInt, GV_PutInt, GLOB_OFF(flags.menu_grav) },
80 	{ "LEVEL_TIME", GV_GetInt, GV_PutInt, GLOB_OFF(level_time) },
81 	{ "EXIT_TIME", GV_GetInt, GV_PutInt, GLOB_OFF(exit_time) },
82 	{ "P_RANDOM", GV_GetInt, GV_PutInt, GLOB_OFF(p_random) },
83 
84 	{ "TOTAL_KILLS",   GV_GetInt, GV_PutInt, GLOB_OFF(total_kills) },
85 	{ "TOTAL_ITEMS",   GV_GetInt, GV_PutInt, GLOB_OFF(total_items) },
86 	{ "TOTAL_SECRETS", GV_GetInt, GV_PutInt, GLOB_OFF(total_secrets) },
87 	{ "CONSOLE_PLAYER", GV_GetInt, GV_PutInt, GLOB_OFF(console_player) },
88 	{ "SKILL", GV_GetInt, GV_PutInt, GLOB_OFF(skill) },
89 	{ "NETGAME", GV_GetInt, GV_PutInt, GLOB_OFF(netgame) },
90 	{ "SKY_IMAGE", GV_GetImage, GV_PutImage, GLOB_OFF(sky_image) },
91 
92 	{ "DESCRIPTION", GV_GetString, GV_PutString, GLOB_OFF(description) },
93 	{ "DESC_DATE", GV_GetString, GV_PutString, GLOB_OFF(desc_date) },
94 
95 	{ "MAPSECTOR", GV_GetCheckCRC, GV_PutCheckCRC, GLOB_OFF(mapsector) },
96 	{ "MAPLINE",   GV_GetCheckCRC, GV_PutCheckCRC, GLOB_OFF(mapline) },
97 	{ "MAPTHING",  GV_GetCheckCRC, GV_PutCheckCRC, GLOB_OFF(mapthing) },
98 
99 	{ "RSCRIPT", GV_GetCheckCRC, GV_PutCheckCRC, GLOB_OFF(rscript) },
100 	{ "DDFATK",  GV_GetCheckCRC, GV_PutCheckCRC, GLOB_OFF(ddfatk) },
101 	{ "DDFGAME", GV_GetCheckCRC, GV_PutCheckCRC, GLOB_OFF(ddfgame) },
102 	{ "DDFLEVL", GV_GetCheckCRC, GV_PutCheckCRC, GLOB_OFF(ddflevl) },
103 	{ "DDFLINE", GV_GetCheckCRC, GV_PutCheckCRC, GLOB_OFF(ddfline) },
104 	{ "DDFSECT", GV_GetCheckCRC, GV_PutCheckCRC, GLOB_OFF(ddfsect) },
105 	{ "DDFMOBJ", GV_GetCheckCRC, GV_PutCheckCRC, GLOB_OFF(ddfmobj) },
106 	{ "DDFWEAP", GV_GetCheckCRC, GV_PutCheckCRC, GLOB_OFF(ddfweap) },
107 
108 	{ NULL, NULL, 0 }
109 };
110 
111 
112 //----------------------------------------------------------------------------
113 //
114 //  PARSERS
115 //
116 
GV_GetInt(const char * info,void * storage)117 static void GV_GetInt(const char *info, void *storage)
118 {
119 	int *dest = (int *)storage;
120 
121 	SYS_ASSERT(info && storage);
122 
123 	*dest = strtol(info, NULL, 0);
124 }
125 
GV_GetString(const char * info,void * storage)126 static void GV_GetString(const char *info, void *storage)
127 {
128 	char **dest = (char **)storage;
129 
130 	SYS_ASSERT(info && storage);
131 
132 	// free any previous string
133 	SV_FreeString(*dest);
134 
135 	if (info[0] == 0)
136 		*dest = NULL;
137 	else
138 		*dest = (char *) SV_DupString(info);
139 }
140 
GV_GetCheckCRC(const char * info,void * storage)141 static void GV_GetCheckCRC(const char *info, void *storage)
142 {
143 	crc_check_t *dest = (crc_check_t *)storage;
144 
145 	SYS_ASSERT(info && storage);
146 
147 	sscanf(info, "%d %u", &dest->count, &dest->crc);
148 }
149 
GV_GetLevelFlags(const char * info,void * storage)150 static void GV_GetLevelFlags(const char *info, void *storage)
151 {
152 	gameflags_t *dest = (gameflags_t *)storage;
153 	int flags;
154 
155 	SYS_ASSERT(info && storage);
156 
157 	flags = strtol(info, NULL, 0);
158 
159 	Z_Clear(dest, gameflags_t, 1);
160 
161 #define HANDLE_FLAG(var, specflag)  \
162 	(var) = (flags & (specflag)) ? true : false;
163 
164 	HANDLE_FLAG(dest->jump, MPF_Jumping);
165 	HANDLE_FLAG(dest->crouch, MPF_Crouching);
166 	HANDLE_FLAG(dest->mlook, MPF_Mlook);
167 	HANDLE_FLAG(dest->itemrespawn, MPF_ItemRespawn);
168 	HANDLE_FLAG(dest->fastparm, MPF_FastParm);
169 	HANDLE_FLAG(dest->true3dgameplay, MPF_True3D);
170 	HANDLE_FLAG(dest->more_blood, MPF_MoreBlood);
171 	HANDLE_FLAG(dest->cheats, MPF_Cheats);
172 	HANDLE_FLAG(dest->respawn, MPF_Respawn);
173 	HANDLE_FLAG(dest->res_respawn, MPF_ResRespawn);
174 	HANDLE_FLAG(dest->have_extra, MPF_Extras);
175 	HANDLE_FLAG(dest->limit_zoom, MPF_LimitZoom);
176 	HANDLE_FLAG(dest->shadows, MPF_Shadows);
177 	HANDLE_FLAG(dest->halos, MPF_Halos);
178 	HANDLE_FLAG(dest->kicking, MPF_Kicking);
179 	HANDLE_FLAG(dest->weapon_switch, MPF_WeaponSwitch);
180 	HANDLE_FLAG(dest->pass_missile, MPF_PassMissile);
181 	HANDLE_FLAG(dest->team_damage, MPF_TeamDamage);
182 
183 #undef HANDLE_FLAG
184 
185 	dest->edge_compat = (flags & MPF_BoomCompat) ? false : true;
186 
187 	dest->autoaim = (flags & MPF_AutoAim) ?
188 		((flags & MPF_AutoAimMlook) ? AA_MLOOK : AA_ON) : AA_OFF;
189 }
190 
GV_GetImage(const char * info,void * storage)191 static void GV_GetImage(const char *info, void *storage)
192 {
193 	// based on SR_LevelGetImage...
194 
195 	const image_c ** dest = (const image_c **)storage;
196 
197 	SYS_ASSERT(info && storage);
198 
199 	if (info[0] == 0)
200 	{
201 		(*dest) = NULL;
202 		return;
203 	}
204 
205 	if (info[1] != ':')
206 		I_Warning("GV_GetImage: invalid image string `%s'\n", info);
207 
208 	(*dest) = W_ImageParseSaveString(info[0], info + 2);
209 }
210 
211 
212 //----------------------------------------------------------------------------
213 //
214 //  STRINGIFIERS
215 //
216 
GV_PutInt(void * storage)217 static const char *GV_PutInt(void *storage)
218 {
219 	int *src = (int *)storage;
220 	char buffer[40];
221 
222 	SYS_ASSERT(storage);
223 
224 	sprintf(buffer, "%d", *src);
225 
226 	return SV_DupString(buffer);
227 }
228 
GV_PutString(void * storage)229 static const char *GV_PutString(void *storage)
230 {
231 	char **src = (char **)storage;
232 
233 	SYS_ASSERT(storage);
234 
235 	if (*src == NULL)
236 		return SV_DupString("");
237 
238 	return SV_DupString(*src);
239 }
240 
GV_PutCheckCRC(void * storage)241 static const char *GV_PutCheckCRC(void *storage)
242 {
243 	crc_check_t *src = (crc_check_t *)storage;
244 	char buffer[80];
245 
246 	SYS_ASSERT(storage);
247 
248 	sprintf(buffer, "%d %u", src->count, src->crc);
249 
250 	return SV_DupString(buffer);
251 }
252 
GV_PutLevelFlags(void * storage)253 static const char *GV_PutLevelFlags(void *storage)
254 {
255 	gameflags_t *src = (gameflags_t *)storage;
256 	int flags;
257 
258 	SYS_ASSERT(storage);
259 
260 	flags = 0;
261 
262 #define HANDLE_FLAG(var, specflag)  \
263 	if (var) flags |= (specflag);
264 
265 	HANDLE_FLAG(src->jump, MPF_Jumping);
266 	HANDLE_FLAG(src->crouch, MPF_Crouching);
267 	HANDLE_FLAG(src->mlook, MPF_Mlook);
268 	HANDLE_FLAG(src->itemrespawn, MPF_ItemRespawn);
269 	HANDLE_FLAG(src->fastparm, MPF_FastParm);
270 	HANDLE_FLAG(src->true3dgameplay, MPF_True3D);
271 	HANDLE_FLAG(src->more_blood, MPF_MoreBlood);
272 	HANDLE_FLAG(src->cheats, MPF_Cheats);
273 	HANDLE_FLAG(src->respawn, MPF_Respawn);
274 	HANDLE_FLAG(src->res_respawn, MPF_ResRespawn);
275 	HANDLE_FLAG(src->have_extra, MPF_Extras);
276 	HANDLE_FLAG(src->limit_zoom, MPF_LimitZoom);
277 	HANDLE_FLAG(src->shadows, MPF_Shadows);
278 	HANDLE_FLAG(src->halos, MPF_Halos);
279 	HANDLE_FLAG(src->kicking, MPF_Kicking);
280 	HANDLE_FLAG(src->weapon_switch, MPF_WeaponSwitch);
281 	HANDLE_FLAG(src->pass_missile, MPF_PassMissile);
282 	HANDLE_FLAG(src->team_damage, MPF_TeamDamage);
283 
284 #undef HANDLE_FLAG
285 
286 	if (!src->edge_compat)
287 		flags |= MPF_BoomCompat;
288 
289 	if (src->autoaim != AA_OFF)
290 		flags |= MPF_AutoAim;
291 	if (src->autoaim == AA_MLOOK)
292 		flags |= MPF_AutoAimMlook;
293 
294 	return GV_PutInt(&flags);
295 }
296 
GV_PutImage(void * storage)297 static const char *GV_PutImage(void *storage)
298 {
299 	// based on SR_LevelPutImage...
300 
301 	const image_c **src = (const image_c **)storage;
302 	char buffer[64];
303 
304 	SYS_ASSERT(storage);
305 
306 	if (*src == NULL)
307 		return SV_DupString("");
308 
309 	W_ImageMakeSaveString(*src, buffer, buffer + 2);
310 	buffer[1] = ':';
311 
312 	return SV_DupString(buffer);
313 }
314 
315 
316 //----------------------------------------------------------------------------
317 //
318 //  MISCELLANY
319 //
320 
SV_NewGLOB(void)321 saveglobals_t *SV_NewGLOB(void)
322 {
323 	saveglobals_t *globs;
324 
325 	globs = new saveglobals_t;
326 
327 	Z_Clear(globs, saveglobals_t, 1);
328 
329 	globs->exit_time = INT_MAX;
330 
331 	return globs;
332 }
333 
SV_FreeGLOB(saveglobals_t * globs)334 void SV_FreeGLOB(saveglobals_t *globs)
335 {
336 	SV_FreeString(globs->game);
337 	SV_FreeString(globs->level);
338 	SV_FreeString(globs->hub_first);
339 	SV_FreeString(globs->description);
340 	SV_FreeString(globs->desc_date);
341 
342 	if (globs->view_pixels)
343 		delete[] globs->view_pixels;
344 
345 	if (globs->wad_names)
346 		delete[] globs->wad_names;
347 
348 	delete globs;
349 }
350 
351 
352 //----------------------------------------------------------------------------
353 //
354 //  LOADING GLOBALS
355 //
356 
GlobReadVARI(saveglobals_t * globs)357 static bool GlobReadVARI(saveglobals_t *globs)
358 {
359 	const char *var_name;
360 	const char *var_data;
361 
362 	int i;
363 	void *storage;
364 
365 	if (! SV_PushReadChunk("Vari"))
366 		return false;
367 
368 	var_name = SV_GetString();
369 	var_data = SV_GetString();
370 
371 	if (! SV_PopReadChunk() || !var_name || !var_data)
372 	{
373 		SV_FreeString(var_name);
374 		SV_FreeString(var_data);
375 
376 		return false;
377 	}
378 
379 	// find variable in list
380 	for (i=0; global_commands[i].name; i++)
381 	{
382 		if (strcmp(global_commands[i].name, var_name) == 0)
383 			break;
384 	}
385 
386 	if (global_commands[i].name)
387 	{
388 		int offset = global_commands[i].offset_p - (char *) &dummy_glob;
389 
390 		// found it, so parse it
391 		storage = ((char *) globs) + offset;
392 
393 		(* global_commands[i].parse_func)(var_data, storage);
394 	}
395 	else
396 	{
397 		I_Debugf("GlobReadVARI: unknown global: %s\n", var_name);
398 	}
399 
400 	SV_FreeString(var_name);
401 	SV_FreeString(var_data);
402 
403 	return true;
404 }
405 
GlobReadWADS(saveglobals_t * glob)406 static bool GlobReadWADS(saveglobals_t *glob)
407 {
408 	if (! SV_PushReadChunk("Wads"))
409 		return false;
410 
411 	//!!! IMPLEMENT THIS
412 
413 	SV_PopReadChunk();
414 	return true;
415 }
416 
GlobReadVIEW(saveglobals_t * glob)417 static bool GlobReadVIEW(saveglobals_t *glob)
418 {
419 	if (! SV_PushReadChunk("View"))
420 		return false;
421 
422 	//!!! IMPLEMENT THIS
423 
424 	SV_PopReadChunk();
425 	return true;
426 }
427 
428 
SV_LoadGLOB(void)429 saveglobals_t *SV_LoadGLOB(void)
430 {
431 	char marker[6];
432 
433 	saveglobals_t *globs;
434 
435 	SV_GetMarker(marker);
436 
437 	if (strcmp(marker, "Glob") != 0 || ! SV_PushReadChunk("Glob"))
438 		return NULL;
439 
440 	cur_globs = globs = SV_NewGLOB();
441 
442 	// read through all the chunks, picking the bits we need
443 
444 	for (;;)
445 	{
446 		if (SV_GetError() != 0)
447 			break;  /// set error !!
448 
449 		if (SV_RemainingChunkSize() == 0)
450 			break;
451 
452 		SV_GetMarker(marker);
453 
454 		if (strcmp(marker, "Vari") == 0)
455 		{
456 			GlobReadVARI(globs);
457 			continue;
458 		}
459 		if (strcmp(marker, "Wads") == 0)
460 		{
461 			GlobReadWADS(globs);
462 			continue;
463 		}
464 		if (strcmp(marker, "View") == 0)
465 		{
466 			GlobReadVIEW(globs);
467 			continue;
468 		}
469 
470 		// skip chunk
471 		I_Warning("LOADGAME: Unknown GLOB chunk [%s]\n", marker);
472 
473 		if (! SV_SkipReadChunk(marker))
474 			break;
475 	}
476 
477 	SV_PopReadChunk();  /// check err
478 
479 	return globs;
480 }
481 
482 
483 //----------------------------------------------------------------------------
484 //
485 //  SAVING GLOBALS
486 //
487 
GlobWriteVARIs(saveglobals_t * globs)488 static void GlobWriteVARIs(saveglobals_t *globs)
489 {
490 	int i;
491 
492 	for (i=0; global_commands[i].name; i++)
493 	{
494 		int offset = global_commands[i].offset_p - (char *) &dummy_glob;
495 
496 		void *storage = ((char *) globs) + offset;
497 
498 		const char *data = (* global_commands[i].stringify_func)(storage);
499 		SYS_ASSERT(data);
500 
501 		SV_PushWriteChunk("Vari");
502 		SV_PutString(global_commands[i].name);
503 		SV_PutString(data);
504 		SV_PopWriteChunk();
505 
506 		SV_FreeString(data);
507 	}
508 }
509 
GlobWriteWADS(saveglobals_t * globs)510 static void GlobWriteWADS(saveglobals_t *globs)
511 {
512 	int i;
513 
514 	if (! globs->wad_names)
515 		return;
516 
517 	SYS_ASSERT(globs->wad_num > 0);
518 
519 	SV_PushWriteChunk("Wads");
520 	SV_PutInt(globs->wad_num);
521 
522 	for (i=0; i < globs->wad_num; i++)
523 		SV_PutString(globs->wad_names[i]);
524 
525 	SV_PopWriteChunk();
526 }
527 
GlobWriteVIEW(saveglobals_t * globs)528 static void GlobWriteVIEW(saveglobals_t *globs)
529 {
530 	int x, y;
531 
532 	if (! globs->view_pixels)
533 		return;
534 
535 	SYS_ASSERT(globs->view_width  > 0);
536 	SYS_ASSERT(globs->view_height > 0);
537 
538 	SV_PushWriteChunk("View");
539 
540 	SV_PutInt(globs->view_width);
541 	SV_PutInt(globs->view_height);
542 
543 	for (y=0; y < globs->view_height; y++)
544 	{
545 		for (x=0; x < globs->view_width; x++)
546 		{
547 			SV_PutShort(globs->view_pixels[y * globs->view_width + x]);
548 		}
549 	}
550 
551 	SV_PopWriteChunk();
552 }
553 
554 
SV_SaveGLOB(saveglobals_t * globs)555 void SV_SaveGLOB(saveglobals_t *globs)
556 {
557 	cur_globs = globs;
558 
559 	SV_PushWriteChunk("Glob");
560 
561 	GlobWriteVARIs(globs);
562 	GlobWriteWADS(globs);
563 	GlobWriteVIEW(globs);
564 
565 	// all done
566 	SV_PopWriteChunk();
567 }
568 
569 //--- editor settings ---
570 // vi:ts=4:sw=4:noexpandtab
571