use the script api for parsing ent data and savegames

This is an imperfect revision of history.
This commit is contained in:
Bill Currie 2004-11-12 02:39:00 +00:00 committed by Jeff Teunissen
parent 53f4d13f43
commit 0e324d1851
3 changed files with 150 additions and 121 deletions

View file

@ -1203,6 +1203,7 @@ struct progs_s {
int pr_edict_size; ///< in bytes int pr_edict_size; ///< in bytes
int pr_edictareasize; ///< for bounds checking, starts at 0 int pr_edictareasize; ///< for bounds checking, starts at 0
func_t edict_parse;
int pr_argc; int pr_argc;

View file

@ -45,12 +45,12 @@ static __attribute__ ((unused)) const char rcsid[] =
#include "QF/cvar.h" #include "QF/cvar.h"
#include "QF/dstring.h" #include "QF/dstring.h"
#include "QF/hash.h" #include "QF/hash.h"
#include "QF/idparse.h"
#include "QF/progs.h" #include "QF/progs.h"
#include "QF/qdefs.h" #include "QF/qdefs.h"
#include "QF/qfplist.h" #include "QF/qfplist.h"
#include "QF/qendian.h" #include "QF/qendian.h"
#include "QF/quakefs.h" #include "QF/quakefs.h"
#include "QF/script.h"
#include "QF/sys.h" #include "QF/sys.h"
#include "QF/zone.h" #include "QF/zone.h"
#include "QF/va.h" #include "QF/va.h"
@ -218,7 +218,7 @@ ED_NewString (progs_t *pr, const char *string)
Can parse either fields or globals Can parse either fields or globals
returns false if error returns false if error
*/ */
static qboolean qboolean
ED_ParseEpair (progs_t *pr, pr_type_t *base, ddef_t *key, const char *s) ED_ParseEpair (progs_t *pr, pr_type_t *base, ddef_t *key, const char *s)
{ {
int i; int i;
@ -288,8 +288,8 @@ ED_ParseEpair (progs_t *pr, pr_type_t *base, ddef_t *key, const char *s)
ent should be a properly initialized empty edict. ent should be a properly initialized empty edict.
Used for initial level load and for savegames. Used for initial level load and for savegames.
*/ */
const char * void
ED_ParseEdict (progs_t *pr, const char *data, edict_t *ent) ED_ParseEdict (progs_t *pr, script_t *script, edict_t *ent)
{ {
ddef_t *key; ddef_t *key;
qboolean anglehack; qboolean anglehack;
@ -302,17 +302,17 @@ ED_ParseEdict (progs_t *pr, const char *data, edict_t *ent)
if (ent != *(pr)->edicts) // hack if (ent != *(pr)->edicts) // hack
memset (&ent->v, 0, pr->progs->entityfields * 4); memset (&ent->v, 0, pr->progs->entityfields * 4);
while (1) { // go through all the dictionary pairs // go through all the dictionary pairs
// parse key while (1) {
data = COM_Parse (data); if (!Script_GetToken (script, 1))
if (com_token[0] == '}')
break;
if (!data)
PR_Error (pr, "ED_ParseEntity: EOF without closing brace"); PR_Error (pr, "ED_ParseEntity: EOF without closing brace");
// parse key
if (script->token->str[0] == '}')
break;
token = com_token; token = script->token->str;
// anglehack is to allow QuakeEd to write single scalar angles // anglehack is to allow QuakeEd to write single scalar angles
// and allow them to be turned into vectors. (FIXME...) // and allow them to be turned into vectors.
if (!strcmp (token, "angle")) { if (!strcmp (token, "angle")) {
token = "angles"; token = "angles";
anglehack = true; anglehack = true;
@ -332,11 +332,12 @@ ED_ParseEdict (progs_t *pr, const char *data, edict_t *ent)
} }
// parse value // parse value
data = COM_Parse (data); //FIXME shouldn't cross line
if (!data) if (!Script_GetToken (script, 1))
PR_Error (pr, "ED_ParseEntity: EOF without closing brace"); PR_Error (pr, "ED_ParseEntity: EOF without closing brace");
token = script->token->str;
if (com_token[0] == '}') if (token[0] == '}')
PR_Error (pr, "ED_ParseEntity: closing brace without data"); PR_Error (pr, "ED_ParseEntity: closing brace without data");
init = true; init = true;
@ -349,7 +350,7 @@ ED_ParseEdict (progs_t *pr, const char *data, edict_t *ent)
key = PR_FindField (pr, keyname->str); key = PR_FindField (pr, keyname->str);
if (!key) { if (!key) {
if (!pr->parse_field if (!pr->parse_field
|| !pr->parse_field (pr, keyname->str, com_token)) { || !pr->parse_field (pr, keyname->str, token)) {
Sys_Printf ("'%s' is not a field\n", keyname->str); Sys_Printf ("'%s' is not a field\n", keyname->str);
continue; continue;
} }
@ -370,7 +371,6 @@ ED_ParseEdict (progs_t *pr, const char *data, edict_t *ent)
ent->free = true; ent->free = true;
dstring_delete (keyname); dstring_delete (keyname);
return data;
} }
void void
@ -378,23 +378,25 @@ ED_ParseGlobals (progs_t *pr, script_t *script)
{ {
dstring_t *keyname = dstring_new (); dstring_t *keyname = dstring_new ();
ddef_t *key; ddef_t *key;
const char *token;
while (1) { while (1) {
// parse key // parse key
data = COM_Parse (data); if (!Script_GetToken (script, 1))
if (com_token[0] == '}')
break;
if (!data)
PR_Error (pr, "ED_ParseEntity: EOF without closing brace"); PR_Error (pr, "ED_ParseEntity: EOF without closing brace");
token = script->token->str;
if (token[0] == '}')
break;
dstring_copystr (keyname, com_token); dstring_copystr (keyname, script->token->str);
// parse value // parse value
data = COM_Parse (data); //FIXME shouldn't cross line
if (!data) if (!Script_GetToken (script, 1))
PR_Error (pr, "ED_ParseEntity: EOF without closing brace"); PR_Error (pr, "ED_ParseEntity: EOF without closing brace");
token = script->token->str;
if (com_token[0] == '}') if (token[0] == '}')
PR_Error (pr, "ED_ParseEntity: closing brace without data"); PR_Error (pr, "ED_ParseEntity: closing brace without data");
key = PR_FindGlobal (pr, keyname->str); key = PR_FindGlobal (pr, keyname->str);
@ -403,7 +405,7 @@ ED_ParseGlobals (progs_t *pr, script_t *script)
continue; continue;
} }
if (!ED_ParseEpair (pr, pr->pr_globals, key, com_token)) if (!ED_ParseEpair (pr, pr->pr_globals, key, token))
PR_Error (pr, "ED_ParseGlobals: parse error"); PR_Error (pr, "ED_ParseGlobals: parse error");
} }
dstring_delete (keyname); dstring_delete (keyname);
@ -423,7 +425,7 @@ ED_ParseGlobals (progs_t *pr, script_t *script)
to call ED_CallSpawnFunctions () to let the objects initialize themselves. to call ED_CallSpawnFunctions () to let the objects initialize themselves.
*/ */
static void static void
ED_ParseOld (progs_t *pr, const char *data) ED_ParseOld (progs_t *pr, script_t *script)
{ {
edict_t *ent = NULL; edict_t *ent = NULL;
int inhibit = 0; int inhibit = 0;
@ -431,19 +433,17 @@ ED_ParseOld (progs_t *pr, const char *data)
pr_type_t *classname; pr_type_t *classname;
ddef_t *def; ddef_t *def;
while (1) { // parse ents while (Script_GetToken (script, 1)) { // parse ents
// parse the opening brace // parse the opening brace
data = COM_Parse (data); if (script->token->str[0] != '{')
if (!data) PR_Error (pr, "ED_LoadFromFile: found %s when expecting {",
break; script->token->str);
if (com_token[0] != '{')
PR_Error (pr, "ED_LoadFromFile: found %s when expecting {", com_token);
if (!ent) if (!ent)
ent = EDICT_NUM (pr, 0); ent = EDICT_NUM (pr, 0);
else else
ent = ED_Alloc (pr); ent = ED_Alloc (pr);
data = ED_ParseEdict (pr, data, ent); ED_ParseEdict (pr, script, ent);
// remove things from different skill levels or deathmatch // remove things from different skill levels or deathmatch
if (pr->prune_edict && pr->prune_edict (pr, ent)) { if (pr->prune_edict && pr->prune_edict (pr, ent)) {
@ -483,12 +483,35 @@ ED_ParseOld (progs_t *pr, const char *data)
void void
ED_LoadFromFile (progs_t *pr, const char *data) ED_LoadFromFile (progs_t *pr, const char *data)
{ {
if (*data == '(') { script_t *script;
if (pr->edict_parse) {
PR_PushFrame (pr);
P_INT (pr, 0) = PR_SetTempString (pr, data);
PR_ExecuteProgram (pr, pr->edict_parse);
PR_PopFrame (pr);
return;
}
script = Script_New ();
Script_Start (script, "ent data", data);
if (Script_GetToken (script, 1)) {
if (*script->token->str == '(') {
// new style (plist) entity data // new style (plist) entity data
plitem_t *plist = PL_GetPropertyList (data); plitem_t *plist = PL_GetPropertyList (data);
plist = plist; plist = plist;
} else { } else {
// oldstyle entity data // oldstyle entity data
ED_ParseOld (pr, data); Script_UngetToken (script);
ED_ParseOld (pr, script);
} }
}
Script_Delete (script);
}
void
ED_EntityParseFunction (progs_t *pr)
{
pr->edict_parse = P_FUNCTION (pr, 0);
} }

View file

@ -39,16 +39,18 @@ static __attribute__ ((unused)) const char rcsid[] =
#endif #endif
#include "QF/cbuf.h" #include "QF/cbuf.h"
#include "QF/idparse.h"
#include "QF/cmd.h" #include "QF/cmd.h"
#include "QF/cvar.h"
#include "QF/va.h"
#include "QF/screen.h"
#include "QF/msg.h"
#include "QF/model.h"
#include "QF/console.h" #include "QF/console.h"
#include "QF/cvar.h"
#include "QF/dstring.h"
#include "QF/idparse.h"
#include "QF/keys.h" #include "QF/keys.h"
#include "QF/model.h"
#include "QF/msg.h"
#include "QF/screen.h"
#include "QF/script.h"
#include "QF/sys.h" #include "QF/sys.h"
#include "QF/va.h"
#include "client.h" #include "client.h"
#include "compat.h" #include "compat.h"
@ -410,22 +412,26 @@ Host_Connect_f (void)
Writes a SAVEGAME_COMMENT_LENGTH character comment describing the current Writes a SAVEGAME_COMMENT_LENGTH character comment describing the current
*/ */
static void static void
Host_SavegameComment (char *text) Host_SavegameComment (QFile *file)
{ {
int i; unsigned i;
char kills[20]; dstring_t *comment = dstring_newstr ();
for (i = 0; i < SAVEGAME_COMMENT_LENGTH; i++) dsprintf (comment, "%-21s kills:%3i/%3i", cl.levelname,
text[i] = ' '; cl.stats[STAT_MONSTERS], cl.stats[STAT_TOTALMONSTERS]);
memcpy (text, cl.levelname, strlen (cl.levelname)); if ((i = comment->size - 1) < SAVEGAME_COMMENT_LENGTH) {
snprintf (kills, sizeof (kills), "kills:%3i/%3i", cl.stats[STAT_MONSTERS], comment->size = SAVEGAME_COMMENT_LENGTH + 1;
cl.stats[STAT_TOTALMONSTERS]); dstring_adjust (comment);
memcpy (text + 22, kills, strlen (kills)); while (i < comment->size - 1)
comment->str[i++] = ' ';
}
comment->str[SAVEGAME_COMMENT_LENGTH] = '\n';
comment->str[SAVEGAME_COMMENT_LENGTH + 1] = '\0';
// convert space to _ to make stdio happy // convert space to _ to make stdio happy
for (i = 0; i < SAVEGAME_COMMENT_LENGTH; i++) for (i = 0; i < SAVEGAME_COMMENT_LENGTH; i++)
if (text[i] == ' ') if (comment->str[i] == ' ')
text[i] = '_'; comment->str[i] = '_';
text[SAVEGAME_COMMENT_LENGTH] = '\0'; Qwrite (file, comment->str, SAVEGAME_COMMENT_LENGTH + 1);
} }
static void static void
@ -434,7 +440,6 @@ Host_Savegame_f (void)
char name[256]; char name[256];
QFile *f; QFile *f;
int i; int i;
char comment[SAVEGAME_COMMENT_LENGTH + 1];
if (cmd_source != src_command) if (cmd_source != src_command)
return; return;
@ -484,8 +489,7 @@ Host_Savegame_f (void)
} }
Qprintf (f, "%i\n", SAVEGAME_VERSION); Qprintf (f, "%i\n", SAVEGAME_VERSION);
Host_SavegameComment (comment); Host_SavegameComment (f);
Qprintf (f, "%s\n", comment);
for (i = 0; i < NUM_SPAWN_PARMS; i++) for (i = 0; i < NUM_SPAWN_PARMS; i++)
Qprintf (f, "%f\n", svs.clients->spawn_parms[i]); Qprintf (f, "%f\n", svs.clients->spawn_parms[i]);
Qprintf (f, "%d\n", current_skill); Qprintf (f, "%d\n", current_skill);
@ -513,77 +517,88 @@ Host_Savegame_f (void)
static void static void
Host_Loadgame_f (void) Host_Loadgame_f (void)
{ {
char name[MAX_OSPATH]; dstring_t *name = 0;
QFile *f; QFile *f;
char mapname[MAX_QPATH]; char *mapname = 0;
script_t *script = 0;
char *str = 0;
float time, tfloat; float time, tfloat;
char str[32768];
const char *start;
unsigned int i; unsigned int i;
int r;
edict_t *ent; edict_t *ent;
int entnum; int entnum;
int version; int version;
float spawn_parms[NUM_SPAWN_PARMS]; float spawn_parms[NUM_SPAWN_PARMS];
char buf[100];
if (cmd_source != src_command) if (cmd_source != src_command)
return; goto end;
if (Cmd_Argc () != 2) { if (Cmd_Argc () != 2) {
Con_Printf ("load <savename> : load a game\n"); Con_Printf ("load <savename> : load a game\n");
return; goto end;
} }
cls.demonum = -1; // stop demo loop in case this fails cls.demonum = -1; // stop demo loop in case this fails
snprintf (name, sizeof (name), "%s/%s", name = dstring_newstr ();
qfs_gamedir->dir.def, Cmd_Argv (1)); dsprintf (name, "%s/%s", qfs_gamedir->dir.def, Cmd_Argv (1));
QFS_DefaultExtension (name, ".sav"); name->size += 4;
dstring_adjust (name);
QFS_DefaultExtension (name->str, ".sav");
// we can't call SCR_BeginLoadingPlaque, because too much stack space has // we can't call SCR_BeginLoadingPlaque, because too much stack space has
// been used. The menu calls it before stuffing loadgame command // been used. The menu calls it before stuffing loadgame command
// SCR_BeginLoadingPlaque (); // SCR_BeginLoadingPlaque ();
Con_Printf ("Loading game from %s...\n", name); Con_Printf ("Loading game from %s...\n", name->str);
f = QFS_Open (name, "rz"); f = QFS_Open (name->str, "rz");
if (!f) { if (!f) {
Con_Printf ("ERROR: couldn't open.\n"); Con_Printf ("ERROR: couldn't open.\n");
return; goto end;
} }
str = malloc (Qfilesize (f) + 1);
Qgets (f, buf, sizeof (buf)); i = Qread (f, str, Qfilesize (f));
sscanf (buf, "%i\n", &version); str[i] = 0;
if (version != SAVEGAME_VERSION) {
Qclose (f); Qclose (f);
script = Script_New ();
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, Con_Printf ("Savegame is version %i, not %i\n", version,
SAVEGAME_VERSION); SAVEGAME_VERSION);
return; goto end;
} }
Qgets (f, buf, sizeof (buf));
sscanf (buf, "%s\n", str); // savegame comment (ignored)
Script_GetToken (script, 1);
for (i = 0; i < NUM_SPAWN_PARMS; i++) { for (i = 0; i < NUM_SPAWN_PARMS; i++) {
Qgets (f, buf, sizeof (buf)); Script_GetToken (script, 1);
sscanf (buf, "%f\n", &spawn_parms[i]); sscanf (script->token->str, "%f", &spawn_parms[i]);
} }
// this silliness is so we can load 1.06 save files, which have float skill // this silliness is so we can load 1.06 save files, which have float skill
// values // values
Qgets (f, buf, sizeof (buf)); Script_GetToken (script, 1);
sscanf (buf, "%f\n", &tfloat); sscanf (script->token->str, "%f", &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);
Qgets (f, buf, sizeof (buf)); Script_GetToken (script, 1);
sscanf (buf, "%s\n", mapname); mapname = strdup (script->token->str);
Qgets (f, buf, sizeof (buf));
sscanf (buf, "%f\n", &time); Script_GetToken (script, 1);
sscanf (script->token->str, "%f", &time);
CL_Disconnect_f (); CL_Disconnect_f ();
SV_SpawnServer (mapname); SV_SpawnServer (mapname);
if (!sv.active) { if (!sv.active) {
Con_Printf ("Couldn't load map\n"); Con_Printf ("Couldn't load map %s\n", mapname);
return; goto end;
} }
sv.paused = true; // pause until all clients connect sv.paused = true; // pause until all clients connect
sv.loadgame = true; sv.loadgame = true;
@ -591,44 +606,27 @@ Host_Loadgame_f (void)
// load the light styles // load the light styles
for (i = 0; i < MAX_LIGHTSTYLES; i++) { for (i = 0; i < MAX_LIGHTSTYLES; i++) {
char *s; char *s;
Qgets (f, buf, sizeof (buf));
sscanf (buf, "%s\n", str); Script_GetToken (script, 1);
s = Hunk_Alloc (strlen (str) + 1); s = Hunk_Alloc (strlen (script->token->str) + 1);
strcpy (s, str); strcpy (s, script->token->str);
sv.lightstyles[i] = s; sv.lightstyles[i] = s;
} }
// 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 (!Qeof (f)) { while (Script_GetToken (script, 1)) {
for (i = 0; i < sizeof (str) - 1; i++) { if (strcmp (script->token->str, "{"))
r = Qgetc (f);
if (r == EOF || !r)
break;
str[i] = r;
if (r == '}') {
i++;
break;
}
}
if (i == sizeof (str) - 1)
Sys_Error ("Loadgame buffer overflow");
str[i] = 0;
start = str;
start = COM_Parse (str);
if (!com_token[0])
break; // end of file
if (strcmp (com_token, "{"))
Sys_Error ("First token isn't a brace"); Sys_Error ("First token isn't a brace");
if (entnum == -1) { // parse the global vars if (entnum == -1) { // parse the global vars
ED_ParseGlobals (&sv_pr_state, start); ED_ParseGlobals (&sv_pr_state, script);
} else { // parse an edict } else { // parse an edict
ent = EDICT_NUM (&sv_pr_state, entnum); ent = EDICT_NUM (&sv_pr_state, entnum);
memset (&ent->v, 0, sv_pr_state.progs->entityfields * 4); memset (&ent->v, 0, sv_pr_state.progs->entityfields * 4);
ent->free = false; ent->free = false;
ED_ParseEdict (&sv_pr_state, start, ent); ED_ParseEdict (&sv_pr_state, script, ent);
// link it into the bsp tree // link it into the bsp tree
if (!ent->free) if (!ent->free)
@ -641,8 +639,6 @@ Host_Loadgame_f (void)
sv.num_edicts = entnum; sv.num_edicts = entnum;
sv.time = time; sv.time = time;
Qclose (f);
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];
@ -650,6 +646,15 @@ Host_Loadgame_f (void)
CL_EstablishConnection ("local"); CL_EstablishConnection ("local");
Host_Reconnect_f (); Host_Reconnect_f ();
} }
end:
if (mapname)
free (mapname);
if (script)
Script_Delete (script);
if (str)
free (str);
if (name)
dstring_delete (name);
} }
//============================================================================ //============================================================================