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