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);
}
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);
// 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
* to perform non-sequential reads on files reopened on pak files
* because we need the bookkeeping about file start/end positions.

View file

@ -1105,13 +1105,13 @@ Host_Loadgame_f
*/
void Host_Loadgame_f (void)
{
static char *start;
char name[MAX_OSPATH];
FILE *f;
char mapname[MAX_QPATH];
float time, tfloat;
char str[32768];
const char *start;
int i, r;
const char *data;
int i;
edict_t *ent;
int entnum;
int version;
@ -1142,30 +1142,38 @@ void Host_Loadgame_f (void)
// SCR_BeginLoadingPlaque ();
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");
return;
}
fscanf (f, "%i\n", &version);
data = start;
data = COM_ParseIntNewline (data, &version);
if (version != SAVEGAME_VERSION)
{
fclose (f);
free (start);
start = NULL;
Con_Printf ("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION);
return;
}
fscanf (f, "%s\n", str);
data = COM_ParseStringNewline (data);
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
fscanf (f, "%f\n", &tfloat);
data = COM_ParseFloatNewline(data, &tfloat);
current_skill = (int)(tfloat + 0.1);
Cvar_SetValue ("skill", (float)current_skill);
fscanf (f, "%s\n",mapname);
fscanf (f, "%f\n",&time);
data = COM_ParseStringNewline (data);
q_strlcpy (mapname, com_token, sizeof(mapname));
data = COM_ParseFloatNewline (data, &time);
CL_Disconnect_f ();
@ -1173,7 +1181,8 @@ void Host_Loadgame_f (void)
if (!sv.active)
{
fclose (f);
free (start);
start = NULL;
Con_Printf ("Couldn't load map\n");
return;
}
@ -1184,50 +1193,25 @@ void Host_Loadgame_f (void)
for (i = 0; i < MAX_LIGHTSTYLES; i++)
{
fscanf (f, "%s\n", str);
sv.lightstyles[i] = (const char *)Hunk_Strdup (str, "lightstyles");
data = COM_ParseStringNewline (data);
sv.lightstyles[i] = (const char *)Hunk_Strdup (com_token, "lightstyles");
}
// load the edicts out of the savegame file
entnum = -1; // -1 is the globals
while (!feof(f))
while (*data)
{
qboolean inside_string = false;
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);
data = COM_Parse (data);
if (!com_token[0])
break; // end of file
if (strcmp(com_token,"{"))
{
fclose (f);
Sys_Error ("First token isn't a brace");
}
if (entnum == -1)
{ // parse the global vars
ED_ParseGlobals (start);
data = ED_ParseGlobals (data);
}
else
{ // parse an edict
@ -1239,7 +1223,7 @@ void Host_Loadgame_f (void)
else {
memset (ent, 0, pr_edict_size);
}
ED_ParseEdict (start, ent);
data = ED_ParseEdict (data, ent);
// link it into the bsp tree
if (!ent->free)
@ -1252,7 +1236,8 @@ void Host_Loadgame_f (void)
sv.num_edicts = entnum;
sv.time = time;
fclose (f);
free (start);
start = NULL;
for (i = 0; i < NUM_SPAWN_PARMS; i++)
svs.clients->spawn_parms[i] = spawn_parms[i];

View file

@ -691,7 +691,7 @@ void ED_WriteGlobals (FILE *f)
ED_ParseGlobals
=============
*/
void ED_ParseGlobals (const char *data)
const char *ED_ParseGlobals (const char *data)
{
char keyname[64];
ddef_t *key;
@ -725,6 +725,7 @@ void ED_ParseGlobals (const char *data)
if (!ED_ParseEpair ((void *)pr_globals, key, com_token))
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);
void ED_WriteGlobals (FILE *f);
void ED_ParseGlobals (const char *data);
const char *ED_ParseGlobals (const char *data);
void ED_LoadFromFile (const char *data);