Host_Loadgame_f: rewritten version loads the whole .sav into memory

+ avoids "Loadgame buffer overflow" when loading saves from QS-Spike/DarkPlaces containing large comments
+ removes ugly } hack
+ should be a bit faster as we avoid calling fgetc for every byte

git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@1398 af15c1b1-3010-417e-b628-4374ebc0bcbd
This commit is contained in:
Eric Wasylishen 2017-04-16 02:53:06 +00:00
parent 83a0b5ae14
commit 8011b3f9fd
5 changed files with 101 additions and 48 deletions

View file

@ -1872,6 +1872,56 @@ byte *COM_LoadMallocFile (const char *path, unsigned int *path_id)
return COM_LoadFile (path, LOADFILE_MALLOC, path_id); return COM_LoadFile (path, LOADFILE_MALLOC, path_id);
} }
byte *COM_LoadMallocFile_OSPath (const char *path, long *len_out)
{
FILE *f;
byte *data;
long len;
f = fopen (path, "rb");
if (f == NULL)
return NULL;
len = COM_filelength (f);
if (len < 0)
return NULL;
data = (byte *) malloc (len + 1);
if (data == NULL)
return NULL;
if (fread (data, 1, len, f) != len)
{
free (data);
return NULL;
}
data[len] = '\0';
if (len_out != NULL)
*len_out = len;
return data;
}
const char *COM_ParseIntNewline(const char *buffer, int *value)
{
int consumed = 0;
sscanf (buffer, "%i\n%n", value, &consumed);
return buffer + consumed;
}
const char *COM_ParseFloatNewline(const char *buffer, float *value)
{
int consumed = 0;
sscanf (buffer, "%f\n%n", value, &consumed);
return buffer + consumed;
}
const char *COM_ParseStringNewline(const char *buffer)
{
int consumed = 0;
com_token[0] = '\0';
sscanf (buffer, "%1023s\n%n", com_token, &consumed);
return buffer + consumed;
}
/* /*
================= =================

View file

@ -255,6 +255,23 @@ void COM_LoadCacheFile (const char *path, struct cache_user_s *cu,
byte *COM_LoadMallocFile (const char *path, unsigned int *path_id); byte *COM_LoadMallocFile (const char *path, unsigned int *path_id);
// allocates the buffer on the system mem (malloc). // allocates the buffer on the system mem (malloc).
// Opens the given path directly, ignoring search paths.
// Returns NULL on failure, or else a '\0'-terminated malloc'ed buffer.
byte *COM_LoadMallocFile_OSPath (const char *path, long *len_out);
// Attempts to parse an int, followed by a newline.
// Returns advanced buffer position.
// Doesn't signal parsing failure, but this is not needed for savegame loading.
const char *COM_ParseIntNewline(const char *buffer, int *value);
// Attempts to parse a float followed by a newline.
// Returns advanced buffer position.
const char *COM_ParseFloatNewline(const char *buffer, float *value);
// Parse a string of non-whitespace into com_token, then tries to consume a
// newline. Returns advanced buffer position.
const char *COM_ParseStringNewline(const char *buffer);
/* The following FS_*() stdio replacements are necessary if one is /* The following FS_*() stdio replacements are necessary if one is
* to perform non-sequential reads on files reopened on pak files * to perform non-sequential reads on files reopened on pak files
* because we need the bookkeeping about file start/end positions. * because we need the bookkeeping about file start/end positions.

View file

@ -1105,13 +1105,13 @@ Host_Loadgame_f
*/ */
void Host_Loadgame_f (void) void Host_Loadgame_f (void)
{ {
static char *start;
char name[MAX_OSPATH]; char name[MAX_OSPATH];
FILE *f;
char mapname[MAX_QPATH]; char mapname[MAX_QPATH];
float time, tfloat; float time, tfloat;
char str[32768]; const char *data;
const char *start; int i;
int i, r;
edict_t *ent; edict_t *ent;
int entnum; int entnum;
int version; int version;
@ -1142,30 +1142,38 @@ void Host_Loadgame_f (void)
// SCR_BeginLoadingPlaque (); // SCR_BeginLoadingPlaque ();
Con_Printf ("Loading game from %s...\n", name); Con_Printf ("Loading game from %s...\n", name);
f = fopen (name, "r");
if (!f) // avoid leaking if the previous Host_Loadgame_f failed with a Host_Error
if (start != NULL)
free (start);
start = (char *) COM_LoadMallocFile_OSPath(name, NULL);
if (start == NULL)
{ {
Con_Printf ("ERROR: couldn't open.\n"); Con_Printf ("ERROR: couldn't open.\n");
return; return;
} }
fscanf (f, "%i\n", &version); data = start;
data = COM_ParseIntNewline (data, &version);
if (version != SAVEGAME_VERSION) if (version != SAVEGAME_VERSION)
{ {
fclose (f); free (start);
start = NULL;
Con_Printf ("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION); Con_Printf ("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION);
return; return;
} }
fscanf (f, "%s\n", str); data = COM_ParseStringNewline (data);
for (i = 0; i < NUM_SPAWN_PARMS; i++) for (i = 0; i < NUM_SPAWN_PARMS; i++)
fscanf (f, "%f\n", &spawn_parms[i]); data = COM_ParseFloatNewline (data, &spawn_parms[i]);
// this silliness is so we can load 1.06 save files, which have float skill values // this silliness is so we can load 1.06 save files, which have float skill values
fscanf (f, "%f\n", &tfloat); data = COM_ParseFloatNewline(data, &tfloat);
current_skill = (int)(tfloat + 0.1); current_skill = (int)(tfloat + 0.1);
Cvar_SetValue ("skill", (float)current_skill); Cvar_SetValue ("skill", (float)current_skill);
fscanf (f, "%s\n",mapname); data = COM_ParseStringNewline (data);
fscanf (f, "%f\n",&time); q_strlcpy (mapname, com_token, sizeof(mapname));
data = COM_ParseFloatNewline (data, &time);
CL_Disconnect_f (); CL_Disconnect_f ();
@ -1173,7 +1181,8 @@ void Host_Loadgame_f (void)
if (!sv.active) if (!sv.active)
{ {
fclose (f); free (start);
start = NULL;
Con_Printf ("Couldn't load map\n"); Con_Printf ("Couldn't load map\n");
return; return;
} }
@ -1184,50 +1193,25 @@ void Host_Loadgame_f (void)
for (i = 0; i < MAX_LIGHTSTYLES; i++) for (i = 0; i < MAX_LIGHTSTYLES; i++)
{ {
fscanf (f, "%s\n", str); data = COM_ParseStringNewline (data);
sv.lightstyles[i] = (const char *)Hunk_Strdup (str, "lightstyles"); sv.lightstyles[i] = (const char *)Hunk_Strdup (com_token, "lightstyles");
} }
// load the edicts out of the savegame file // load the edicts out of the savegame file
entnum = -1; // -1 is the globals entnum = -1; // -1 is the globals
while (!feof(f)) while (*data)
{ {
qboolean inside_string = false; data = COM_Parse (data);
for (i = 0; i < (int) sizeof(str) - 1; i++)
{
r = fgetc (f);
if (r == EOF || !r)
break;
str[i] = r;
if (r == '"')
{
inside_string = !inside_string;
}
else if (r == '}' && !inside_string) // only handle } characters outside of quoted strings
{
i++;
break;
}
}
if (i == (int) sizeof(str) - 1)
{
fclose (f);
Sys_Error ("Loadgame buffer overflow");
}
str[i] = 0;
start = str;
start = COM_Parse(str);
if (!com_token[0]) if (!com_token[0])
break; // end of file break; // end of file
if (strcmp(com_token,"{")) if (strcmp(com_token,"{"))
{ {
fclose (f);
Sys_Error ("First token isn't a brace"); Sys_Error ("First token isn't a brace");
} }
if (entnum == -1) if (entnum == -1)
{ // parse the global vars { // parse the global vars
ED_ParseGlobals (start); data = ED_ParseGlobals (data);
} }
else else
{ // parse an edict { // parse an edict
@ -1239,7 +1223,7 @@ void Host_Loadgame_f (void)
else { else {
memset (ent, 0, pr_edict_size); memset (ent, 0, pr_edict_size);
} }
ED_ParseEdict (start, ent); data = ED_ParseEdict (data, ent);
// link it into the bsp tree // link it into the bsp tree
if (!ent->free) if (!ent->free)
@ -1252,7 +1236,8 @@ void Host_Loadgame_f (void)
sv.num_edicts = entnum; sv.num_edicts = entnum;
sv.time = time; sv.time = time;
fclose (f); free (start);
start = NULL;
for (i = 0; i < NUM_SPAWN_PARMS; i++) for (i = 0; i < NUM_SPAWN_PARMS; i++)
svs.clients->spawn_parms[i] = spawn_parms[i]; svs.clients->spawn_parms[i] = spawn_parms[i];

View file

@ -691,7 +691,7 @@ void ED_WriteGlobals (FILE *f)
ED_ParseGlobals ED_ParseGlobals
============= =============
*/ */
void ED_ParseGlobals (const char *data) const char *ED_ParseGlobals (const char *data)
{ {
char keyname[64]; char keyname[64];
ddef_t *key; ddef_t *key;
@ -725,6 +725,7 @@ void ED_ParseGlobals (const char *data)
if (!ED_ParseEpair ((void *)pr_globals, key, com_token)) if (!ED_ParseEpair ((void *)pr_globals, key, com_token))
Host_Error ("ED_ParseGlobals: parse error"); Host_Error ("ED_ParseGlobals: parse error");
} }
return data;
} }
//============================================================================ //============================================================================

View file

@ -87,7 +87,7 @@ void ED_Write (FILE *f, edict_t *ed);
const char *ED_ParseEdict (const char *data, edict_t *ent); const char *ED_ParseEdict (const char *data, edict_t *ent);
void ED_WriteGlobals (FILE *f); void ED_WriteGlobals (FILE *f);
void ED_ParseGlobals (const char *data); const char *ED_ParseGlobals (const char *data);
void ED_LoadFromFile (const char *data); void ED_LoadFromFile (const char *data);