2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26 prvm_prog_t prvm_prog_list[PRVM_PROG_MAX];
28 int prvm_type_size[8] = {1,sizeof(string_t)/4,1,3,1,1,sizeof(func_t)/4,sizeof(void *)/4};
30 prvm_eval_t prvm_badvalue; // used only for error returns
32 cvar_t prvm_language = {CVAR_SAVE, "prvm_language", "", "when set, loads progs.dat.LANGUAGENAME.po for string translations; when set to dump, progs.dat.pot is written from the strings in the progs"};
33 // LordHavoc: prints every opcode as it executes - warning: this is significant spew
34 cvar_t prvm_traceqc = {0, "prvm_traceqc", "0", "prints every QuakeC statement as it is executed (only for really thorough debugging!)"};
35 // LordHavoc: counts usage of each QuakeC statement
36 cvar_t prvm_statementprofiling = {0, "prvm_statementprofiling", "0", "counts how many times each QuakeC statement has been executed, these counts are displayed in prvm_printfunction output (if enabled)"};
37 cvar_t prvm_timeprofiling = {0, "prvm_timeprofiling", "0", "counts how long each function has been executed, these counts are displayed in prvm_profile output (if enabled)"};
38 cvar_t prvm_backtraceforwarnings = {0, "prvm_backtraceforwarnings", "0", "print a backtrace for warnings too"};
39 cvar_t prvm_leaktest = {0, "prvm_leaktest", "0", "try to detect memory leaks in strings or entities"};
40 cvar_t prvm_leaktest_ignore_classnames = {0, "prvm_leaktest_ignore_classnames", "", "classnames of entities to NOT leak check because they are found by find(world, classname, ...) but are actually spawned by QC code (NOT map entities)"};
41 cvar_t prvm_errordump = {0, "prvm_errordump", "0", "write a savegame on crash to crash-server.dmp"};
42 cvar_t prvm_reuseedicts_startuptime = {0, "prvm_reuseedicts_startuptime", "2", "allows immediate re-use of freed entity slots during start of new level (value in seconds)"};
43 cvar_t prvm_reuseedicts_neverinsameframe = {0, "prvm_reuseedicts_neverinsameframe", "1", "never allows re-use of freed entity slots during same frame"};
45 static double prvm_reuseedicts_always_allow = 0;
46 qboolean prvm_runawaycheck = true;
48 //============================================================================
56 static void PRVM_MEM_Alloc(prvm_prog_t *prog)
60 // reserve space for the null entity aka world
61 // check bound of max_edicts
62 prog->max_edicts = bound(1 + prog->reserved_edicts, prog->max_edicts, prog->limit_edicts);
63 prog->num_edicts = bound(1 + prog->reserved_edicts, prog->num_edicts, prog->max_edicts);
65 // edictprivate_size has to be min as big prvm_edict_private_t
66 prog->edictprivate_size = max(prog->edictprivate_size,(int)sizeof(prvm_edict_private_t));
69 prog->edicts = (prvm_edict_t *)Mem_Alloc(prog->progs_mempool,prog->limit_edicts * sizeof(prvm_edict_t));
71 // alloc edict private space
72 prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size);
75 prog->entityfieldsarea = prog->entityfields * prog->max_edicts;
76 prog->edictsfields = (vec_t *)Mem_Alloc(prog->progs_mempool, prog->entityfieldsarea * sizeof(vec_t));
79 for(i = 0; i < prog->max_edicts; i++)
81 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
82 prog->edicts[i].fields.vp = prog->edictsfields + i * prog->entityfields;
88 PRVM_MEM_IncreaseEdicts
91 void PRVM_MEM_IncreaseEdicts(prvm_prog_t *prog)
95 if(prog->max_edicts >= prog->limit_edicts)
98 prog->begin_increase_edicts(prog);
101 prog->max_edicts = min(prog->max_edicts + 256, prog->limit_edicts);
103 prog->entityfieldsarea = prog->entityfields * prog->max_edicts;
104 prog->edictsfields = (vec_t*)Mem_Realloc(prog->progs_mempool, (void *)prog->edictsfields, prog->entityfieldsarea * sizeof(vec_t));
105 prog->edictprivate = (void *)Mem_Realloc(prog->progs_mempool, (void *)prog->edictprivate, prog->max_edicts * prog->edictprivate_size);
107 //set e and v pointers
108 for(i = 0; i < prog->max_edicts; i++)
110 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
111 prog->edicts[i].fields.vp = prog->edictsfields + i * prog->entityfields;
114 prog->end_increase_edicts(prog);
117 //============================================================================
120 int PRVM_ED_FindFieldOffset(prvm_prog_t *prog, const char *field)
123 d = PRVM_ED_FindField(prog, field);
129 int PRVM_ED_FindGlobalOffset(prvm_prog_t *prog, const char *global)
132 d = PRVM_ED_FindGlobal(prog, global);
138 func_t PRVM_ED_FindFunctionOffset(prvm_prog_t *prog, const char *function)
141 f = PRVM_ED_FindFunction(prog, function);
144 return (func_t)(f - prog->functions);
152 prvm_prog_t *PRVM_ProgFromString(const char *str)
154 if (!strcmp(str, "server"))
156 if (!strcmp(str, "client"))
158 if (!strcmp(str, "menu"))
165 PRVM_FriendlyProgFromString
168 prvm_prog_t *PRVM_FriendlyProgFromString(const char *str)
170 prvm_prog_t *prog = PRVM_ProgFromString(str);
173 Con_Printf("%s: unknown program name\n", str);
178 Con_Printf("%s: program is not loaded\n", str);
188 Sets everything to NULL
191 void PRVM_ED_ClearEdict(prvm_prog_t *prog, prvm_edict_t *e)
193 memset(e->fields.vp, 0, prog->entityfields * 4);
194 e->priv.required->free = false;
196 // AK: Let the init_edict function determine if something needs to be initialized
197 prog->init_edict(prog, e);
200 const char *PRVM_AllocationOrigin(prvm_prog_t *prog)
203 if(prog->leaktest_active)
204 if(prog->depth > 0) // actually in QC code and not just parsing the entities block of a map/savegame
206 buf = (char *)PRVM_Alloc(128);
207 PRVM_ShortStackTrace(prog, buf, 128);
216 Returns if this particular edict could get allocated by PRVM_ED_Alloc
219 qboolean PRVM_ED_CanAlloc(prvm_prog_t *prog, prvm_edict_t *e)
221 if(!e->priv.required->free)
223 if(prvm_reuseedicts_always_allow == realtime)
225 if(realtime <= e->priv.required->freetime + 0.1 && prvm_reuseedicts_neverinsameframe.integer)
226 return false; // never allow reuse in same frame (causes networking trouble)
227 if(e->priv.required->freetime < prog->starttime + prvm_reuseedicts_startuptime.value)
229 if(realtime > e->priv.required->freetime + 1)
231 return false; // entity slot still blocked because the entity was freed less than one second ago
238 Either finds a free edict, or allocates a new one.
239 Try to avoid reusing an entity that was recently freed, because it
240 can cause the client to think the entity morphed into something else
241 instead of being removed and recreated, which can cause interpolated
242 angles and bad trails.
245 prvm_edict_t *PRVM_ED_Alloc(prvm_prog_t *prog)
250 // the client qc dont need maxclients
251 // thus it doesnt need to use svs.maxclients
252 // AK: changed i=svs.maxclients+1
253 // AK: changed so the edict 0 wont spawn -> used as reserved/world entity
254 // although the menu/client has no world
255 for (i = prog->reserved_edicts + 1;i < prog->num_edicts;i++)
257 e = PRVM_EDICT_NUM(i);
258 if(PRVM_ED_CanAlloc(prog, e))
260 PRVM_ED_ClearEdict (prog, e);
261 e->priv.required->allocation_origin = PRVM_AllocationOrigin(prog);
266 if (i == prog->limit_edicts)
267 prog->error_cmd("%s: PRVM_ED_Alloc: no free edicts", prog->name);
270 if (prog->num_edicts >= prog->max_edicts)
271 PRVM_MEM_IncreaseEdicts(prog);
273 e = PRVM_EDICT_NUM(i);
274 PRVM_ED_ClearEdict(prog, e);
276 e->priv.required->allocation_origin = PRVM_AllocationOrigin(prog);
285 Marks the edict as free
286 FIXME: walk all entities and NULL out references to this entity
289 void PRVM_ED_Free(prvm_prog_t *prog, prvm_edict_t *ed)
291 // dont delete the null entity (world) or reserved edicts
292 if (ed - prog->edicts <= prog->reserved_edicts)
295 prog->free_edict(prog, ed);
297 ed->priv.required->free = true;
298 ed->priv.required->freetime = realtime;
299 if(ed->priv.required->allocation_origin)
301 Mem_Free((char *)ed->priv.required->allocation_origin);
302 ed->priv.required->allocation_origin = NULL;
306 //===========================================================================
313 static ddef_t *PRVM_ED_GlobalAtOfs (prvm_prog_t *prog, int ofs)
318 for (i = 0;i < prog->numglobaldefs;i++)
320 def = &prog->globaldefs[i];
332 ddef_t *PRVM_ED_FieldAtOfs (prvm_prog_t *prog, int ofs)
337 for (i = 0;i < prog->numfielddefs;i++)
339 def = &prog->fielddefs[i];
351 ddef_t *PRVM_ED_FindField (prvm_prog_t *prog, const char *name)
356 for (i = 0;i < prog->numfielddefs;i++)
358 def = &prog->fielddefs[i];
359 if (!strcmp(PRVM_GetString(prog, def->s_name), name))
370 ddef_t *PRVM_ED_FindGlobal (prvm_prog_t *prog, const char *name)
375 for (i = 0;i < prog->numglobaldefs;i++)
377 def = &prog->globaldefs[i];
378 if (!strcmp(PRVM_GetString(prog, def->s_name), name))
390 mfunction_t *PRVM_ED_FindFunction (prvm_prog_t *prog, const char *name)
395 for (i = 0;i < prog->numfunctions;i++)
397 func = &prog->functions[i];
398 if (!strcmp(PRVM_GetString(prog, func->s_name), name))
409 Returns a string describing *data in a type specific manner
412 static char *PRVM_ValueString (prvm_prog_t *prog, etype_t type, prvm_eval_t *val, char *line, size_t linelength)
418 type = (etype_t)((int) type & ~DEF_SAVEGLOBAL);
423 strlcpy (line, PRVM_GetString (prog, val->string), linelength);
427 if (n < 0 || n >= prog->max_edicts)
428 dpsnprintf (line, linelength, "entity %i (invalid!)", n);
430 dpsnprintf (line, linelength, "entity %i", n);
433 f = prog->functions + val->function;
434 dpsnprintf (line, linelength, "%s()", PRVM_GetString(prog, f->s_name));
437 def = PRVM_ED_FieldAtOfs ( prog, val->_int );
438 dpsnprintf (line, linelength, ".%s", PRVM_GetString(prog, def->s_name));
441 dpsnprintf (line, linelength, "void");
444 // LordHavoc: changed from %5.1f to %10.4f
445 dpsnprintf (line, linelength, "%10.4f", val->_float);
448 // LordHavoc: changed from %5.1f to %10.4f
449 dpsnprintf (line, linelength, "'%10.4f %10.4f %10.4f'", val->vector[0], val->vector[1], val->vector[2]);
452 dpsnprintf (line, linelength, "pointer");
455 dpsnprintf (line, linelength, "bad type %i", (int) type);
466 Returns a string describing *data in a type specific manner
467 Easier to parse than PR_ValueString
470 char *PRVM_UglyValueString (prvm_prog_t *prog, etype_t type, prvm_eval_t *val, char *line, size_t linelength)
477 type = (etype_t)((int)type & ~DEF_SAVEGLOBAL);
482 // Parse the string a bit to turn special characters
483 // (like newline, specifically) into escape codes,
484 // this fixes saving games from various mods
485 s = PRVM_GetString (prog, val->string);
486 for (i = 0;i < (int)linelength - 2 && *s;)
515 dpsnprintf (line, linelength, "%i", PRVM_NUM_FOR_EDICT(PRVM_PROG_TO_EDICT(val->edict)));
518 f = prog->functions + val->function;
519 strlcpy (line, PRVM_GetString (prog, f->s_name), linelength);
522 def = PRVM_ED_FieldAtOfs ( prog, val->_int );
523 dpsnprintf (line, linelength, ".%s", PRVM_GetString(prog, def->s_name));
526 dpsnprintf (line, linelength, "void");
529 dpsnprintf (line, linelength, "%.9g", val->_float);
532 dpsnprintf (line, linelength, "%.9g %.9g %.9g", val->vector[0], val->vector[1], val->vector[2]);
535 dpsnprintf (line, linelength, "bad type %i", type);
546 Returns a string with a description and the contents of a global,
547 padded to 20 field width
550 char *PRVM_GlobalString (prvm_prog_t *prog, int ofs, char *line, size_t linelength)
556 char valuebuf[MAX_INPUTLINE];
558 val = (void *)&prog->globals.generic[ofs];
559 def = PRVM_ED_GlobalAtOfs(prog, ofs);
561 dpsnprintf (line, linelength, "GLOBAL%i", ofs);
564 s = PRVM_ValueString (prog, (etype_t)def->type, (prvm_eval_t *)val, valuebuf, sizeof(valuebuf));
565 dpsnprintf (line, linelength, "%s (=%s)", PRVM_GetString(prog, def->s_name), s);
569 //for ( ; i<20 ; i++)
570 // strcat (line," ");
576 char *PRVM_GlobalStringNoContents (prvm_prog_t *prog, int ofs, char *line, size_t linelength)
581 def = PRVM_ED_GlobalAtOfs(prog, ofs);
583 dpsnprintf (line, linelength, "GLOBAL%i", ofs);
585 dpsnprintf (line, linelength, "%s", PRVM_GetString(prog, def->s_name));
588 //for ( ; i<20 ; i++)
589 // strcat (line," ");
603 // LordHavoc: optimized this to print out much more quickly (tempstring)
604 // LordHavoc: changed to print out every 4096 characters (incase there are a lot of fields to print)
605 void PRVM_ED_Print(prvm_prog_t *prog, prvm_edict_t *ed, const char *wildcard_fieldname)
613 char tempstring[MAX_INPUTLINE], tempstring2[260]; // temporary string buffers
614 char valuebuf[MAX_INPUTLINE];
616 if (ed->priv.required->free)
618 Con_Printf("%s: FREE\n",prog->name);
623 dpsnprintf(tempstring, sizeof(tempstring), "\n%s EDICT %i:\n", prog->name, PRVM_NUM_FOR_EDICT(ed));
624 for (i = 1;i < prog->numfielddefs;i++)
626 d = &prog->fielddefs[i];
627 name = PRVM_GetString(prog, d->s_name);
628 if(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
629 continue; // skip _x, _y, _z vars
631 // Check Field Name Wildcard
632 if(wildcard_fieldname)
633 if( !matchpattern(name, wildcard_fieldname, 1) )
634 // Didn't match; skip
637 v = (int *)(ed->fields.vp + d->ofs);
639 // if the value is still all 0, skip the field
640 type = d->type & ~DEF_SAVEGLOBAL;
642 for (j=0 ; j<prvm_type_size[type] ; j++)
645 if (j == prvm_type_size[type])
648 if (strlen(name) > sizeof(tempstring2)-4)
650 memcpy (tempstring2, name, sizeof(tempstring2)-4);
651 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
652 tempstring2[sizeof(tempstring2)-1] = 0;
655 strlcat(tempstring, name, sizeof(tempstring));
656 for (l = strlen(name);l < 14;l++)
657 strlcat(tempstring, " ", sizeof(tempstring));
658 strlcat(tempstring, " ", sizeof(tempstring));
660 name = PRVM_ValueString(prog, (etype_t)d->type, (prvm_eval_t *)v, valuebuf, sizeof(valuebuf));
661 if (strlen(name) > sizeof(tempstring2)-4)
663 memcpy (tempstring2, name, sizeof(tempstring2)-4);
664 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
665 tempstring2[sizeof(tempstring2)-1] = 0;
668 strlcat(tempstring, name, sizeof(tempstring));
669 strlcat(tempstring, "\n", sizeof(tempstring));
670 if (strlen(tempstring) >= sizeof(tempstring)/2)
672 Con_Print(tempstring);
677 Con_Print(tempstring);
687 extern cvar_t developer_entityparsing;
688 void PRVM_ED_Write (prvm_prog_t *prog, qfile_t *f, prvm_edict_t *ed)
696 char valuebuf[MAX_INPUTLINE];
700 if (ed->priv.required->free)
706 for (i = 1;i < prog->numfielddefs;i++)
708 d = &prog->fielddefs[i];
709 name = PRVM_GetString(prog, d->s_name);
711 if(developer_entityparsing.integer)
712 Con_Printf("PRVM_ED_Write: at entity %d field %s\n", PRVM_NUM_FOR_EDICT(ed), name);
714 //if(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
715 if(strlen(name) > 1 && name[strlen(name)-2] == '_')
716 continue; // skip _x, _y, _z vars, and ALSO other _? vars as some mods expect them to be never saved (TODO: a gameplayfix for using the "more precise" condition above?)
718 v = (int *)(ed->fields.vp + d->ofs);
720 // if the value is still all 0, skip the field
721 type = d->type & ~DEF_SAVEGLOBAL;
722 for (j=0 ; j<prvm_type_size[type] ; j++)
725 if (j == prvm_type_size[type])
728 FS_Printf(f,"\"%s\" ",name);
729 prog->statestring = va(vabuf, sizeof(vabuf), "PRVM_ED_Write, ent=%d, name=%s", i, name);
730 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString(prog, (etype_t)d->type, (prvm_eval_t *)v, valuebuf, sizeof(valuebuf)));
731 prog->statestring = NULL;
737 void PRVM_ED_PrintNum (prvm_prog_t *prog, int ent, const char *wildcard_fieldname)
739 PRVM_ED_Print(prog, PRVM_EDICT_NUM(ent), wildcard_fieldname);
744 PRVM_ED_PrintEdicts_f
746 For debugging, prints all the entities in the current server
749 void PRVM_ED_PrintEdicts_f (void)
753 const char *wildcard_fieldname;
755 if(Cmd_Argc() < 2 || Cmd_Argc() > 3)
757 Con_Print("prvm_edicts <program name> <optional field name wildcard>\n");
761 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
765 wildcard_fieldname = Cmd_Argv(2);
767 wildcard_fieldname = NULL;
769 Con_Printf("%s: %i entities\n", prog->name, prog->num_edicts);
770 for (i=0 ; i<prog->num_edicts ; i++)
771 PRVM_ED_PrintNum (prog, i, wildcard_fieldname);
778 For debugging, prints a single edict
781 static void PRVM_ED_PrintEdict_f (void)
785 const char *wildcard_fieldname;
787 if(Cmd_Argc() < 3 || Cmd_Argc() > 4)
789 Con_Print("prvm_edict <program name> <edict number> <optional field name wildcard>\n");
793 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
796 i = atoi (Cmd_Argv(2));
797 if (i >= prog->num_edicts)
799 Con_Print("Bad edict number\n");
803 // Optional Wildcard Provided
804 wildcard_fieldname = Cmd_Argv(3);
807 wildcard_fieldname = NULL;
808 PRVM_ED_PrintNum (prog, i, wildcard_fieldname);
818 // 2 possibilities : 1. just displaying the active edict count
819 // 2. making a function pointer [x]
820 static void PRVM_ED_Count_f (void)
826 Con_Print("prvm_count <program name>\n");
830 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
833 prog->count_edicts(prog);
837 ==============================================================================
841 FIXME: need to tag constants, doesn't really work
842 ==============================================================================
850 void PRVM_ED_WriteGlobals (prvm_prog_t *prog, qfile_t *f)
857 char valuebuf[MAX_INPUTLINE];
860 for (i = 0;i < prog->numglobaldefs;i++)
862 def = &prog->globaldefs[i];
864 if ( !(def->type & DEF_SAVEGLOBAL) )
866 type &= ~DEF_SAVEGLOBAL;
868 if (type != ev_string && type != ev_float && type != ev_entity)
871 name = PRVM_GetString(prog, def->s_name);
873 if(developer_entityparsing.integer)
874 Con_Printf("PRVM_ED_WriteGlobals: at global %s\n", name);
876 prog->statestring = va(vabuf, sizeof(vabuf), "PRVM_ED_WriteGlobals, name=%s", name);
877 FS_Printf(f,"\"%s\" ", name);
878 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString(prog, (etype_t)type, (prvm_eval_t *)&prog->globals.generic[def->ofs], valuebuf, sizeof(valuebuf)));
879 prog->statestring = NULL;
889 void PRVM_ED_ParseGlobals (prvm_prog_t *prog, const char *data)
891 char keyname[MAX_INPUTLINE];
897 if (!COM_ParseToken_Simple(&data, false, false, true))
898 prog->error_cmd("PRVM_ED_ParseGlobals: EOF without closing brace");
899 if (com_token[0] == '}')
902 if (developer_entityparsing.integer)
903 Con_Printf("Key: \"%s\"", com_token);
905 strlcpy (keyname, com_token, sizeof(keyname));
908 if (!COM_ParseToken_Simple(&data, false, true, true))
909 prog->error_cmd("PRVM_ED_ParseGlobals: EOF without closing brace");
911 if (developer_entityparsing.integer)
912 Con_Printf(" \"%s\"\n", com_token);
914 if (com_token[0] == '}')
915 prog->error_cmd("PRVM_ED_ParseGlobals: closing brace without data");
917 key = PRVM_ED_FindGlobal (prog, keyname);
920 Con_DPrintf("'%s' is not a global on %s\n", keyname, prog->name);
924 if (!PRVM_ED_ParseEpair(prog, NULL, key, com_token, true))
925 prog->error_cmd("PRVM_ED_ParseGlobals: parse error");
929 //============================================================================
936 Can parse either fields or globals
937 returns false if error
940 qboolean PRVM_ED_ParseEpair(prvm_prog_t *prog, prvm_edict_t *ent, ddef_t *key, const char *s, qboolean parsebackslash)
949 val = (prvm_eval_t *)(ent->fields.vp + key->ofs);
951 val = (prvm_eval_t *)(prog->globals.generic + key->ofs);
952 switch (key->type & ~DEF_SAVEGLOBAL)
955 l = (int)strlen(s) + 1;
956 val->string = PRVM_AllocString(prog, l, &new_p);
957 for (i = 0;i < l;i++)
959 if (s[i] == '\\' && s[i+1] && parsebackslash)
964 else if (s[i] == 'r')
975 while (*s && ISWHITESPACE(*s))
977 val->_float = atof(s);
981 for (i = 0;i < 3;i++)
983 while (*s && ISWHITESPACE(*s))
987 val->vector[i] = atof(s);
988 while (!ISWHITESPACE(*s))
996 while (*s && ISWHITESPACE(*s))
999 if (i >= prog->limit_edicts)
1000 Con_Printf("PRVM_ED_ParseEpair: ev_entity reference too large (edict %u >= MAX_EDICTS %u) on %s\n", (unsigned int)i, prog->limit_edicts, prog->name);
1001 while (i >= prog->max_edicts)
1002 PRVM_MEM_IncreaseEdicts(prog);
1003 // if IncreaseEdicts was called the base pointer needs to be updated
1005 val = (prvm_eval_t *)(ent->fields.vp + key->ofs);
1006 val->edict = PRVM_EDICT_TO_PROG(PRVM_EDICT_NUM((int)i));
1012 Con_DPrintf("PRVM_ED_ParseEpair: Bogus field name %s in %s\n", s, prog->name);
1015 def = PRVM_ED_FindField(prog, s + 1);
1018 Con_DPrintf("PRVM_ED_ParseEpair: Can't find field %s in %s\n", s, prog->name);
1021 val->_int = def->ofs;
1025 func = PRVM_ED_FindFunction(prog, s);
1028 Con_Printf("PRVM_ED_ParseEpair: Can't find function %s in %s\n", s, prog->name);
1031 val->function = func - prog->functions;
1035 Con_Printf("PRVM_ED_ParseEpair: Unknown key->type %i for key \"%s\" on %s\n", key->type, PRVM_GetString(prog, key->s_name), prog->name);
1045 Console command to send a string to QC function GameCommand of the
1049 sv_cmd adminmsg 3 "do not teamkill"
1050 cl_cmd someclientcommand
1051 menu_cmd somemenucommand
1053 All progs can support this extension; sg calls it in server QC, cg in client
1057 static void PRVM_GameCommand(const char *whichprogs, const char *whichcmd)
1062 Con_Printf("%s text...\n", whichcmd);
1066 if (!(prog = PRVM_FriendlyProgFromString(whichprogs)))
1069 if(!PRVM_allfunction(GameCommand))
1071 Con_Printf("%s program do not support GameCommand!\n", whichprogs);
1075 int restorevm_tempstringsbuf_cursize;
1080 restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
1081 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(prog, s ? s : "");
1082 prog->ExecuteProgram(prog, PRVM_allfunction(GameCommand), "QC function GameCommand is missing");
1083 prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1086 static void PRVM_GameCommand_Server_f(void)
1088 PRVM_GameCommand("server", "sv_cmd");
1090 static void PRVM_GameCommand_Client_f(void)
1092 PRVM_GameCommand("client", "cl_cmd");
1094 static void PRVM_GameCommand_Menu_f(void)
1096 PRVM_GameCommand("menu", "menu_cmd");
1103 Console command to load a field of a specified edict
1106 static void PRVM_ED_EdictGet_f(void)
1113 char valuebuf[MAX_INPUTLINE];
1115 if(Cmd_Argc() != 4 && Cmd_Argc() != 5)
1117 Con_Print("prvm_edictget <program name> <edict number> <field> [<cvar>]\n");
1121 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
1124 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1126 if((key = PRVM_ED_FindField(prog, Cmd_Argv(3))) == 0)
1128 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1132 v = (prvm_eval_t *)(ed->fields.vp + key->ofs);
1133 s = PRVM_UglyValueString(prog, (etype_t)key->type, v, valuebuf, sizeof(valuebuf));
1136 cvar_t *cvar = Cvar_FindVar(Cmd_Argv(4));
1137 if (cvar && cvar->flags & CVAR_READONLY)
1139 Con_Printf("prvm_edictget: %s is read-only\n", cvar->name);
1142 Cvar_Get(Cmd_Argv(4), s, 0, NULL);
1145 Con_Printf("%s\n", s);
1151 static void PRVM_ED_GlobalGet_f(void)
1157 char valuebuf[MAX_INPUTLINE];
1159 if(Cmd_Argc() != 3 && Cmd_Argc() != 4)
1161 Con_Print("prvm_globalget <program name> <global> [<cvar>]\n");
1165 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
1168 key = PRVM_ED_FindGlobal(prog, Cmd_Argv(2));
1171 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
1175 v = (prvm_eval_t *) &prog->globals.generic[key->ofs];
1176 s = PRVM_UglyValueString(prog, (etype_t)key->type, v, valuebuf, sizeof(valuebuf));
1179 cvar_t *cvar = Cvar_FindVar(Cmd_Argv(3));
1180 if (cvar && cvar->flags & CVAR_READONLY)
1182 Con_Printf("prvm_globalget: %s is read-only\n", cvar->name);
1185 Cvar_Get(Cmd_Argv(3), s, 0, NULL);
1188 Con_Printf("%s\n", s);
1198 Console command to set a field of a specified edict
1201 static void PRVM_ED_EdictSet_f(void)
1209 Con_Print("prvm_edictset <program name> <edict number> <field> <value>\n");
1213 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
1216 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1218 if((key = PRVM_ED_FindField(prog, Cmd_Argv(3))) == 0)
1219 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1221 PRVM_ED_ParseEpair(prog, ed, key, Cmd_Argv(4), true);
1225 ====================
1228 Parses an edict out of the given string, returning the new position
1229 ed should be a properly initialized empty edict.
1230 Used for initial level load and for savegames.
1231 ====================
1233 const char *PRVM_ED_ParseEdict (prvm_prog_t *prog, const char *data, prvm_edict_t *ent)
1243 // go through all the dictionary pairs
1247 if (!COM_ParseToken_Simple(&data, false, false, true))
1248 prog->error_cmd("PRVM_ED_ParseEdict: EOF without closing brace");
1249 if (developer_entityparsing.integer)
1250 Con_Printf("Key: \"%s\"", com_token);
1251 if (com_token[0] == '}')
1254 // anglehack is to allow QuakeEd to write single scalar angles
1255 // and allow them to be turned into vectors. (FIXME...)
1256 if (!strcmp(com_token, "angle"))
1258 strlcpy (com_token, "angles", sizeof(com_token));
1264 // FIXME: change light to _light to get rid of this hack
1265 if (!strcmp(com_token, "light"))
1266 strlcpy (com_token, "light_lev", sizeof(com_token)); // hack for single light def
1268 strlcpy (keyname, com_token, sizeof(keyname));
1270 // another hack to fix keynames with trailing spaces
1271 n = strlen(keyname);
1272 while (n && keyname[n-1] == ' ')
1279 if (!COM_ParseToken_Simple(&data, false, false, true))
1280 prog->error_cmd("PRVM_ED_ParseEdict: EOF without closing brace");
1281 if (developer_entityparsing.integer)
1282 Con_Printf(" \"%s\"\n", com_token);
1284 if (com_token[0] == '}')
1285 prog->error_cmd("PRVM_ED_ParseEdict: closing brace without data");
1289 // ignore attempts to set key "" (this problem occurs in nehahra neh1m8.bsp)
1293 // keynames with a leading underscore are used for utility comments,
1294 // and are immediately discarded by quake
1295 if (keyname[0] == '_')
1298 key = PRVM_ED_FindField (prog, keyname);
1301 Con_DPrintf("%s: '%s' is not a field\n", prog->name, keyname);
1308 strlcpy (temp, com_token, sizeof(temp));
1309 dpsnprintf (com_token, sizeof(com_token), "0 %s 0", temp);
1312 if (!PRVM_ED_ParseEpair(prog, ent, key, com_token, strcmp(keyname, "wad") != 0))
1313 prog->error_cmd("PRVM_ED_ParseEdict: parse error");
1317 ent->priv.required->free = true;
1325 PRVM_ED_LoadFromFile
1327 The entities are directly placed in the array, rather than allocated with
1328 PRVM_ED_Alloc, because otherwise an error loading the map would have entity
1329 number references out of order.
1331 Creates a server's entity / program execution context by
1332 parsing textual entity definitions out of an ent file.
1334 Used for both fresh maps and savegame loads. A fresh map would also need
1335 to call PRVM_ED_CallSpawnFunctions () to let the objects initialize themselves.
1338 void PRVM_ED_LoadFromFile (prvm_prog_t *prog, const char *data)
1341 int parsed, inhibited, spawned, died;
1342 const char *funcname;
1351 prvm_reuseedicts_always_allow = realtime;
1356 // parse the opening brace
1357 if (!COM_ParseToken_Simple(&data, false, false, true))
1359 if (com_token[0] != '{')
1360 prog->error_cmd("PRVM_ED_LoadFromFile: %s: found %s when expecting {", prog->name, com_token);
1362 // CHANGED: this is not conform to PR_LoadFromFile
1363 if(prog->loadintoworld)
1365 prog->loadintoworld = false;
1366 ent = PRVM_EDICT_NUM(0);
1369 ent = PRVM_ED_Alloc(prog);
1372 if (ent != prog->edicts) // hack
1373 memset (ent->fields.vp, 0, prog->entityfields * 4);
1375 data = PRVM_ED_ParseEdict (prog, data, ent);
1378 // remove the entity ?
1379 if(!prog->load_edict(prog, ent))
1381 PRVM_ED_Free(prog, ent);
1386 if (PRVM_serverfunction(SV_OnEntityPreSpawnFunction))
1389 PRVM_serverglobalfloat(time) = sv.time;
1390 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1391 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityPreSpawnFunction), "QC function SV_OnEntityPreSpawnFunction is missing");
1394 if(ent->priv.required->free)
1401 // immediately call spawn function, but only if there is a self global and a classname
1403 if(!ent->priv.required->free)
1405 if (!PRVM_alledictstring(ent, classname))
1407 Con_Print("No classname for:\n");
1408 PRVM_ED_Print(prog, ent, NULL);
1409 PRVM_ED_Free (prog, ent);
1413 // look for the spawn function
1414 funcname = PRVM_GetString(prog, PRVM_alledictstring(ent, classname));
1415 func = PRVM_ED_FindFunction (prog, va(vabuf, sizeof(vabuf), "spawnfunc_%s", funcname));
1417 if(!PRVM_allglobalfloat(require_spawnfunc_prefix))
1418 func = PRVM_ED_FindFunction (prog, funcname);
1422 // check for OnEntityNoSpawnFunction
1423 if (PRVM_serverfunction(SV_OnEntityNoSpawnFunction))
1426 PRVM_serverglobalfloat(time) = sv.time;
1427 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1428 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityNoSpawnFunction), "QC function SV_OnEntityNoSpawnFunction is missing");
1432 if (developer.integer > 0) // don't confuse non-developers with errors
1434 Con_Print("No spawn function for:\n");
1435 PRVM_ED_Print(prog, ent, NULL);
1437 PRVM_ED_Free (prog, ent);
1438 continue; // not included in "inhibited" count
1444 PRVM_serverglobalfloat(time) = sv.time;
1445 PRVM_allglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1446 prog->ExecuteProgram(prog, func - prog->functions, "");
1450 if(!ent->priv.required->free)
1451 if (PRVM_serverfunction(SV_OnEntityPostSpawnFunction))
1454 PRVM_serverglobalfloat(time) = sv.time;
1455 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1456 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityPostSpawnFunction), "QC function SV_OnEntityPostSpawnFunction is missing");
1460 if (ent->priv.required->free)
1464 Con_DPrintf("%s: %i new entities parsed, %i new inhibited, %i (%i new) spawned (whereas %i removed self, %i stayed)\n", prog->name, parsed, inhibited, prog->num_edicts, spawned, died, spawned - died);
1466 prvm_reuseedicts_always_allow = 0;
1469 static void PRVM_FindOffsets(prvm_prog_t *prog)
1471 // field and global searches use -1 for NULL
1472 memset(&prog->fieldoffsets, -1, sizeof(prog->fieldoffsets));
1473 memset(&prog->globaloffsets, -1, sizeof(prog->globaloffsets));
1474 // function searches use 0 for NULL
1475 memset(&prog->funcoffsets, 0, sizeof(prog->funcoffsets));
1476 #define PRVM_DECLARE_serverglobalfloat(x)
1477 #define PRVM_DECLARE_serverglobalvector(x)
1478 #define PRVM_DECLARE_serverglobalstring(x)
1479 #define PRVM_DECLARE_serverglobaledict(x)
1480 #define PRVM_DECLARE_serverglobalfunction(x)
1481 #define PRVM_DECLARE_clientglobalfloat(x)
1482 #define PRVM_DECLARE_clientglobalvector(x)
1483 #define PRVM_DECLARE_clientglobalstring(x)
1484 #define PRVM_DECLARE_clientglobaledict(x)
1485 #define PRVM_DECLARE_clientglobalfunction(x)
1486 #define PRVM_DECLARE_menuglobalfloat(x)
1487 #define PRVM_DECLARE_menuglobalvector(x)
1488 #define PRVM_DECLARE_menuglobalstring(x)
1489 #define PRVM_DECLARE_menuglobaledict(x)
1490 #define PRVM_DECLARE_menuglobalfunction(x)
1491 #define PRVM_DECLARE_serverfieldfloat(x)
1492 #define PRVM_DECLARE_serverfieldvector(x)
1493 #define PRVM_DECLARE_serverfieldstring(x)
1494 #define PRVM_DECLARE_serverfieldedict(x)
1495 #define PRVM_DECLARE_serverfieldfunction(x)
1496 #define PRVM_DECLARE_clientfieldfloat(x)
1497 #define PRVM_DECLARE_clientfieldvector(x)
1498 #define PRVM_DECLARE_clientfieldstring(x)
1499 #define PRVM_DECLARE_clientfieldedict(x)
1500 #define PRVM_DECLARE_clientfieldfunction(x)
1501 #define PRVM_DECLARE_menufieldfloat(x)
1502 #define PRVM_DECLARE_menufieldvector(x)
1503 #define PRVM_DECLARE_menufieldstring(x)
1504 #define PRVM_DECLARE_menufieldedict(x)
1505 #define PRVM_DECLARE_menufieldfunction(x)
1506 #define PRVM_DECLARE_serverfunction(x)
1507 #define PRVM_DECLARE_clientfunction(x)
1508 #define PRVM_DECLARE_menufunction(x)
1509 #define PRVM_DECLARE_field(x) prog->fieldoffsets.x = PRVM_ED_FindFieldOffset(prog, #x);
1510 #define PRVM_DECLARE_global(x) prog->globaloffsets.x = PRVM_ED_FindGlobalOffset(prog, #x);
1511 #define PRVM_DECLARE_function(x) prog->funcoffsets.x = PRVM_ED_FindFunctionOffset(prog, #x);
1512 #include "prvm_offsets.h"
1513 #undef PRVM_DECLARE_serverglobalfloat
1514 #undef PRVM_DECLARE_serverglobalvector
1515 #undef PRVM_DECLARE_serverglobalstring
1516 #undef PRVM_DECLARE_serverglobaledict
1517 #undef PRVM_DECLARE_serverglobalfunction
1518 #undef PRVM_DECLARE_clientglobalfloat
1519 #undef PRVM_DECLARE_clientglobalvector
1520 #undef PRVM_DECLARE_clientglobalstring
1521 #undef PRVM_DECLARE_clientglobaledict
1522 #undef PRVM_DECLARE_clientglobalfunction
1523 #undef PRVM_DECLARE_menuglobalfloat
1524 #undef PRVM_DECLARE_menuglobalvector
1525 #undef PRVM_DECLARE_menuglobalstring
1526 #undef PRVM_DECLARE_menuglobaledict
1527 #undef PRVM_DECLARE_menuglobalfunction
1528 #undef PRVM_DECLARE_serverfieldfloat
1529 #undef PRVM_DECLARE_serverfieldvector
1530 #undef PRVM_DECLARE_serverfieldstring
1531 #undef PRVM_DECLARE_serverfieldedict
1532 #undef PRVM_DECLARE_serverfieldfunction
1533 #undef PRVM_DECLARE_clientfieldfloat
1534 #undef PRVM_DECLARE_clientfieldvector
1535 #undef PRVM_DECLARE_clientfieldstring
1536 #undef PRVM_DECLARE_clientfieldedict
1537 #undef PRVM_DECLARE_clientfieldfunction
1538 #undef PRVM_DECLARE_menufieldfloat
1539 #undef PRVM_DECLARE_menufieldvector
1540 #undef PRVM_DECLARE_menufieldstring
1541 #undef PRVM_DECLARE_menufieldedict
1542 #undef PRVM_DECLARE_menufieldfunction
1543 #undef PRVM_DECLARE_serverfunction
1544 #undef PRVM_DECLARE_clientfunction
1545 #undef PRVM_DECLARE_menufunction
1546 #undef PRVM_DECLARE_field
1547 #undef PRVM_DECLARE_global
1548 #undef PRVM_DECLARE_function
1553 typedef struct dpfield_s
1560 #define DPFIELDS (sizeof(dpfields) / sizeof(dpfield_t))
1562 dpfield_t dpfields[] =
1573 #define PO_HASHSIZE 16384
1574 typedef struct po_string_s
1577 struct po_string_s *nextonhashchain;
1582 po_string_t *hashtable[PO_HASHSIZE];
1585 static void PRVM_PO_UnparseString(char *out, const char *in, size_t outsize)
1594 case '\a': if(outsize >= 2) { *out++ = '\\'; *out++ = 'a'; outsize -= 2; } break;
1595 case '\b': if(outsize >= 2) { *out++ = '\\'; *out++ = 'b'; outsize -= 2; } break;
1596 case '\t': if(outsize >= 2) { *out++ = '\\'; *out++ = 't'; outsize -= 2; } break;
1597 case '\r': if(outsize >= 2) { *out++ = '\\'; *out++ = 'r'; outsize -= 2; } break;
1598 case '\n': if(outsize >= 2) { *out++ = '\\'; *out++ = 'n'; outsize -= 2; } break;
1599 case '\\': if(outsize >= 2) { *out++ = '\\'; *out++ = '\\'; outsize -= 2; } break;
1600 case '"': if(outsize >= 2) { *out++ = '\\'; *out++ = '"'; outsize -= 2; } break;
1602 if(*in >= 0 && *in <= 0x1F)
1607 *out++ = '0' + ((*in & 0700) >> 6);
1608 *out++ = '0' + ((*in & 0070) >> 3);
1609 *out++ = '0' + ((*in & 0007));
1626 static void PRVM_PO_ParseString(char *out, const char *in, size_t outsize)
1639 case 'a': if(outsize > 0) { *out++ = '\a'; --outsize; } break;
1640 case 'b': if(outsize > 0) { *out++ = '\b'; --outsize; } break;
1641 case 't': if(outsize > 0) { *out++ = '\t'; --outsize; } break;
1642 case 'r': if(outsize > 0) { *out++ = '\r'; --outsize; } break;
1643 case 'n': if(outsize > 0) { *out++ = '\n'; --outsize; } break;
1644 case '\\': if(outsize > 0) { *out++ = '\\'; --outsize; } break;
1645 case '"': if(outsize > 0) { *out++ = '"'; --outsize; } break;
1646 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
1650 if(*in >= '0' && *in <= '7')
1653 *out = (*out << 3) | (*in - '0');
1656 if(*in >= '0' && *in <= '7')
1659 *out = (*out << 3) | (*in - '0');
1670 if(outsize > 0) { *out++ = *in; --outsize; }
1685 static po_t *PRVM_PO_Load(const char *filename, mempool_t *pool)
1690 char inbuf[MAX_INPUTLINE];
1691 char decodedbuf[MAX_INPUTLINE];
1694 po_string_t thisstr;
1695 const char *buf = (const char *) FS_LoadFile(filename, pool, true, NULL);
1700 memset(&thisstr, 0, sizeof(thisstr)); // hush compiler warning
1702 po = (po_t *)Mem_Alloc(pool, sizeof(*po));
1703 memset(po, 0, sizeof(*po));
1711 p = strchr(p, '\n');
1717 if(*p == '\r' || *p == '\n')
1722 if(!strncmp(p, "msgid \"", 7))
1727 else if(!strncmp(p, "msgstr \"", 8))
1734 p = strchr(p, '\n');
1744 q = strchr(p, '\n');
1751 if((size_t)(q - p) >= (size_t) sizeof(inbuf))
1753 strlcpy(inbuf, p, q - p); // not - 1, because this adds a NUL
1754 PRVM_PO_ParseString(decodedbuf + decodedpos, inbuf, sizeof(decodedbuf) - decodedpos);
1755 decodedpos += strlen(decodedbuf + decodedpos);
1765 Mem_Free(thisstr.key);
1766 thisstr.key = (char *)Mem_Alloc(pool, decodedpos + 1);
1767 memcpy(thisstr.key, decodedbuf, decodedpos + 1);
1769 else if(decodedpos > 0 && thisstr.key) // skip empty translation results
1771 thisstr.value = (char *)Mem_Alloc(pool, decodedpos + 1);
1772 memcpy(thisstr.value, decodedbuf, decodedpos + 1);
1773 hashindex = CRC_Block((const unsigned char *) thisstr.key, strlen(thisstr.key)) % PO_HASHSIZE;
1774 thisstr.nextonhashchain = po->hashtable[hashindex];
1775 po->hashtable[hashindex] = (po_string_t *)Mem_Alloc(pool, sizeof(thisstr));
1776 memcpy(po->hashtable[hashindex], &thisstr, sizeof(thisstr));
1777 memset(&thisstr, 0, sizeof(thisstr));
1781 Mem_Free((char *) buf);
1784 static const char *PRVM_PO_Lookup(po_t *po, const char *str)
1786 int hashindex = CRC_Block((const unsigned char *) str, strlen(str)) % PO_HASHSIZE;
1787 po_string_t *p = po->hashtable[hashindex];
1790 if(!strcmp(str, p->key))
1792 p = p->nextonhashchain;
1796 static void PRVM_PO_Destroy(po_t *po)
1799 for(i = 0; i < PO_HASHSIZE; ++i)
1801 po_string_t *p = po->hashtable[i];
1805 p = p->nextonhashchain;
1814 void PRVM_LeakTest(prvm_prog_t *prog);
1815 void PRVM_Prog_Reset(prvm_prog_t *prog)
1817 PRVM_LeakTest(prog);
1818 prog->reset_cmd(prog);
1819 Mem_FreePool(&prog->progs_mempool);
1821 PRVM_PO_Destroy((po_t *) prog->po);
1822 memset(prog,0,sizeof(prvm_prog_t));
1830 static void PRVM_LoadLNO( prvm_prog_t *prog, const char *progname ) {
1831 fs_offset_t filesize;
1833 unsigned int *header;
1836 FS_StripExtension( progname, filename, sizeof( filename ) );
1837 strlcat( filename, ".lno", sizeof( filename ) );
1839 lno = FS_LoadFile( filename, tempmempool, false, &filesize );
1845 <Spike> SafeWrite (h, &lnotype, sizeof(int));
1846 <Spike> SafeWrite (h, &version, sizeof(int));
1847 <Spike> SafeWrite (h, &numglobaldefs, sizeof(int));
1848 <Spike> SafeWrite (h, &numpr_globals, sizeof(int));
1849 <Spike> SafeWrite (h, &numfielddefs, sizeof(int));
1850 <Spike> SafeWrite (h, &numstatements, sizeof(int));
1851 <Spike> SafeWrite (h, statement_linenums, numstatements*sizeof(int));
1853 if ((unsigned int)filesize < (6 + prog->progs_numstatements) * sizeof(int))
1859 header = (unsigned int *) lno;
1860 if( header[ 0 ] == *(unsigned int *) "LNOF" &&
1861 LittleLong( header[ 1 ] ) == 1 &&
1862 (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs_numglobaldefs &&
1863 (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs_numglobals &&
1864 (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs_numfielddefs &&
1865 (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs_numstatements )
1867 prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof( int ) );
1868 memcpy( prog->statement_linenums, (int *) lno + 6, prog->progs_numstatements * sizeof( int ) );
1878 void PRVM_Prog_Load(prvm_prog_t *prog, const char * filename, int numrequiredfunc, const char **required_func, int numrequiredfields, prvm_required_field_t *required_field, int numrequiredglobals, prvm_required_field_t *required_global)
1881 dprograms_t *dprograms;
1882 dstatement_t *instatements;
1883 ddef_t *infielddefs;
1884 ddef_t *inglobaldefs;
1886 dfunction_t *infunctions;
1888 fs_offset_t filesize;
1889 int requiredglobalspace;
1897 prog->error_cmd("PRVM_LoadProgs: there is already a %s program loaded!", prog->name );
1899 dprograms = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
1900 if (dprograms == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
1901 prog->error_cmd("PRVM_LoadProgs: couldn't load %s for %s", filename, prog->name);
1902 // TODO bounds check header fields (e.g. numstatements), they must never go behind end of file
1904 prog->profiletime = Sys_DirtyTime();
1905 prog->starttime = realtime;
1907 Con_DPrintf("%s programs occupy %iK.\n", prog->name, (int)(filesize/1024));
1909 requiredglobalspace = 0;
1910 for (i = 0;i < numrequiredglobals;i++)
1911 requiredglobalspace += required_global[i].type == ev_vector ? 3 : 1;
1913 prog->filecrc = CRC_Block((unsigned char *)dprograms, filesize);
1915 // byte swap the header
1916 prog->progs_version = LittleLong(dprograms->version);
1917 prog->progs_crc = LittleLong(dprograms->crc);
1918 if (prog->progs_version != PROG_VERSION)
1919 prog->error_cmd("%s: %s has wrong version number (%i should be %i)", prog->name, filename, prog->progs_version, PROG_VERSION);
1920 instatements = (dstatement_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_statements));
1921 prog->progs_numstatements = LittleLong(dprograms->numstatements);
1922 inglobaldefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globaldefs));
1923 prog->progs_numglobaldefs = LittleLong(dprograms->numglobaldefs);
1924 infielddefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_fielddefs));
1925 prog->progs_numfielddefs = LittleLong(dprograms->numfielddefs);
1926 infunctions = (dfunction_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_functions));
1927 prog->progs_numfunctions = LittleLong(dprograms->numfunctions);
1928 instrings = (char *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_strings));
1929 prog->progs_numstrings = LittleLong(dprograms->numstrings);
1930 inglobals = (float *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globals));
1931 prog->progs_numglobals = LittleLong(dprograms->numglobals);
1932 prog->progs_entityfields = LittleLong(dprograms->entityfields);
1934 prog->numstatements = prog->progs_numstatements;
1935 prog->numglobaldefs = prog->progs_numglobaldefs;
1936 prog->numfielddefs = prog->progs_numfielddefs;
1937 prog->numfunctions = prog->progs_numfunctions;
1938 prog->numstrings = prog->progs_numstrings;
1939 prog->numglobals = prog->progs_numglobals;
1940 prog->entityfields = prog->progs_entityfields;
1942 if (LittleLong(dprograms->ofs_strings) + prog->progs_numstrings >= (int)filesize)
1943 prog->error_cmd("%s: %s strings go past end of file", prog->name, filename);
1944 prog->strings = (char *)Mem_Alloc(prog->progs_mempool, prog->progs_numstrings);
1945 memcpy(prog->strings, instrings, prog->progs_numstrings);
1946 prog->stringssize = prog->progs_numstrings;
1948 prog->numknownstrings = 0;
1949 prog->maxknownstrings = 0;
1950 prog->knownstrings = NULL;
1951 prog->knownstrings_freeable = NULL;
1953 Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
1955 // we need to expand the globaldefs and fielddefs to include engine defs
1956 prog->globaldefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobaldefs + numrequiredglobals) * sizeof(ddef_t));
1957 prog->globals.generic = (float *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobals + requiredglobalspace) * sizeof(float));
1958 prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numfielddefs + numrequiredfields) * sizeof(ddef_t));
1959 // we need to convert the statements to our memory format
1960 prog->statements = (mstatement_t *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(mstatement_t));
1961 // allocate space for profiling statement usage
1962 prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(*prog->statement_profile));
1963 // functions need to be converted to the memory format
1964 prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs_numfunctions);
1966 for (i = 0;i < prog->progs_numfunctions;i++)
1968 prog->functions[i].first_statement = LittleLong(infunctions[i].first_statement);
1969 prog->functions[i].parm_start = LittleLong(infunctions[i].parm_start);
1970 prog->functions[i].s_name = LittleLong(infunctions[i].s_name);
1971 prog->functions[i].s_file = LittleLong(infunctions[i].s_file);
1972 prog->functions[i].numparms = LittleLong(infunctions[i].numparms);
1973 prog->functions[i].locals = LittleLong(infunctions[i].locals);
1974 memcpy(prog->functions[i].parm_size, infunctions[i].parm_size, sizeof(infunctions[i].parm_size));
1975 if(prog->functions[i].first_statement >= prog->numstatements)
1976 prog->error_cmd("PRVM_LoadProgs: out of bounds function statement (function %d) in %s", i, prog->name);
1977 // TODO bounds check parm_start, s_name, s_file, numparms, locals, parm_size
1980 // copy the globaldefs to the new globaldefs list
1981 for (i=0 ; i<prog->numglobaldefs ; i++)
1983 prog->globaldefs[i].type = LittleShort(inglobaldefs[i].type);
1984 prog->globaldefs[i].ofs = LittleShort(inglobaldefs[i].ofs);
1985 prog->globaldefs[i].s_name = LittleLong(inglobaldefs[i].s_name);
1986 // TODO bounds check ofs, s_name
1989 // append the required globals
1990 for (i = 0;i < numrequiredglobals;i++)
1992 prog->globaldefs[prog->numglobaldefs].type = required_global[i].type;
1993 prog->globaldefs[prog->numglobaldefs].ofs = prog->numglobals;
1994 prog->globaldefs[prog->numglobaldefs].s_name = PRVM_SetEngineString(prog, required_global[i].name);
1995 if (prog->globaldefs[prog->numglobaldefs].type == ev_vector)
1996 prog->numglobals += 3;
1999 prog->numglobaldefs++;
2002 // copy the progs fields to the new fields list
2003 for (i = 0;i < prog->numfielddefs;i++)
2005 prog->fielddefs[i].type = LittleShort(infielddefs[i].type);
2006 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
2007 prog->error_cmd("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", prog->name);
2008 prog->fielddefs[i].ofs = LittleShort(infielddefs[i].ofs);
2009 prog->fielddefs[i].s_name = LittleLong(infielddefs[i].s_name);
2010 // TODO bounds check ofs, s_name
2013 // append the required fields
2014 for (i = 0;i < numrequiredfields;i++)
2016 prog->fielddefs[prog->numfielddefs].type = required_field[i].type;
2017 prog->fielddefs[prog->numfielddefs].ofs = prog->entityfields;
2018 prog->fielddefs[prog->numfielddefs].s_name = PRVM_SetEngineString(prog, required_field[i].name);
2019 if (prog->fielddefs[prog->numfielddefs].type == ev_vector)
2020 prog->entityfields += 3;
2022 prog->entityfields++;
2023 prog->numfielddefs++;
2026 // LordHavoc: TODO: reorder globals to match engine struct
2027 // LordHavoc: TODO: reorder fields to match engine struct
2028 #define remapglobal(index) (index)
2029 #define remapfield(index) (index)
2032 for (i = 0;i < prog->progs_numglobals;i++)
2033 ((int *)prog->globals.generic)[remapglobal(i)] = LittleLong(((int *)inglobals)[i]);
2035 // LordHavoc: TODO: support 32bit progs statement formats
2036 // copy, remap globals in statements, bounds check
2037 for (i = 0;i < prog->progs_numstatements;i++)
2039 op = (opcode_t)LittleShort(instatements[i].op);
2040 a = (unsigned short)LittleShort(instatements[i].a);
2041 b = (unsigned short)LittleShort(instatements[i].b);
2042 c = (unsigned short)LittleShort(instatements[i].c);
2048 if (a >= prog->progs_numglobals || b + i < 0 || b + i >= prog->progs_numstatements)
2049 prog->error_cmd("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", i, prog->name);
2050 prog->statements[i].op = op;
2051 prog->statements[i].operand[0] = remapglobal(a);
2052 prog->statements[i].operand[1] = -1;
2053 prog->statements[i].operand[2] = -1;
2054 prog->statements[i].jumpabsolute = i + b;
2058 if (a + i < 0 || a + i >= prog->progs_numstatements)
2059 prog->error_cmd("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, prog->name);
2060 prog->statements[i].op = op;
2061 prog->statements[i].operand[0] = -1;
2062 prog->statements[i].operand[1] = -1;
2063 prog->statements[i].operand[2] = -1;
2064 prog->statements[i].jumpabsolute = i + a;
2067 Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", (int)op, i, prog->name);
2068 // global global global
2103 if (a >= prog->progs_numglobals || b >= prog->progs_numglobals || c >= prog->progs_numglobals)
2104 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
2105 prog->statements[i].op = op;
2106 prog->statements[i].operand[0] = remapglobal(a);
2107 prog->statements[i].operand[1] = remapglobal(b);
2108 prog->statements[i].operand[2] = remapglobal(c);
2109 prog->statements[i].jumpabsolute = -1;
2111 // global none global
2117 if (a >= prog->progs_numglobals || c >= prog->progs_numglobals)
2118 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2119 prog->statements[i].op = op;
2120 prog->statements[i].operand[0] = remapglobal(a);
2121 prog->statements[i].operand[1] = -1;
2122 prog->statements[i].operand[2] = remapglobal(c);
2123 prog->statements[i].jumpabsolute = -1;
2139 if (a >= prog->progs_numglobals || b >= prog->progs_numglobals)
2140 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2141 prog->statements[i].op = op;
2142 prog->statements[i].operand[0] = remapglobal(a);
2143 prog->statements[i].operand[1] = remapglobal(b);
2144 prog->statements[i].operand[2] = -1;
2145 prog->statements[i].jumpabsolute = -1;
2159 if ( a >= prog->progs_numglobals)
2160 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2161 prog->statements[i].op = op;
2162 prog->statements[i].operand[0] = remapglobal(a);
2163 prog->statements[i].operand[1] = -1;
2164 prog->statements[i].operand[2] = -1;
2165 prog->statements[i].jumpabsolute = -1;
2169 if(prog->numstatements < 1)
2171 prog->error_cmd("PRVM_LoadProgs: empty program in %s", prog->name);
2173 else switch(prog->statements[prog->numstatements - 1].op)
2180 prog->error_cmd("PRVM_LoadProgs: program may fall off the edge (does not end with RETURN, GOTO or DONE) in %s", prog->name);
2184 // we're done with the file now
2185 Mem_Free(dprograms);
2188 // check required functions
2189 for(i=0 ; i < numrequiredfunc ; i++)
2190 if(PRVM_ED_FindFunction(prog, required_func[i]) == 0)
2191 prog->error_cmd("%s: %s not found in %s",prog->name, required_func[i], filename);
2193 PRVM_LoadLNO(prog, filename);
2195 PRVM_Init_Exec(prog);
2197 if(*prvm_language.string)
2198 // in CSQC we really shouldn't be able to change how stuff works... sorry for now
2199 // later idea: include a list of authorized .po file checksums with the csprogs
2201 qboolean deftrans = prog == CLVM_prog;
2202 const char *realfilename = (prog != CLVM_prog ? filename : csqc_progname.string);
2203 if(deftrans) // once we have dotranslate_ strings, ALWAYS use the opt-in method!
2205 for (i=0 ; i<prog->numglobaldefs ; i++)
2208 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2209 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2210 if(name && !strncmp(name, "dotranslate_", 12))
2217 if(!strcmp(prvm_language.string, "dump"))
2219 qfile_t *f = FS_OpenRealFile(va(vabuf, sizeof(vabuf), "%s.pot", realfilename), "w", false);
2220 Con_Printf("Dumping to %s.pot\n", realfilename);
2223 for (i=0 ; i<prog->numglobaldefs ; i++)
2226 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2227 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2228 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2230 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2231 const char *value = PRVM_GetString(prog, val->string);
2234 char buf[MAX_INPUTLINE];
2235 PRVM_PO_UnparseString(buf, value, sizeof(buf));
2236 FS_Printf(f, "msgid \"%s\"\nmsgstr \"\"\n\n", buf);
2245 po_t *po = PRVM_PO_Load(va(vabuf, sizeof(vabuf), "%s.%s.po", realfilename, prvm_language.string), prog->progs_mempool);
2248 for (i=0 ; i<prog->numglobaldefs ; i++)
2251 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2252 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2253 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2255 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2256 const char *value = PRVM_GetString(prog, val->string);
2259 value = PRVM_PO_Lookup(po, value);
2261 val->string = PRVM_SetEngineString(prog, value);
2269 for (i=0 ; i<prog->numglobaldefs ; i++)
2272 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2273 //Con_Printf("found var %s\n", name);
2275 && !strncmp(name, "autocvar_", 9)
2276 && !(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
2279 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2280 cvar_t *cvar = Cvar_FindVar(name + 9);
2281 //Con_Printf("PRVM_LoadProgs: autocvar global %s in %s, processing...\n", name, prog->name);
2286 Con_DPrintf("PRVM_LoadProgs: no cvar for autocvar global %s in %s, creating...\n", name, prog->name);
2287 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2290 if((float)((int)(val->_float)) == val->_float)
2291 dpsnprintf(buf, sizeof(buf), "%i", (int)(val->_float));
2293 dpsnprintf(buf, sizeof(buf), "%.9g", val->_float);
2297 dpsnprintf(buf, sizeof(buf), "%.9g %.9g %.9g", val->vector[0], val->vector[1], val->vector[2]); value = buf;
2300 value = PRVM_GetString(prog, val->string);
2303 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2306 cvar = Cvar_Get(name + 9, value, 0, NULL);
2307 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2309 val->string = PRVM_SetEngineString(prog, cvar->string);
2310 cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2313 prog->error_cmd("PRVM_LoadProgs: could not create cvar for autocvar global %s in %s", name, prog->name);
2314 cvar->globaldefindex_progid[prog - prvm_prog_list] = prog->id;
2315 cvar->globaldefindex[prog - prvm_prog_list] = i;
2317 else if((cvar->flags & CVAR_PRIVATE) == 0)
2319 // MUST BE SYNCED WITH cvar.c Cvar_Set
2322 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2325 val->_float = cvar->value;
2329 VectorClear(val->vector);
2330 for (j = 0;j < 3;j++)
2332 while (*s && ISWHITESPACE(*s))
2336 val->vector[j] = atof(s);
2337 while (!ISWHITESPACE(*s))
2344 val->string = PRVM_SetEngineString(prog, cvar->string);
2345 cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2348 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2351 cvar->globaldefindex_progid[prog - prvm_prog_list] = prog->id;
2352 cvar->globaldefindex[prog - prvm_prog_list] = i;
2355 Con_Printf("PRVM_LoadProgs: private cvar for autocvar global %s in %s\n", name, prog->name);
2361 prog->loaded = TRUE;
2363 // set flags & ddef_ts in prog
2367 PRVM_FindOffsets(prog);
2369 prog->init_cmd(prog);
2372 PRVM_MEM_Alloc(prog);
2376 static void PRVM_Fields_f (void)
2379 int i, j, ednum, used, usedamount;
2381 char tempstring[MAX_INPUTLINE], tempstring2[260];
2391 Con_Print("no progs loaded\n");
2398 Con_Print("prvm_fields <program name>\n");
2402 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2405 counts = (int *)Mem_Alloc(tempmempool, prog->numfielddefs * sizeof(int));
2406 for (ednum = 0;ednum < prog->max_edicts;ednum++)
2408 ed = PRVM_EDICT_NUM(ednum);
2409 if (ed->priv.required->free)
2411 for (i = 1;i < prog->numfielddefs;i++)
2413 d = &prog->fielddefs[i];
2414 name = PRVM_GetString(prog, d->s_name);
2415 if (name[strlen(name)-2] == '_')
2416 continue; // skip _x, _y, _z vars
2417 v = (int *)(ed->fields.vp + d->ofs);
2418 // if the value is still all 0, skip the field
2419 for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
2432 for (i = 0;i < prog->numfielddefs;i++)
2434 d = &prog->fielddefs[i];
2435 name = PRVM_GetString(prog, d->s_name);
2436 if (name[strlen(name)-2] == '_')
2437 continue; // skip _x, _y, _z vars
2438 switch(d->type & ~DEF_SAVEGLOBAL)
2441 strlcat(tempstring, "string ", sizeof(tempstring));
2444 strlcat(tempstring, "entity ", sizeof(tempstring));
2447 strlcat(tempstring, "function ", sizeof(tempstring));
2450 strlcat(tempstring, "field ", sizeof(tempstring));
2453 strlcat(tempstring, "void ", sizeof(tempstring));
2456 strlcat(tempstring, "float ", sizeof(tempstring));
2459 strlcat(tempstring, "vector ", sizeof(tempstring));
2462 strlcat(tempstring, "pointer ", sizeof(tempstring));
2465 dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
2466 strlcat(tempstring, tempstring2, sizeof(tempstring));
2469 if (strlen(name) > sizeof(tempstring2)-4)
2471 memcpy (tempstring2, name, sizeof(tempstring2)-4);
2472 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
2473 tempstring2[sizeof(tempstring2)-1] = 0;
2476 strlcat(tempstring, name, sizeof(tempstring));
2477 for (j = (int)strlen(name);j < 25;j++)
2478 strlcat(tempstring, " ", sizeof(tempstring));
2479 dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
2480 strlcat(tempstring, tempstring2, sizeof(tempstring));
2481 strlcat(tempstring, "\n", sizeof(tempstring));
2482 if (strlen(tempstring) >= sizeof(tempstring)/2)
2484 Con_Print(tempstring);
2490 usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
2494 Con_Printf("%s: %i entity fields (%i in use), totalling %i bytes per edict (%i in use), %i edicts allocated, %i bytes total spent on edict fields (%i needed)\n", prog->name, prog->entityfields, used, prog->entityfields * 4, usedamount * 4, prog->max_edicts, prog->entityfields * 4 * prog->max_edicts, usedamount * 4 * prog->max_edicts);
2497 static void PRVM_Globals_f (void)
2501 const char *wildcard;
2507 Con_Print("no progs loaded\n");
2510 if(Cmd_Argc () < 2 || Cmd_Argc() > 3)
2512 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
2516 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2519 if( Cmd_Argc() == 3)
2520 wildcard = Cmd_Argv(2);
2524 Con_Printf("%s :", prog->name);
2526 for (i = 0;i < prog->numglobaldefs;i++)
2529 if( !matchpattern( PRVM_GetString(prog, prog->globaldefs[i].s_name), wildcard, 1) )
2534 Con_Printf("%s\n", PRVM_GetString(prog, prog->globaldefs[i].s_name));
2536 Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->numglobals, numculled, prog->numglobals * 4);
2544 static void PRVM_Global_f(void)
2548 char valuebuf[MAX_INPUTLINE];
2549 if( Cmd_Argc() != 3 ) {
2550 Con_Printf( "prvm_global <program name> <global name>\n" );
2554 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2557 global = PRVM_ED_FindGlobal( prog, Cmd_Argv(2) );
2559 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2561 Con_Printf( "%s: %s\n", Cmd_Argv(2), PRVM_ValueString( prog, (etype_t)global->type, PRVM_GLOBALFIELDVALUE(global->ofs), valuebuf, sizeof(valuebuf) ) );
2569 static void PRVM_GlobalSet_f(void)
2573 if( Cmd_Argc() != 4 ) {
2574 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2578 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2581 global = PRVM_ED_FindGlobal( prog, Cmd_Argv(2) );
2583 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2585 PRVM_ED_ParseEpair( prog, NULL, global, Cmd_Argv(3), true );
2593 void PRVM_Init (void)
2595 Cmd_AddCommand ("prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2596 Cmd_AddCommand ("prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2597 Cmd_AddCommand ("prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2598 Cmd_AddCommand ("prvm_profile", PRVM_Profile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu)");
2599 Cmd_AddCommand ("prvm_childprofile", PRVM_ChildProfile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu), sorted by time taken in function with child calls");
2600 Cmd_AddCommand ("prvm_callprofile", PRVM_CallProfile_f, "prints execution statistics about the most time consuming QuakeC calls from the engine in the selected VM (server, client, menu)");
2601 Cmd_AddCommand ("prvm_fields", PRVM_Fields_f, "prints usage statistics on properties (how many entities have non-zero values) in the selected VM (server, client, menu)");
2602 Cmd_AddCommand ("prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2603 Cmd_AddCommand ("prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2604 Cmd_AddCommand ("prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2605 Cmd_AddCommand ("prvm_edictset", PRVM_ED_EdictSet_f, "changes value of a specified property of a specified entity in the selected VM (server, client, menu)");
2606 Cmd_AddCommand ("prvm_edictget", PRVM_ED_EdictGet_f, "retrieves the value of a specified property of a specified entity in the selected VM (server, client menu) into a cvar or to the console");
2607 Cmd_AddCommand ("prvm_globalget", PRVM_ED_GlobalGet_f, "retrieves the value of a specified global variable in the selected VM (server, client menu) into a cvar or to the console");
2608 Cmd_AddCommand ("prvm_printfunction", PRVM_PrintFunction_f, "prints a disassembly (QuakeC instructions) of the specified function in the selected VM (server, client, menu)");
2609 Cmd_AddCommand ("cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2610 Cmd_AddCommand ("menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2611 Cmd_AddCommand ("sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2613 Cvar_RegisterVariable (&prvm_language);
2614 Cvar_RegisterVariable (&prvm_traceqc);
2615 Cvar_RegisterVariable (&prvm_statementprofiling);
2616 Cvar_RegisterVariable (&prvm_timeprofiling);
2617 Cvar_RegisterVariable (&prvm_backtraceforwarnings);
2618 Cvar_RegisterVariable (&prvm_leaktest);
2619 Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames);
2620 Cvar_RegisterVariable (&prvm_errordump);
2621 Cvar_RegisterVariable (&prvm_reuseedicts_startuptime);
2622 Cvar_RegisterVariable (&prvm_reuseedicts_neverinsameframe);
2624 // COMMANDLINEOPTION: PRVM: -norunaway disables the runaway loop check (it might be impossible to exit DarkPlaces if used!)
2625 prvm_runawaycheck = !COM_CheckParm("-norunaway");
2635 void PRVM_Prog_Init(prvm_prog_t *prog)
2638 PRVM_Prog_Reset(prog);
2640 memset(prog, 0, sizeof(prvm_prog_t));
2641 prog->leaktest_active = prvm_leaktest.integer != 0;
2644 // LordHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
2645 unsigned int PRVM_EDICT_NUM_ERROR(prvm_prog_t *prog, unsigned int n, const char *filename, int fileline)
2647 prog->error_cmd("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", prog->name, n, filename, fileline);
2651 #define PRVM_KNOWNSTRINGBASE 0x40000000
2653 const char *PRVM_GetString(prvm_prog_t *prog, int num)
2658 VM_Warning(prog, "PRVM_GetString: Invalid string offset (%i < 0)\n", num);
2661 else if (num < prog->stringssize)
2663 // constant string from progs.dat
2664 return prog->strings + num;
2666 else if (num <= prog->stringssize + prog->tempstringsbuf.maxsize)
2668 // tempstring returned by engine to QC (becomes invalid after returning to engine)
2669 num -= prog->stringssize;
2670 if (num < prog->tempstringsbuf.cursize)
2671 return (char *)prog->tempstringsbuf.data + num;
2674 VM_Warning(prog, "PRVM_GetString: Invalid temp-string offset (%i >= %i prog->tempstringsbuf.cursize)\n", num, prog->tempstringsbuf.cursize);
2678 else if (num & PRVM_KNOWNSTRINGBASE)
2681 num = num - PRVM_KNOWNSTRINGBASE;
2682 if (num >= 0 && num < prog->numknownstrings)
2684 if (!prog->knownstrings[num])
2686 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
2689 return prog->knownstrings[num];
2693 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
2699 // invalid string offset
2700 VM_Warning(prog, "PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
2705 const char *PRVM_ChangeEngineString(prvm_prog_t *prog, int i, const char *s)
2708 i = i - PRVM_KNOWNSTRINGBASE;
2709 if(i < 0 || i >= prog->numknownstrings)
2710 prog->error_cmd("PRVM_ChangeEngineString: s is not an engine string");
2711 old = prog->knownstrings[i];
2712 prog->knownstrings[i] = s;
2716 int PRVM_SetEngineString(prvm_prog_t *prog, const char *s)
2721 if (s >= prog->strings && s <= prog->strings + prog->stringssize)
2722 prog->error_cmd("PRVM_SetEngineString: s in prog->strings area");
2723 // if it's in the tempstrings area, use a reserved range
2724 // (otherwise we'd get millions of useless string offsets cluttering the database)
2725 if (s >= (char *)prog->tempstringsbuf.data && s < (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.maxsize)
2727 return prog->stringssize + (s - (char *)prog->tempstringsbuf.data);
2729 // see if it's a known string address
2730 for (i = 0;i < prog->numknownstrings;i++)
2731 if (prog->knownstrings[i] == s)
2732 return PRVM_KNOWNSTRINGBASE + i;
2733 // new unknown engine string
2734 if (developer_insane.integer)
2735 Con_DPrintf("new engine string %p = \"%s\"\n", s, s);
2736 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2737 if (!prog->knownstrings[i])
2739 if (i >= prog->numknownstrings)
2741 if (i >= prog->maxknownstrings)
2743 const char **oldstrings = prog->knownstrings;
2744 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2745 const char **oldstrings_origin = prog->knownstrings_origin;
2746 prog->maxknownstrings += 128;
2747 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2748 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2749 if(prog->leaktest_active)
2750 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2751 if (prog->numknownstrings)
2753 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2754 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2755 if(prog->leaktest_active)
2756 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
2759 prog->numknownstrings++;
2761 prog->firstfreeknownstring = i + 1;
2762 prog->knownstrings[i] = s;
2763 prog->knownstrings_freeable[i] = false;
2764 if(prog->leaktest_active)
2765 prog->knownstrings_origin[i] = NULL;
2766 return PRVM_KNOWNSTRINGBASE + i;
2769 // temp string handling
2771 // all tempstrings go into this buffer consecutively, and it is reset
2772 // whenever PRVM_ExecuteProgram returns to the engine
2773 // (technically each PRVM_ExecuteProgram call saves the cursize value and
2774 // restores it on return, so multiple recursive calls can share the same
2776 // the buffer size is automatically grown as needed
2778 int PRVM_SetTempString(prvm_prog_t *prog, const char *s)
2784 size = (int)strlen(s) + 1;
2785 if (developer_insane.integer)
2786 Con_DPrintf("PRVM_SetTempString: cursize %i, size %i\n", prog->tempstringsbuf.cursize, size);
2787 if (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
2789 sizebuf_t old = prog->tempstringsbuf;
2790 if (prog->tempstringsbuf.cursize + size >= 1<<28)
2791 prog->error_cmd("PRVM_SetTempString: ran out of tempstring memory! (refusing to grow tempstring buffer over 256MB, cursize %i, size %i)\n", prog->tempstringsbuf.cursize, size);
2792 prog->tempstringsbuf.maxsize = max(prog->tempstringsbuf.maxsize, 65536);
2793 while (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
2794 prog->tempstringsbuf.maxsize *= 2;
2795 if (prog->tempstringsbuf.maxsize != old.maxsize || prog->tempstringsbuf.data == NULL)
2797 Con_DPrintf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, prog->tempstringsbuf.maxsize/1024);
2798 prog->tempstringsbuf.data = (unsigned char *) Mem_Alloc(prog->progs_mempool, prog->tempstringsbuf.maxsize);
2800 memcpy(prog->tempstringsbuf.data, old.data, old.cursize);
2805 t = (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.cursize;
2807 prog->tempstringsbuf.cursize += size;
2808 return PRVM_SetEngineString(prog, t);
2811 int PRVM_AllocString(prvm_prog_t *prog, size_t bufferlength, char **pointer)
2816 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2817 if (!prog->knownstrings[i])
2819 if (i >= prog->numknownstrings)
2821 if (i >= prog->maxknownstrings)
2823 const char **oldstrings = prog->knownstrings;
2824 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2825 const char **oldstrings_origin = prog->knownstrings_origin;
2826 prog->maxknownstrings += 128;
2827 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2828 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2829 if(prog->leaktest_active)
2830 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2831 if (prog->numknownstrings)
2833 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2834 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2835 if(prog->leaktest_active)
2836 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
2839 Mem_Free((char **)oldstrings);
2840 if (oldstrings_freeable)
2841 Mem_Free((unsigned char *)oldstrings_freeable);
2842 if (oldstrings_origin)
2843 Mem_Free((char **)oldstrings_origin);
2845 prog->numknownstrings++;
2847 prog->firstfreeknownstring = i + 1;
2848 prog->knownstrings[i] = (char *)PRVM_Alloc(bufferlength);
2849 prog->knownstrings_freeable[i] = true;
2850 if(prog->leaktest_active)
2851 prog->knownstrings_origin[i] = PRVM_AllocationOrigin(prog);
2853 *pointer = (char *)(prog->knownstrings[i]);
2854 return PRVM_KNOWNSTRINGBASE + i;
2857 void PRVM_FreeString(prvm_prog_t *prog, int num)
2860 prog->error_cmd("PRVM_FreeString: attempt to free a NULL string");
2861 else if (num >= 0 && num < prog->stringssize)
2862 prog->error_cmd("PRVM_FreeString: attempt to free a constant string");
2863 else if (num >= PRVM_KNOWNSTRINGBASE && num < PRVM_KNOWNSTRINGBASE + prog->numknownstrings)
2865 num = num - PRVM_KNOWNSTRINGBASE;
2866 if (!prog->knownstrings[num])
2867 prog->error_cmd("PRVM_FreeString: attempt to free a non-existent or already freed string");
2868 if (!prog->knownstrings_freeable[num])
2869 prog->error_cmd("PRVM_FreeString: attempt to free a string owned by the engine");
2870 PRVM_Free((char *)prog->knownstrings[num]);
2871 if(prog->leaktest_active)
2872 if(prog->knownstrings_origin[num])
2873 PRVM_Free((char *)prog->knownstrings_origin[num]);
2874 prog->knownstrings[num] = NULL;
2875 prog->knownstrings_freeable[num] = false;
2876 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
2879 prog->error_cmd("PRVM_FreeString: invalid string offset %i", num);
2882 static qboolean PRVM_IsStringReferenced(prvm_prog_t *prog, string_t string)
2886 for (i = 0;i < prog->numglobaldefs;i++)
2888 ddef_t *d = &prog->globaldefs[i];
2889 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
2891 if(string == PRVM_GLOBALFIELDSTRING(d->ofs))
2895 for(j = 0; j < prog->num_edicts; ++j)
2897 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2898 if (ed->priv.required->free)
2900 for (i=0; i<prog->numfielddefs; ++i)
2902 ddef_t *d = &prog->fielddefs[i];
2903 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
2905 if(string == PRVM_EDICTFIELDSTRING(ed, d->ofs))
2913 static qboolean PRVM_IsEdictRelevant(prvm_prog_t *prog, prvm_edict_t *edict)
2917 if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
2918 return true; // world or clients
2919 if (prog == SVVM_prog)
2921 if(PRVM_serveredictfloat(edict, solid)) // can block other stuff, or is a trigger?
2923 if(PRVM_serveredictfloat(edict, modelindex)) // visible ent?
2925 if(PRVM_serveredictfloat(edict, effects)) // particle effect?
2927 if(PRVM_serveredictfunction(edict, think)) // has a think function?
2928 if(PRVM_serveredictfloat(edict, nextthink) > 0) // that actually will eventually run?
2930 if(PRVM_serveredictfloat(edict, takedamage))
2932 if(*prvm_leaktest_ignore_classnames.string)
2934 if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_serveredictstring(edict, classname)))))
2938 else if (prog == CLVM_prog)
2940 // TODO someone add more stuff here
2941 if(PRVM_clientedictfloat(edict, entnum)) // csqc networked
2943 if(PRVM_clientedictfloat(edict, modelindex)) // visible ent?
2945 if(PRVM_clientedictfloat(edict, effects)) // particle effect?
2947 if(PRVM_clientedictfunction(edict, think)) // has a think function?
2948 if(PRVM_clientedictfloat(edict, nextthink) > 0) // that actually will eventually run?
2950 if(*prvm_leaktest_ignore_classnames.string)
2952 if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_clientedictstring(edict, classname)))))
2958 // menu prog does not have classnames
2963 static qboolean PRVM_IsEdictReferenced(prvm_prog_t *prog, prvm_edict_t *edict, int mark)
2966 int edictnum = PRVM_NUM_FOR_EDICT(edict);
2967 const char *targetname = NULL;
2969 if (prog == SVVM_prog)
2970 targetname = PRVM_GetString(prog, PRVM_serveredictstring(edict, targetname));
2973 if(!*targetname) // ""
2978 for (i = 0;i < prog->numglobaldefs;i++)
2980 ddef_t *d = &prog->globaldefs[i];
2981 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
2983 if(edictnum == PRVM_GLOBALFIELDEDICT(d->ofs))
2988 for(j = 0; j < prog->num_edicts; ++j)
2990 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2991 if (ed->priv.required->mark < mark)
2997 const char *target = PRVM_GetString(prog, PRVM_serveredictstring(ed, target));
2999 if(!strcmp(target, targetname))
3002 for (i=0; i<prog->numfielddefs; ++i)
3004 ddef_t *d = &prog->fielddefs[i];
3005 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3007 if(edictnum == PRVM_EDICTFIELDEDICT(ed, d->ofs))
3015 static void PRVM_MarkReferencedEdicts(prvm_prog_t *prog)
3021 for(j = 0; j < prog->num_edicts; ++j)
3023 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3024 if(ed->priv.required->free)
3026 ed->priv.required->mark = PRVM_IsEdictRelevant(prog, ed) ? 1 : 0;
3033 for(j = 0; j < prog->num_edicts; ++j)
3035 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3036 if(ed->priv.required->free)
3038 if(ed->priv.required->mark)
3040 if(PRVM_IsEdictReferenced(prog, ed, stage))
3042 ed->priv.required->mark = stage + 1;
3049 Con_DPrintf("leak check used %d stages to find all references\n", stage);
3052 void PRVM_LeakTest(prvm_prog_t *prog)
3055 qboolean leaked = false;
3057 if(!prog->leaktest_active)
3061 for (i = 0; i < prog->numknownstrings; ++i)
3063 if(prog->knownstrings[i])
3064 if(prog->knownstrings_freeable[i])
3065 if(prog->knownstrings_origin[i])
3066 if(!PRVM_IsStringReferenced(prog, PRVM_KNOWNSTRINGBASE + i))
3068 Con_Printf("Unreferenced string found!\n Value: %s\n Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
3074 PRVM_MarkReferencedEdicts(prog);
3075 for(j = 0; j < prog->num_edicts; ++j)
3077 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3078 if(ed->priv.required->free)
3080 if(!ed->priv.required->mark)
3081 if(ed->priv.required->allocation_origin)
3083 Con_Printf("Unreferenced edict found!\n Allocated at: %s\n", ed->priv.required->allocation_origin);
3084 PRVM_ED_Print(prog, ed, NULL);
3089 ed->priv.required->mark = 0; // clear marks again when done
3092 for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
3094 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
3096 if(stringbuffer->origin)
3098 Con_Printf("Open string buffer handle found!\n Allocated at: %s\n", stringbuffer->origin);
3103 for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
3105 if(prog->openfiles[i])
3106 if(prog->openfiles_origin[i])
3108 Con_Printf("Open file handle found!\n Allocated at: %s\n", prog->openfiles_origin[i]);
3113 for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
3115 if(prog->opensearches[i])
3116 if(prog->opensearches_origin[i])
3118 Con_Printf("Open search handle found!\n Allocated at: %s\n", prog->opensearches_origin[i]);
3124 Con_Printf("Congratulations. No leaks found.\n");