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.

This commit is contained in:
Bill Currie 2006-12-09 02:35:44 +00:00 committed by Jeff Teunissen
parent 9cbac0bbc1
commit c290e9a988
3 changed files with 206 additions and 143 deletions

View file

@ -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);

View file

@ -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);
}

View file

@ -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];