diff --git a/libs/gamecode/engine/Makefile.am b/libs/gamecode/engine/Makefile.am index 4608ab4f3..ce2b634de 100644 --- a/libs/gamecode/engine/Makefile.am +++ b/libs/gamecode/engine/Makefile.am @@ -7,5 +7,5 @@ lib_LTLIBRARIES= libQFgamecode.la libQFgamecode_la_LDFLAGS= -version-info 1:0:0 libQFgamecode_la_SOURCES= \ - pr_builtins.c pr_edict.c pr_debug.c pr_exec.c pr_load.c pr_obj.c \ - pr_opcode.c pr_resolve.c pr_resource.c pr_strings.c pr_zone.c + pr_builtins.c pr_edict.c pr_debug.c pr_exec.c pr_load.c pr_parse.c \ + pr_obj.c pr_opcode.c pr_resolve.c pr_resource.c pr_strings.c pr_zone.c diff --git a/libs/gamecode/engine/pr_edict.c b/libs/gamecode/engine/pr_edict.c index 58a1ef0a9..2de2d6ccd 100644 --- a/libs/gamecode/engine/pr_edict.c +++ b/libs/gamecode/engine/pr_edict.c @@ -100,7 +100,6 @@ const char *pr_type_name[ev_type_count] = { }; ddef_t *ED_FieldAtOfs (progs_t *pr, int ofs); -qboolean ED_ParseEpair (progs_t *pr, pr_type_t *base, ddef_t *key, const char *s); /* ED_ClearEdict @@ -288,57 +287,6 @@ PR_ValueString (progs_t *pr, etype_t type, pr_type_t *val) return line; } -/* - PR_UglyValueString - - Returns a string describing *data in a type specific manner - Easier to parse than PR_ValueString -*/ -char * -PR_UglyValueString (progs_t *pr, etype_t type, pr_type_t *val) -{ - static char line[256]; - ddef_t *def; - dfunction_t *f; - - type &= ~DEF_SAVEGLOBAL; - - switch (type) { - case ev_string: - snprintf (line, sizeof (line), "%s", - PR_GetString (pr, val->string_var)); - break; - case ev_entity: - snprintf (line, sizeof (line), "%i", - NUM_FOR_BAD_EDICT (pr, PROG_TO_EDICT (pr, val->entity_var))); - break; - case ev_func: - f = pr->pr_functions + val->func_var; - snprintf (line, sizeof (line), "%s", PR_GetString (pr, f->s_name)); - break; - case ev_field: - def = ED_FieldAtOfs (pr, val->integer_var); - snprintf (line, sizeof (line), "%s", - PR_GetString (pr, def->s_name)); - break; - case ev_void: - strcpy (line, "void"); - break; - case ev_float: - snprintf (line, sizeof (line), "%f", val->float_var); - break; - case ev_vector: - snprintf (line, sizeof (line), "%f %f %f", val->vector_var[0], - val->vector_var[1], val->vector_var[2]); - break; - default: - snprintf (line, sizeof (line), "bad type %i", type); - break; - } - - return line; -} - /* PR_GlobalString @@ -490,49 +438,6 @@ ED_Print (progs_t *pr, edict_t *ed) } } -/* - ED_Write - - For savegames -*/ -void -ED_Write (progs_t *pr, QFile *f, edict_t *ed) -{ - int i, j; - int type; - char *name; - ddef_t *d; - pr_type_t *v; - - Qprintf (f, "{\n"); - - if (ed->free) { - Qprintf (f, "}\n"); - return; - } - - 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"); -} void ED_PrintNum (progs_t *pr, int ent) @@ -607,344 +512,6 @@ ED_Count (progs_t *pr) Sys_Printf ("zombie :%3i\n", zombie); } -/* - ARCHIVING GLOBALS - - FIXME: need to tag constants, doesn't really work -*/ - -/* - ED_WriteGlobals -*/ -void -ED_WriteGlobals (progs_t *pr, QFile *f) -{ - ddef_t *def; - int i; - char *name; - int type; - - Qprintf (f, "{\n"); - for (i = 0; i < pr->progs->numglobaldefs; i++) { - def = &pr->pr_globaldefs[i]; - type = def->type; - if (!(def->type & DEF_SAVEGLOBAL)) - continue; - type &= ~DEF_SAVEGLOBAL; - - if (type != ev_string && type != ev_float && type != ev_entity) - 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])); - } - Qprintf (f, "}\n"); -} - -/* - ED_ParseGlobals -*/ -void -ED_ParseGlobals (progs_t *pr, const char *data) -{ - char keyname[64]; - ddef_t *key; - - while (1) { - // parse key - data = COM_Parse (data); - if (com_token[0] == '}') - break; - if (!data) - PR_Error (pr, "ED_ParseEntity: EOF without closing brace"); - - strcpy (keyname, com_token); - - // parse value - data = COM_Parse (data); - if (!data) - PR_Error (pr, "ED_ParseEntity: EOF without closing brace"); - - if (com_token[0] == '}') - PR_Error (pr, "ED_ParseEntity: closing brace without data"); - - key = PR_FindGlobal (pr, keyname); - if (!key) { - Sys_Printf ("'%s' is not a global\n", keyname); - continue; - } - - if (!ED_ParseEpair (pr, pr->pr_globals, key, com_token)) - PR_Error (pr, "ED_ParseGlobals: parse error"); - } -} - -//============================================================================ - - -/* - ED_NewString -*/ -char * -ED_NewString (progs_t *pr, const char *string) -{ - char *new, *new_p; - int i, l; - - l = strlen (string) + 1; - new = Hunk_Alloc (l); - new_p = new; - - for (i = 0; i < l; i++) { - if (string[i] == '\\' && i < l - 1) { - i++; - if (string[i] == 'n') - *new_p++ = '\n'; - else - *new_p++ = '\\'; - } else - *new_p++ = string[i]; - } - - return new; -} - - -/* - ED_ParseEval - - Can parse either fields or globals - returns false if error -*/ -qboolean -ED_ParseEpair (progs_t *pr, pr_type_t *base, ddef_t *key, const char *s) -{ - int i; - char string[128]; - ddef_t *def; - char *v, *w; - pr_type_t *d; - dfunction_t *func; - - d = &base[key->ofs]; - - switch (key->type & ~DEF_SAVEGLOBAL) { - case ev_string: - d->string_var = PR_SetString (pr, ED_NewString (pr, s)); - break; - - case ev_float: - d->float_var = atof (s); - break; - - case ev_vector: - strcpy (string, s); - v = string; - w = string; - for (i = 0; i < 3; i++) { - while (*v && *v != ' ') - v++; - *v = 0; - d->vector_var[i] = atof (w); - w = v = v + 1; - } - break; - - case ev_entity: - d->entity_var = EDICT_TO_PROG (pr, EDICT_NUM (pr, atoi (s))); - break; - - case ev_field: - def = ED_FindField (pr, s); - if (!def) { - Sys_Printf ("Can't find field %s\n", s); - return false; - } - d->integer_var = G_INT (pr, def->ofs); - break; - - case ev_func: - func = ED_FindFunction (pr, s); - if (!func) { - Sys_Printf ("Can't find function %s\n", s); - return false; - } - d->func_var = func - pr->pr_functions; - break; - - default: - break; - } - return true; -} - -/* - ED_ParseEdict - - Parses an edict out of the given string, returning the new position - ed should be a properly initialized empty edict. - Used for initial level load and for savegames. -*/ -const char * -ED_ParseEdict (progs_t *pr, const char *data, edict_t *ent) -{ - ddef_t *key; - qboolean anglehack; - qboolean init = false; - char keyname[256]; - const char *token; - int n; - - // clear it - if (ent != *(pr)->edicts) // hack - memset (&ent->v, 0, pr->progs->entityfields * 4); - - while (1) { // go through all the dictionary pairs - // parse key - data = COM_Parse (data); - if (com_token[0] == '}') - break; - if (!data) - PR_Error (pr, "ED_ParseEntity: EOF without closing brace"); - - token = com_token; - // anglehack is to allow QuakeEd to write single scalar angles - // and allow them to be turned into vectors. (FIXME...) - if (!strcmp (token, "angle")) { - token = "angles"; - anglehack = true; - } else - anglehack = false; - - if (!strcmp (token, "light")) - token = "light_lev"; // hack for single light def - - strcpy (keyname, token); - - // another hack to fix heynames with trailing spaces - n = strlen (keyname); - while (n && keyname[n - 1] == ' ') { - keyname[n - 1] = 0; - n--; - } - - // parse value - data = COM_Parse (data); - if (!data) - PR_Error (pr, "ED_ParseEntity: EOF without closing brace"); - - if (com_token[0] == '}') - PR_Error (pr, "ED_ParseEntity: closing brace without data"); - - init = true; - -// keynames with a leading underscore are used for utility comments, -// and are immediately discarded by quake - if (keyname[0] == '_') - continue; - - key = ED_FindField (pr, keyname); - if (!key) { - if (!pr->parse_field || !pr->parse_field (pr, keyname, com_token)) { - Sys_Printf ("'%s' is not a field\n", keyname); - continue; - } - } else { - int ret; - - if (anglehack) { - ret = ED_ParseEpair (pr, ent->v, key, va ("0 %s 0", com_token)); - } else { - ret = ED_ParseEpair (pr, ent->v, key, com_token); - } - if (!ret) - PR_Error (pr, "ED_ParseEdict: parse error"); - } - } - - if (!init) - ent->free = true; - - return data; -} - - -/* - ED_LoadFromFile - - The entities are directly placed in the array, rather than allocated with - ED_Alloc, because otherwise an error loading the map would have entity - number references out of order. - - Creates a server's entity / program execution context by - parsing textual entity definitions out of an ent file. - - Used for both fresh maps and savegame loads. A fresh map would also need - to call ED_CallSpawnFunctions () to let the objects initialize themselves. -*/ -void -ED_LoadFromFile (progs_t *pr, const char *data) -{ - edict_t *ent = NULL; - int inhibit = 0; - dfunction_t *func; - pr_type_t *classname; - ddef_t *def; - - *pr->globals.time = *(pr)->time; - - while (1) { // parse ents - // parse the opening brace - data = COM_Parse (data); - if (!data) - break; - if (com_token[0] != '{') - PR_Error (pr, "ED_LoadFromFile: found %s when expecting {", com_token); - - if (!ent) - ent = EDICT_NUM (pr, 0); - else - ent = ED_Alloc (pr); - data = ED_ParseEdict (pr, data, ent); - - // remove things from different skill levels or deathmatch - if (pr->prune_edict && pr->prune_edict (pr, ent)) { - ED_Free (pr, ent); - inhibit++; - continue; - } - - // - // immediately call spawn function - // - def = ED_FindField (pr, "classname"); - if (!def) { - Sys_Printf ("No classname for:\n"); - ED_Print (pr, ent); - ED_Free (pr, ent); - continue; - } - classname = &ent->v[def->ofs]; - - // look for the spawn function - func = ED_FindFunction (pr, PR_GetString (pr, classname->string_var)); - if (!func) { - Sys_Printf ("No spawn function for:\n"); - ED_Print (pr, ent); - ED_Free (pr, ent); - continue; - } - - *pr->globals.self = EDICT_TO_PROG (pr, ent); - PR_ExecuteProgram (pr, func - pr->pr_functions); - if (pr->flush) - pr->flush (); - } - - Sys_DPrintf ("%i entities inhibited\n", inhibit); -} edict_t * PR_InitEdicts (progs_t *pr, int num_edicts) diff --git a/libs/gamecode/engine/pr_parse.c b/libs/gamecode/engine/pr_parse.c new file mode 100644 index 000000000..2ccf49cc2 --- /dev/null +++ b/libs/gamecode/engine/pr_parse.c @@ -0,0 +1,488 @@ +/* + pr_parse.c + + map and savegame parsing + + Copyright (C) 1996-1997 Id Software, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +static const char rcsid[] = + "$Id$"; + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#include +#include + +#include "QF/cbuf.h" +#include "QF/crc.h" +#include "QF/cvar.h" +#include "QF/dstring.h" +#include "QF/hash.h" +#include "QF/idparse.h" +#include "QF/progs.h" +#include "QF/qdefs.h" +#include "QF/qendian.h" +#include "QF/quakefs.h" +#include "QF/sys.h" +#include "QF/zone.h" +#include "QF/va.h" + +#include "compat.h" + +/* + PR_UglyValueString + + Returns a string describing *data in a type specific manner + Easier to parse than PR_ValueString +*/ +char * +PR_UglyValueString (progs_t *pr, etype_t type, pr_type_t *val) +{ + static char line[256]; + ddef_t *def; + dfunction_t *f; + + type &= ~DEF_SAVEGLOBAL; + + switch (type) { + case ev_string: + snprintf (line, sizeof (line), "%s", + PR_GetString (pr, val->string_var)); + break; + case ev_entity: + snprintf (line, sizeof (line), "%i", + NUM_FOR_BAD_EDICT (pr, PROG_TO_EDICT (pr, val->entity_var))); + break; + case ev_func: + f = pr->pr_functions + val->func_var; + snprintf (line, sizeof (line), "%s", PR_GetString (pr, f->s_name)); + break; + case ev_field: + def = ED_FieldAtOfs (pr, val->integer_var); + snprintf (line, sizeof (line), "%s", + PR_GetString (pr, def->s_name)); + break; + case ev_void: + strcpy (line, "void"); + break; + case ev_float: + snprintf (line, sizeof (line), "%f", val->float_var); + break; + case ev_vector: + snprintf (line, sizeof (line), "%f %f %f", val->vector_var[0], + val->vector_var[1], val->vector_var[2]); + break; + default: + snprintf (line, sizeof (line), "bad type %i", type); + break; + } + + return line; +} + +/* + ED_Write + + For savegames +*/ +void +ED_Write (progs_t *pr, QFile *f, edict_t *ed) +{ + int i, j; + int type; + char *name; + ddef_t *d; + pr_type_t *v; + + Qprintf (f, "{\n"); + + if (ed->free) { + Qprintf (f, "}\n"); + return; + } + + 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"); +} + +/* + ARCHIVING GLOBALS + + FIXME: need to tag constants, doesn't really work +*/ + +/* + ED_WriteGlobals +*/ +void +ED_WriteGlobals (progs_t *pr, QFile *f) +{ + ddef_t *def; + int i; + char *name; + int type; + + Qprintf (f, "{\n"); + for (i = 0; i < pr->progs->numglobaldefs; i++) { + def = &pr->pr_globaldefs[i]; + type = def->type; + if (!(def->type & DEF_SAVEGLOBAL)) + continue; + type &= ~DEF_SAVEGLOBAL; + + if (type != ev_string && type != ev_float && type != ev_entity) + 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])); + } + Qprintf (f, "}\n"); +} + + +/* + ED_NewString +*/ +char * +ED_NewString (progs_t *pr, const char *string) +{ + char *new, *new_p; + int i, l; + + l = strlen (string) + 1; + new = Hunk_Alloc (l); + new_p = new; + + for (i = 0; i < l; i++) { + if (string[i] == '\\' && i < l - 1) { + i++; + if (string[i] == 'n') + *new_p++ = '\n'; + else + *new_p++ = '\\'; + } else + *new_p++ = string[i]; + } + + return new; +} + + +/* + ED_ParseEval + + Can parse either fields or globals + returns false if error +*/ +qboolean +ED_ParseEpair (progs_t *pr, pr_type_t *base, ddef_t *key, const char *s) +{ + int i; + char string[128]; + ddef_t *def; + char *v, *w; + pr_type_t *d; + dfunction_t *func; + + d = &base[key->ofs]; + + switch (key->type & ~DEF_SAVEGLOBAL) { + case ev_string: + d->string_var = PR_SetString (pr, ED_NewString (pr, s)); + break; + + case ev_float: + d->float_var = atof (s); + break; + + case ev_vector: + strcpy (string, s); + v = string; + w = string; + for (i = 0; i < 3; i++) { + while (*v && *v != ' ') + v++; + *v = 0; + d->vector_var[i] = atof (w); + w = v = v + 1; + } + break; + + case ev_entity: + d->entity_var = EDICT_TO_PROG (pr, EDICT_NUM (pr, atoi (s))); + break; + + case ev_field: + def = ED_FindField (pr, s); + if (!def) { + Sys_Printf ("Can't find field %s\n", s); + return false; + } + d->integer_var = G_INT (pr, def->ofs); + break; + + case ev_func: + func = ED_FindFunction (pr, s); + if (!func) { + Sys_Printf ("Can't find function %s\n", s); + return false; + } + d->func_var = func - pr->pr_functions; + break; + + default: + break; + } + return true; +} + +/* + ED_ParseEdict + + Parses an edict out of the given string, returning the new position + ed should be a properly initialized empty edict. + Used for initial level load and for savegames. +*/ +const char * +ED_ParseEdict (progs_t *pr, const char *data, edict_t *ent) +{ + ddef_t *key; + qboolean anglehack; + qboolean init = false; + char keyname[256]; + const char *token; + int n; + + // clear it + if (ent != *(pr)->edicts) // hack + memset (&ent->v, 0, pr->progs->entityfields * 4); + + while (1) { // go through all the dictionary pairs + // parse key + data = COM_Parse (data); + if (com_token[0] == '}') + break; + if (!data) + PR_Error (pr, "ED_ParseEntity: EOF without closing brace"); + + token = com_token; + // anglehack is to allow QuakeEd to write single scalar angles + // and allow them to be turned into vectors. (FIXME...) + if (!strcmp (token, "angle")) { + token = "angles"; + anglehack = true; + } else + anglehack = false; + + if (!strcmp (token, "light")) + token = "light_lev"; // hack for single light def + + strcpy (keyname, token); + + // another hack to fix heynames with trailing spaces + n = strlen (keyname); + while (n && keyname[n - 1] == ' ') { + keyname[n - 1] = 0; + n--; + } + + // parse value + data = COM_Parse (data); + if (!data) + PR_Error (pr, "ED_ParseEntity: EOF without closing brace"); + + if (com_token[0] == '}') + PR_Error (pr, "ED_ParseEntity: closing brace without data"); + + init = true; + +// keynames with a leading underscore are used for utility comments, +// and are immediately discarded by quake + if (keyname[0] == '_') + continue; + + key = ED_FindField (pr, keyname); + if (!key) { + if (!pr->parse_field || !pr->parse_field (pr, keyname, com_token)) { + Sys_Printf ("'%s' is not a field\n", keyname); + continue; + } + } else { + int ret; + + if (anglehack) { + ret = ED_ParseEpair (pr, ent->v, key, va ("0 %s 0", com_token)); + } else { + ret = ED_ParseEpair (pr, ent->v, key, com_token); + } + if (!ret) + PR_Error (pr, "ED_ParseEdict: parse error"); + } + } + + if (!init) + ent->free = true; + + return data; +} + +/* + ED_ParseGlobals +*/ +void +ED_ParseGlobals (progs_t *pr, const char *data) +{ + char keyname[64]; + ddef_t *key; + + while (1) { + // parse key + data = COM_Parse (data); + if (com_token[0] == '}') + break; + if (!data) + PR_Error (pr, "ED_ParseEntity: EOF without closing brace"); + + strcpy (keyname, com_token); + + // parse value + data = COM_Parse (data); + if (!data) + PR_Error (pr, "ED_ParseEntity: EOF without closing brace"); + + if (com_token[0] == '}') + PR_Error (pr, "ED_ParseEntity: closing brace without data"); + + key = PR_FindGlobal (pr, keyname); + if (!key) { + Sys_Printf ("'%s' is not a global\n", keyname); + continue; + } + + if (!ED_ParseEpair (pr, pr->pr_globals, key, com_token)) + PR_Error (pr, "ED_ParseGlobals: parse error"); + } +} + + +/* + ED_LoadFromFile + + The entities are directly placed in the array, rather than allocated with + ED_Alloc, because otherwise an error loading the map would have entity + number references out of order. + + Creates a server's entity / program execution context by + parsing textual entity definitions out of an ent file. + + Used for both fresh maps and savegame loads. A fresh map would also need + to call ED_CallSpawnFunctions () to let the objects initialize themselves. +*/ +void +ED_LoadFromFile (progs_t *pr, const char *data) +{ + edict_t *ent = NULL; + int inhibit = 0; + dfunction_t *func; + pr_type_t *classname; + ddef_t *def; + + *pr->globals.time = *(pr)->time; + + while (1) { // parse ents + // parse the opening brace + data = COM_Parse (data); + if (!data) + break; + if (com_token[0] != '{') + PR_Error (pr, "ED_LoadFromFile: found %s when expecting {", com_token); + + if (!ent) + ent = EDICT_NUM (pr, 0); + else + ent = ED_Alloc (pr); + data = ED_ParseEdict (pr, data, ent); + + // remove things from different skill levels or deathmatch + if (pr->prune_edict && pr->prune_edict (pr, ent)) { + ED_Free (pr, ent); + inhibit++; + continue; + } + + // + // immediately call spawn function + // + def = ED_FindField (pr, "classname"); + if (!def) { + Sys_Printf ("No classname for:\n"); + ED_Print (pr, ent); + ED_Free (pr, ent); + continue; + } + classname = &ent->v[def->ofs]; + + // look for the spawn function + func = ED_FindFunction (pr, PR_GetString (pr, classname->string_var)); + if (!func) { + Sys_Printf ("No spawn function for:\n"); + ED_Print (pr, ent); + ED_Free (pr, ent); + continue; + } + + *pr->globals.self = EDICT_TO_PROG (pr, ent); + PR_ExecuteProgram (pr, func - pr->pr_functions); + if (pr->flush) + pr->flush (); + } + + Sys_DPrintf ("%i entities inhibited\n", inhibit); +}