From c290e9a988ad69a59728cc724f996bc03b6b67f9 Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Sat, 9 Dec 2006 02:35:44 +0000 Subject: [PATCH] use plists for save-games. old save-games can still be read, but new ones will always be in the new format. might be a good idea to back up any old saves until more testing has been done. --- include/QF/progs.h | 4 +- libs/gamecode/engine/pr_parse.c | 83 +++++----- nq/source/host_cmd.c | 262 ++++++++++++++++++++------------ 3 files changed, 206 insertions(+), 143 deletions(-) diff --git a/include/QF/progs.h b/include/QF/progs.h index 99cfded82..c92b7770f 100644 --- a/include/QF/progs.h +++ b/include/QF/progs.h @@ -232,8 +232,8 @@ struct script_s; struct plitem_s; qboolean ED_ParseEpair (progs_t *pr, pr_type_t *base, ddef_t *key, const char *s); -void ED_Write (progs_t *pr, QFile *f, edict_t *ed); -void ED_WriteGlobals (progs_t *pr, QFile *f); +struct plitem_s *ED_EntityDict (progs_t *pr, edict_t *ed); +struct plitem_s *ED_GlobalsDict (progs_t *pr); void ED_InitGlobals (progs_t *pr, struct plitem_s *globals); void ED_InitEntity (progs_t *pr, struct plitem_s *entity, edict_t *ent); struct plitem_s *ED_ConvertToPlist (progs_t *pr, struct script_s *script); diff --git a/libs/gamecode/engine/pr_parse.c b/libs/gamecode/engine/pr_parse.c index 9e5036c16..76755f9fa 100644 --- a/libs/gamecode/engine/pr_parse.c +++ b/libs/gamecode/engine/pr_parse.c @@ -108,49 +108,40 @@ PR_UglyValueString (progs_t *pr, etype_t type, pr_type_t *val) return line; } -/* - ED_Write - - For savegames -*/ -void -ED_Write (progs_t *pr, QFile *f, edict_t *ed) +plitem_t * +ED_EntityDict (progs_t *pr, edict_t *ed) { - unsigned int i; + plitem_t *entity = PL_NewDictionary (); + unsigned i; int j; int type; const char *name; - ddef_t *d; + const char *value; pr_type_t *v; - Qprintf (f, "{\n"); + if (!ed->free) { + for (i = 0; i < pr->progs->numfielddefs; i++) { + ddef_t *d = &pr->pr_fielddefs[i]; - if (ed->free) { - Qprintf (f, "}\n"); - return; + name = PR_GetString (pr, d->s_name); + if (name[strlen (name) - 2] == '_') + continue; // skip _x, _y, _z vars + + v = &ed->v[d->ofs]; + + // if the value is still all 0, skip the field + type = d->type & ~DEF_SAVEGLOBAL; + for (j = 0; j < pr_type_size[type]; j++) + if (v[j].integer_var) + break; + if (j == pr_type_size[type]) + continue; + + value = PR_UglyValueString (pr, type, v); + PL_D_AddObject (entity, PL_NewString (name), PL_NewString (value)); + } } - - for (i = 1; i < pr->progs->numfielddefs; i++) { - d = &pr->pr_fielddefs[i]; - name = PR_GetString (pr, d->s_name); - if (name[strlen (name) - 2] == '_') - continue; // skip _x, _y, _z vars - - v = &ed->v[d->ofs]; - - // if the value is still all 0, skip the field - type = d->type & ~DEF_SAVEGLOBAL; - for (j = 0; j < pr_type_size[type]; j++) - if (v[j].integer_var) - break; - if (j == pr_type_size[type]) - continue; - - Qprintf (f, "\"%s\" ", name); - Qprintf (f, "\"%s\"\n", PR_UglyValueString (pr, d->type, v)); - } - - Qprintf (f, "}\n"); + return entity; } /* @@ -159,15 +150,16 @@ ED_Write (progs_t *pr, QFile *f, edict_t *ed) FIXME: need to tag constants, doesn't really work */ -void -ED_WriteGlobals (progs_t *pr, QFile *f) +plitem_t * +ED_GlobalsDict (progs_t *pr) { - ddef_t *def; - unsigned int i; + plitem_t *globals = PL_NewDictionary (); + unsigned i; const char *name; + const char *value; + ddef_t *def; int type; - Qprintf (f, "{\n"); for (i = 0; i < pr->progs->numglobaldefs; i++) { def = &pr->pr_globaldefs[i]; type = def->type; @@ -179,11 +171,10 @@ ED_WriteGlobals (progs_t *pr, QFile *f) continue; name = PR_GetString (pr, def->s_name); - Qprintf (f, "\"%s\" ", name); - Qprintf (f, "\"%s\"\n", - PR_UglyValueString (pr, type, &pr->pr_globals[def->ofs])); + value = PR_UglyValueString (pr, type, &pr->pr_globals[def->ofs]); + PL_D_AddObject (globals, PL_NewString (name), PL_NewString (value)); } - Qprintf (f, "}\n"); + return globals; } @@ -357,13 +348,13 @@ ED_InitGlobals (progs_t *pr, plitem_t *globals) while (count--) { global_name = PL_String (PL_ObjectAtIndex (keys, count)); value = PL_String (PL_ObjectForKey (globals, global_name)); - global = PR_FindField (pr, global_name); + global = PR_FindGlobal (pr, global_name); if (!global) { Sys_Printf ("'%s' is not a global\n", global_name); continue; } if (!ED_ParseEpair (pr, pr->pr_globals, global, value)) - PR_Error (pr, "ED_ParseGlobals: parse error"); + PR_Error (pr, "ED_InitGlobals: parse error"); } PL_Free (keys); } diff --git a/nq/source/host_cmd.c b/nq/source/host_cmd.c index 012aaf11b..250fd2df6 100644 --- a/nq/source/host_cmd.c +++ b/nq/source/host_cmd.c @@ -407,32 +407,139 @@ Host_Connect_f (void) #define SAVEGAME_VERSION 5 -/* - Host_SavegameComment - Writes a SAVEGAME_COMMENT_LENGTH character comment describing the current -*/ -static void -Host_SavegameComment (QFile *file) +static plitem_t * +spawn_parms_array (void) { - unsigned i; - dstring_t *comment = dstring_newstr (); + plitem_t *parms = PL_NewArray (); + int i; + const char *parm; - dsprintf (comment, "%-21s kills:%3i/%3i", cl.levelname, - cl.stats[STAT_MONSTERS], cl.stats[STAT_TOTALMONSTERS]); - if ((i = comment->size - 1) < SAVEGAME_COMMENT_LENGTH) { - comment->size = SAVEGAME_COMMENT_LENGTH + 1; - dstring_adjust (comment); - while (i < comment->size - 1) - comment->str[i++] = ' '; + for (i = 0; i < NUM_SPAWN_PARMS; i++) { + parm = va ("%f", svs.clients->spawn_parms[i]); + PL_A_AddObject (parms, PL_NewString (parm)); } - comment->str[SAVEGAME_COMMENT_LENGTH] = '\n'; - comment->str[SAVEGAME_COMMENT_LENGTH + 1] = '\0'; - // convert space to _ to make stdio happy - for (i = 0; i < SAVEGAME_COMMENT_LENGTH; i++) - if (comment->str[i] == ' ') - comment->str[i] = '_'; - Qwrite (file, comment->str, SAVEGAME_COMMENT_LENGTH + 1); + return parms; +} + +static plitem_t * +lightstyles_array (void) +{ + plitem_t *styles = PL_NewArray (); + const char *st; + int i; + + for (i = 0; i < MAX_LIGHTSTYLES; i++) { + st = "m"; + if (sv.lightstyles[i]) + st = sv.lightstyles[i]; + PL_A_AddObject (styles, PL_NewString (st)); + } + return styles; +} + +static plitem_t * +entities_array (void) +{ + plitem_t *entities = PL_NewArray (); + int i; + + for (i = 0; i < sv.num_edicts; i++) { + PL_A_AddObject (entities, + ED_EntityDict (&sv_pr_state, + EDICT_NUM (&sv_pr_state, i))); + } + return entities; +} + +static plitem_t * +game_dict (void) +{ + plitem_t *game = PL_NewDictionary (); + + PL_D_AddObject (game, + PL_NewString ("comment"), + PL_NewString (va ("%-21s kills:%3i/%3i", cl.levelname, + cl.stats[STAT_MONSTERS], + cl.stats[STAT_TOTALMONSTERS]))); + PL_D_AddObject (game, PL_NewString ("spawn_parms"), spawn_parms_array ()); + PL_D_AddObject (game, + PL_NewString ("current_skill"), + PL_NewString (va ("%d", current_skill))); + PL_D_AddObject (game, PL_NewString ("name"), PL_NewString (sv.name)); + PL_D_AddObject (game, + PL_NewString ("time"), + PL_NewString (va ("%f", sv.time))); + PL_D_AddObject (game, PL_NewString ("lightstyles"), lightstyles_array ()); + PL_D_AddObject (game, + PL_NewString ("globals"), + ED_GlobalsDict (&sv_pr_state)); + PL_D_AddObject (game, PL_NewString ("entities"), entities_array ()); + return game; +} + +static plitem_t * +convert_to_game_dict (script_t *script) +{ + plitem_t *game = PL_NewDictionary (); + plitem_t *item; + plitem_t *list; + int skill; + int i; + + // savegame comment (ignored) + Script_GetToken (script, 1); + PL_D_AddObject (game, + PL_NewString ("comment"), + PL_NewString (script->token->str)); + + // spawn_parms + item = PL_NewArray (); + for (i = 0; i < NUM_SPAWN_PARMS; i++) { + Script_GetToken (script, 1); + PL_A_AddObject (item, PL_NewString (script->token->str)); + } + PL_D_AddObject (game, PL_NewString ("spawn_parms"), item); + + + // this silliness is so we can load 1.06 save files, which have float skill + // values + Script_GetToken (script, 1); + skill = (int) (atof (script->token->str) + 0.1); + PL_D_AddObject (game, + PL_NewString ("current_skill"), + PL_NewString (va ("%d", skill))); + + Script_GetToken (script, 1); + PL_D_AddObject (game, + PL_NewString ("name"), + PL_NewString (script->token->str)); + + Script_GetToken (script, 1); + PL_D_AddObject (game, + PL_NewString ("time"), + PL_NewString (script->token->str)); + + // load the light styles + item = PL_NewArray (); + for (i = 0; i < MAX_LIGHTSTYLES; i++) { + Script_GetToken (script, 1); + PL_A_AddObject (item, PL_NewString (script->token->str)); + //char *s; + + //s = Hunk_Alloc (strlen (script->token->str) + 1); + //strcpy (s, script->token->str); + //sv.lightstyles[i] = s; + } + PL_D_AddObject (game, PL_NewString ("lightstyles"), item); + + // load the edicts out of the savegame file + list = ED_ConvertToPlist (&sv_pr_state, script); + item = PL_RemoveObjectAtIndex (list, 0); + PL_D_AddObject (game, PL_NewString ("globals"), item); + PL_D_AddObject (game, PL_NewString ("entities"), list); + + return game; } static void @@ -489,28 +596,8 @@ Host_Savegame_f (void) return; } - Qprintf (f, "%i\n", SAVEGAME_VERSION); - Host_SavegameComment (f); - for (i = 0; i < NUM_SPAWN_PARMS; i++) - Qprintf (f, "%f\n", svs.clients->spawn_parms[i]); - Qprintf (f, "%d\n", current_skill); - Qprintf (f, "%s\n", sv.name); - Qprintf (f, "%f\n", sv.time); + Qprintf (f, "%s\n%s", PROGRAM, PL_WritePropertyList (game_dict ())); - // write the light styles - for (i = 0; i < MAX_LIGHTSTYLES; i++) { - if (sv.lightstyles[i]) - Qprintf (f, "%s\n", sv.lightstyles[i]); - else - Qprintf (f, "m\n"); - } - - - ED_WriteGlobals (&sv_pr_state, f); - for (i = 0; i < sv.num_edicts; i++) { - ED_Write (&sv_pr_state, f, EDICT_NUM (&sv_pr_state, i)); - Qflush (f); - } Qclose (f); Con_Printf ("done.\n"); } @@ -522,10 +609,11 @@ Host_Loadgame_f (void) QFile *f; char *mapname = 0; script_t *script = 0; + plitem_t *game; plitem_t *list; + plitem_t *item; char *str = 0; - float time, tfloat; - unsigned int i; + int i; int entnum; int count; int version; @@ -567,33 +655,30 @@ Host_Loadgame_f (void) Script_Start (script, name->str, str); Script_GetToken (script, 1); - sscanf (script->token->str, "%i", &version); - if (version != SAVEGAME_VERSION) { - Con_Printf ("Savegame is version %i, not %i\n", version, - SAVEGAME_VERSION); - goto end; + if (strequal (script->token->str, PROGRAM)) { + if (!Script_TokenAvailable (script, 1)) { + Con_Printf ("Unexpected EOF reading %s\n", name->str); + goto end; + } + game = PL_GetPropertyList (script->p); + } else { + sscanf (script->token->str, "%i", &version); + if (version != SAVEGAME_VERSION) { + Con_Printf ("Savegame is version %i, not %i\n", version, + SAVEGAME_VERSION); + goto end; + } + game = convert_to_game_dict (script); } - // savegame comment (ignored) - Script_GetToken (script, 1); - + item = PL_ObjectForKey (game, "spawn_parms"); for (i = 0; i < NUM_SPAWN_PARMS; i++) { - Script_GetToken (script, 1); - sscanf (script->token->str, "%f", &spawn_parms[i]); + if (i >= PL_A_NumObjects (item)) + break; + spawn_parms[i] = atof (PL_String (PL_ObjectAtIndex (item, i))); } - - // this silliness is so we can load 1.06 save files, which have float skill - // values - Script_GetToken (script, 1); - sscanf (script->token->str, "%f", &tfloat); - current_skill = (int) (tfloat + 0.1); - Cvar_SetValue (skill, (float) current_skill); - - Script_GetToken (script, 1); - mapname = strdup (script->token->str); - - Script_GetToken (script, 1); - sscanf (script->token->str, "%f", &time); + current_skill = atoi (PL_String (PL_ObjectForKey (game, "current_skill"))); + mapname = strdup (PL_String (PL_ObjectForKey (game, "name"))); CL_Disconnect_f (); @@ -605,39 +690,26 @@ Host_Loadgame_f (void) sv.paused = true; // pause until all clients connect sv.loadgame = true; - // load the light styles - for (i = 0; i < MAX_LIGHTSTYLES; i++) { - char *s; + ED_InitGlobals (&sv_pr_state, PL_ObjectForKey (game, "globals")); - Script_GetToken (script, 1); - s = Hunk_Alloc (strlen (script->token->str) + 1); - strcpy (s, script->token->str); - sv.lightstyles[i] = s; - } + list = PL_ObjectForKey (game, "entities"); + entnum = 0; + count = PL_A_NumObjects (list); + for (entnum = 0; entnum < count; entnum++) { + plitem_t *entity = PL_ObjectAtIndex (list, entnum); + edict_t *ent = EDICT_NUM (&sv_pr_state, entnum); - // load the edicts out of the savegame file - list = ED_ConvertToPlist (&sv_pr_state, script); + memset (&ent->v, 0, sv_pr_state.progs->entityfields * 4); + ent->free = false; + ED_InitEntity (&sv_pr_state, entity, ent); - entnum = -1; // -1 is the globals - count = PL_A_NumObjects (list) - 1; - for (entnum = -1; entnum < count; entnum++) { - plitem_t *entity = PL_ObjectAtIndex (list, entnum + 1); - if (entnum == -1) { - ED_InitGlobals (&sv_pr_state, entity); - } else { - edict_t *ent = EDICT_NUM (&sv_pr_state, entnum); - memset (&ent->v, 0, sv_pr_state.progs->entityfields * 4); - ent->free = false; - ED_InitEntity (&sv_pr_state, entity, ent); - - // link it into the bsp tree - if (!ent->free) - SV_LinkEdict (ent, false); - } + // link it into the bsp tree + if (!ent->free) + SV_LinkEdict (ent, false); } sv.num_edicts = entnum; - sv.time = time; + sv.time = atof (PL_String (PL_ObjectForKey (game, "time"))); for (i = 0; i < NUM_SPAWN_PARMS; i++) svs.clients->spawn_parms[i] = spawn_parms[i];