quakeforge/libs/gamecode/engine/pr_parse.c

518 lines
11 KiB
C
Raw Normal View History

2002-10-23 04:57:08 +00:00
/*
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
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
static __attribute__ ((unused)) const char rcsid[] =
"$Id$";
2002-10-23 04:57:08 +00:00
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#include <stdarg.h>
#include <stdio.h>
#include "QF/cbuf.h"
#include "QF/crc.h"
#include "QF/cvar.h"
#include "QF/dstring.h"
#include "QF/hash.h"
#include "QF/progs.h"
#include "QF/qdefs.h"
2003-07-17 18:27:00 +00:00
#include "QF/qfplist.h"
2002-10-23 04:57:08 +00:00
#include "QF/qendian.h"
#include "QF/quakefs.h"
#include "QF/script.h"
2002-10-23 04:57:08 +00:00
#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
*/
static char *
2002-10-23 04:57:08 +00:00
PR_UglyValueString (progs_t *pr, etype_t type, pr_type_t *val)
{
static char line[256];
2002-10-23 04:57:08 +00:00
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), "%ld",
2002-10-23 04:57:08 +00:00
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 = PR_FieldAtOfs (pr, val->integer_var);
2002-10-23 04:57:08 +00:00
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)
{
2003-04-17 00:01:48 +00:00
unsigned int i;
int j;
int type;
const char *name;
ddef_t *d;
pr_type_t *v;
2002-10-23 04:57:08 +00:00
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
*/
void
ED_WriteGlobals (progs_t *pr, QFile *f)
{
ddef_t *def;
2003-04-17 00:01:48 +00:00
unsigned int i;
const char *name;
int type;
2002-10-23 04:57:08 +00:00
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");
}
static int
2002-10-23 04:57:08 +00:00
ED_NewString (progs_t *pr, const char *string)
{
char *new, *new_p;
int i, l;
l = strlen (string) + 1;
new = alloca (l);
2002-10-23 04:57:08 +00:00
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 PR_SetString (pr, new);
2002-10-23 04:57:08 +00:00
}
/*
ED_ParseEval
Can parse either fields or globals
returns false if error
*/
qboolean
2002-10-23 04:57:08 +00:00
ED_ParseEpair (progs_t *pr, pr_type_t *base, ddef_t *key, const char *s)
{
int i;
char *string;
2002-10-23 04:57:08 +00:00
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 = ED_NewString (pr, s);
2002-10-23 04:57:08 +00:00
break;
case ev_float:
d->float_var = atof (s);
break;
case ev_vector:
string = strdup (s);
2002-10-23 04:57:08 +00:00
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;
}
free (string);
2002-10-23 04:57:08 +00:00
break;
case ev_entity:
d->entity_var = EDICT_TO_PROG (pr, EDICT_NUM (pr, atoi (s)));
break;
case ev_field:
def = PR_FindField (pr, s);
2002-10-23 04:57:08 +00:00
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 = PR_FindFunction (pr, s);
2002-10-23 04:57:08 +00:00
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
2003-03-25 17:40:02 +00:00
ent should be a properly initialized empty edict.
2002-10-23 04:57:08 +00:00
Used for initial level load and for savegames.
*/
void
ED_ParseEdict (progs_t *pr, script_t *script, edict_t *ent)
2002-10-23 04:57:08 +00:00
{
ddef_t *key;
qboolean anglehack;
qboolean init = false;
dstring_t *keyname = dstring_new ();
2002-10-23 04:57:08 +00:00
const char *token;
int n;
// clear it
if (ent != *(pr)->edicts) // hack
memset (&ent->v, 0, pr->progs->entityfields * 4);
// go through all the dictionary pairs
while (1) {
if (!Script_GetToken (script, 1))
PR_Error (pr, "ED_ParseEntity: EOF without closing brace");
2002-10-23 04:57:08 +00:00
// parse key
if (script->token->str[0] == '}')
2002-10-23 04:57:08 +00:00
break;
token = script->token->str;
2002-10-23 04:57:08 +00:00
// anglehack is to allow QuakeEd to write single scalar angles
// and allow them to be turned into vectors.
2002-10-23 04:57:08 +00:00
if (!strcmp (token, "angle")) {
token = "angles";
anglehack = true;
} else
anglehack = false;
if (!strcmp (token, "light"))
token = "light_lev"; // hack for single light def
dstring_copystr (keyname, token);
2002-10-23 04:57:08 +00:00
// another hack to fix heynames with trailing spaces
n = strlen (keyname->str);
while (n && keyname->str[n - 1] == ' ') {
keyname->str[n - 1] = 0;
2002-10-23 04:57:08 +00:00
n--;
}
// parse value
//FIXME shouldn't cross line
if (!Script_GetToken (script, 1))
2002-10-23 04:57:08 +00:00
PR_Error (pr, "ED_ParseEntity: EOF without closing brace");
token = script->token->str;
2002-10-23 04:57:08 +00:00
if (token[0] == '}')
2002-10-23 04:57:08 +00:00
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->str[0] == '_')
2002-10-23 04:57:08 +00:00
continue;
key = PR_FindField (pr, keyname->str);
2002-10-23 04:57:08 +00:00
if (!key) {
if (!pr->parse_field
|| !pr->parse_field (pr, keyname->str, token)) {
Sys_Printf ("'%s' is not a field\n", keyname->str);
2002-10-23 04:57:08 +00:00
continue;
}
} else {
int ret;
if (anglehack) {
ret = ED_ParseEpair (pr, ent->v, key, va ("0 %s 0", token));
2002-10-23 04:57:08 +00:00
} else {
ret = ED_ParseEpair (pr, ent->v, key, token);
2002-10-23 04:57:08 +00:00
}
if (!ret)
PR_Error (pr, "ED_ParseEdict: parse error");
}
}
if (!init)
ent->free = true;
dstring_delete (keyname);
2002-10-23 04:57:08 +00:00
}
void
ED_ParseGlobals (progs_t *pr, script_t *script)
2002-10-23 04:57:08 +00:00
{
dstring_t *keyname = dstring_new ();
ddef_t *key;
const char *token;
2002-10-23 04:57:08 +00:00
while (1) {
// parse key
if (!Script_GetToken (script, 1))
2002-10-23 04:57:08 +00:00
PR_Error (pr, "ED_ParseEntity: EOF without closing brace");
token = script->token->str;
if (token[0] == '}')
break;
2002-10-23 04:57:08 +00:00
dstring_copystr (keyname, script->token->str);
2002-10-23 04:57:08 +00:00
// parse value
//FIXME shouldn't cross line
if (!Script_GetToken (script, 1))
2002-10-23 04:57:08 +00:00
PR_Error (pr, "ED_ParseEntity: EOF without closing brace");
token = script->token->str;
2002-10-23 04:57:08 +00:00
if (token[0] == '}')
2002-10-23 04:57:08 +00:00
PR_Error (pr, "ED_ParseEntity: closing brace without data");
key = PR_FindGlobal (pr, keyname->str);
2002-10-23 04:57:08 +00:00
if (!key) {
Sys_Printf ("'%s' is not a global\n", keyname->str);
2002-10-23 04:57:08 +00:00
continue;
}
if (!ED_ParseEpair (pr, pr->pr_globals, key, token))
2002-10-23 04:57:08 +00:00
PR_Error (pr, "ED_ParseGlobals: parse error");
}
dstring_delete (keyname);
2002-10-23 04:57:08 +00:00
}
/*
2003-07-17 18:27:00 +00:00
ED_ParseOld
2002-10-23 04:57:08 +00:00
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.
*/
2003-07-17 18:27:00 +00:00
static void
ED_ParseOld (progs_t *pr, script_t *script)
2002-10-23 04:57:08 +00:00
{
edict_t *ent = NULL;
int inhibit = 0;
dfunction_t *func;
pr_type_t *classname;
ddef_t *def;
while (Script_GetToken (script, 1)) { // parse ents
2003-03-25 17:40:02 +00:00
// parse the opening brace
if (script->token->str[0] != '{')
PR_Error (pr, "ED_LoadFromFile: found %s when expecting {",
script->token->str);
2002-10-23 04:57:08 +00:00
if (!ent)
ent = EDICT_NUM (pr, 0);
else
ent = ED_Alloc (pr);
ED_ParseEdict (pr, script, ent);
2002-10-23 04:57:08 +00:00
// 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 = PR_FindField (pr, "classname");
2002-10-23 04:57:08 +00:00
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 = PR_FindFunction (pr, PR_GetString (pr, classname->string_var));
2002-10-23 04:57:08 +00:00
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);
}
2003-07-17 18:27:00 +00:00
void
ED_LoadFromFile (progs_t *pr, const char *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
plitem_t *plist = PL_GetPropertyList (data);
plist = plist;
} else {
// oldstyle entity data
Script_UngetToken (script);
ED_ParseOld (pr, script);
}
2003-07-17 18:27:00 +00:00
}
Script_Delete (script);
}
void
ED_EntityParseFunction (progs_t *pr)
{
pr->edict_parse = P_FUNCTION (pr, 0);
2003-07-17 18:27:00 +00:00
}