diff --git a/quakespasm/Quake/common.c b/quakespasm/Quake/common.c index 9fc07658..3899154d 100644 --- a/quakespasm/Quake/common.c +++ b/quakespasm/Quake/common.c @@ -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; +} /* ================= diff --git a/quakespasm/Quake/common.h b/quakespasm/Quake/common.h index 5fc97b6b..1edf88bc 100644 --- a/quakespasm/Quake/common.h +++ b/quakespasm/Quake/common.h @@ -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. diff --git a/quakespasm/Quake/host_cmd.c b/quakespasm/Quake/host_cmd.c index 626dc8d7..cfb428da 100644 --- a/quakespasm/Quake/host_cmd.c +++ b/quakespasm/Quake/host_cmd.c @@ -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]; diff --git a/quakespasm/Quake/pr_edict.c b/quakespasm/Quake/pr_edict.c index 9c6fc94f..5c8fc2f0 100644 --- a/quakespasm/Quake/pr_edict.c +++ b/quakespasm/Quake/pr_edict.c @@ -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; } //============================================================================ diff --git a/quakespasm/Quake/progs.h b/quakespasm/Quake/progs.h index 429f9d3d..dfcb6639 100644 --- a/quakespasm/Quake/progs.h +++ b/quakespasm/Quake/progs.h @@ -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);