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 Host_LockSession(); // all progs can use the session cvar
1900 Crypto_LoadKeys(); // all progs might use the keys at init time
1902 dprograms = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
1903 if (dprograms == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
1904 prog->error_cmd("PRVM_LoadProgs: couldn't load %s for %s", filename, prog->name);
1905 // TODO bounds check header fields (e.g. numstatements), they must never go behind end of file
1907 prog->profiletime = Sys_DirtyTime();
1908 prog->starttime = realtime;
1910 Con_DPrintf("%s programs occupy %iK.\n", prog->name, (int)(filesize/1024));
1912 requiredglobalspace = 0;
1913 for (i = 0;i < numrequiredglobals;i++)
1914 requiredglobalspace += required_global[i].type == ev_vector ? 3 : 1;
1916 prog->filecrc = CRC_Block((unsigned char *)dprograms, filesize);
1918 // byte swap the header
1919 prog->progs_version = LittleLong(dprograms->version);
1920 prog->progs_crc = LittleLong(dprograms->crc);
1921 if (prog->progs_version != PROG_VERSION)
1922 prog->error_cmd("%s: %s has wrong version number (%i should be %i)", prog->name, filename, prog->progs_version, PROG_VERSION);
1923 instatements = (dstatement_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_statements));
1924 prog->progs_numstatements = LittleLong(dprograms->numstatements);
1925 inglobaldefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globaldefs));
1926 prog->progs_numglobaldefs = LittleLong(dprograms->numglobaldefs);
1927 infielddefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_fielddefs));
1928 prog->progs_numfielddefs = LittleLong(dprograms->numfielddefs);
1929 infunctions = (dfunction_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_functions));
1930 prog->progs_numfunctions = LittleLong(dprograms->numfunctions);
1931 instrings = (char *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_strings));
1932 prog->progs_numstrings = LittleLong(dprograms->numstrings);
1933 inglobals = (float *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globals));
1934 prog->progs_numglobals = LittleLong(dprograms->numglobals);
1935 prog->progs_entityfields = LittleLong(dprograms->entityfields);
1937 prog->numstatements = prog->progs_numstatements;
1938 prog->numglobaldefs = prog->progs_numglobaldefs;
1939 prog->numfielddefs = prog->progs_numfielddefs;
1940 prog->numfunctions = prog->progs_numfunctions;
1941 prog->numstrings = prog->progs_numstrings;
1942 prog->numglobals = prog->progs_numglobals;
1943 prog->entityfields = prog->progs_entityfields;
1945 if (LittleLong(dprograms->ofs_strings) + prog->progs_numstrings >= (int)filesize)
1946 prog->error_cmd("%s: %s strings go past end of file", prog->name, filename);
1947 prog->strings = (char *)Mem_Alloc(prog->progs_mempool, prog->progs_numstrings);
1948 memcpy(prog->strings, instrings, prog->progs_numstrings);
1949 prog->stringssize = prog->progs_numstrings;
1951 prog->numknownstrings = 0;
1952 prog->maxknownstrings = 0;
1953 prog->knownstrings = NULL;
1954 prog->knownstrings_freeable = NULL;
1956 Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
1958 // we need to expand the globaldefs and fielddefs to include engine defs
1959 prog->globaldefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobaldefs + numrequiredglobals) * sizeof(ddef_t));
1960 prog->globals.generic = (float *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobals + requiredglobalspace) * sizeof(float));
1961 prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numfielddefs + numrequiredfields) * sizeof(ddef_t));
1962 // we need to convert the statements to our memory format
1963 prog->statements = (mstatement_t *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(mstatement_t));
1964 // allocate space for profiling statement usage
1965 prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(*prog->statement_profile));
1966 // functions need to be converted to the memory format
1967 prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs_numfunctions);
1969 for (i = 0;i < prog->progs_numfunctions;i++)
1971 prog->functions[i].first_statement = LittleLong(infunctions[i].first_statement);
1972 prog->functions[i].parm_start = LittleLong(infunctions[i].parm_start);
1973 prog->functions[i].s_name = LittleLong(infunctions[i].s_name);
1974 prog->functions[i].s_file = LittleLong(infunctions[i].s_file);
1975 prog->functions[i].numparms = LittleLong(infunctions[i].numparms);
1976 prog->functions[i].locals = LittleLong(infunctions[i].locals);
1977 memcpy(prog->functions[i].parm_size, infunctions[i].parm_size, sizeof(infunctions[i].parm_size));
1978 if(prog->functions[i].first_statement >= prog->numstatements)
1979 prog->error_cmd("PRVM_LoadProgs: out of bounds function statement (function %d) in %s", i, prog->name);
1980 // TODO bounds check parm_start, s_name, s_file, numparms, locals, parm_size
1983 // copy the globaldefs to the new globaldefs list
1984 for (i=0 ; i<prog->numglobaldefs ; i++)
1986 prog->globaldefs[i].type = LittleShort(inglobaldefs[i].type);
1987 prog->globaldefs[i].ofs = LittleShort(inglobaldefs[i].ofs);
1988 prog->globaldefs[i].s_name = LittleLong(inglobaldefs[i].s_name);
1989 // TODO bounds check ofs, s_name
1992 // append the required globals
1993 for (i = 0;i < numrequiredglobals;i++)
1995 prog->globaldefs[prog->numglobaldefs].type = required_global[i].type;
1996 prog->globaldefs[prog->numglobaldefs].ofs = prog->numglobals;
1997 prog->globaldefs[prog->numglobaldefs].s_name = PRVM_SetEngineString(prog, required_global[i].name);
1998 if (prog->globaldefs[prog->numglobaldefs].type == ev_vector)
1999 prog->numglobals += 3;
2002 prog->numglobaldefs++;
2005 // copy the progs fields to the new fields list
2006 for (i = 0;i < prog->numfielddefs;i++)
2008 prog->fielddefs[i].type = LittleShort(infielddefs[i].type);
2009 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
2010 prog->error_cmd("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", prog->name);
2011 prog->fielddefs[i].ofs = LittleShort(infielddefs[i].ofs);
2012 prog->fielddefs[i].s_name = LittleLong(infielddefs[i].s_name);
2013 // TODO bounds check ofs, s_name
2016 // append the required fields
2017 for (i = 0;i < numrequiredfields;i++)
2019 prog->fielddefs[prog->numfielddefs].type = required_field[i].type;
2020 prog->fielddefs[prog->numfielddefs].ofs = prog->entityfields;
2021 prog->fielddefs[prog->numfielddefs].s_name = PRVM_SetEngineString(prog, required_field[i].name);
2022 if (prog->fielddefs[prog->numfielddefs].type == ev_vector)
2023 prog->entityfields += 3;
2025 prog->entityfields++;
2026 prog->numfielddefs++;
2029 // LordHavoc: TODO: reorder globals to match engine struct
2030 // LordHavoc: TODO: reorder fields to match engine struct
2031 #define remapglobal(index) (index)
2032 #define remapfield(index) (index)
2035 for (i = 0;i < prog->progs_numglobals;i++)
2036 ((int *)prog->globals.generic)[remapglobal(i)] = LittleLong(((int *)inglobals)[i]);
2038 // LordHavoc: TODO: support 32bit progs statement formats
2039 // copy, remap globals in statements, bounds check
2040 for (i = 0;i < prog->progs_numstatements;i++)
2042 op = (opcode_t)LittleShort(instatements[i].op);
2043 a = (unsigned short)LittleShort(instatements[i].a);
2044 b = (unsigned short)LittleShort(instatements[i].b);
2045 c = (unsigned short)LittleShort(instatements[i].c);
2051 if (a >= prog->progs_numglobals || b + i < 0 || b + i >= prog->progs_numstatements)
2052 prog->error_cmd("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", i, prog->name);
2053 prog->statements[i].op = op;
2054 prog->statements[i].operand[0] = remapglobal(a);
2055 prog->statements[i].operand[1] = -1;
2056 prog->statements[i].operand[2] = -1;
2057 prog->statements[i].jumpabsolute = i + b;
2061 if (a + i < 0 || a + i >= prog->progs_numstatements)
2062 prog->error_cmd("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, prog->name);
2063 prog->statements[i].op = op;
2064 prog->statements[i].operand[0] = -1;
2065 prog->statements[i].operand[1] = -1;
2066 prog->statements[i].operand[2] = -1;
2067 prog->statements[i].jumpabsolute = i + a;
2070 Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", (int)op, i, prog->name);
2071 // global global global
2106 if (a >= prog->progs_numglobals || b >= prog->progs_numglobals || c >= prog->progs_numglobals)
2107 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
2108 prog->statements[i].op = op;
2109 prog->statements[i].operand[0] = remapglobal(a);
2110 prog->statements[i].operand[1] = remapglobal(b);
2111 prog->statements[i].operand[2] = remapglobal(c);
2112 prog->statements[i].jumpabsolute = -1;
2114 // global none global
2120 if (a >= prog->progs_numglobals || c >= prog->progs_numglobals)
2121 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2122 prog->statements[i].op = op;
2123 prog->statements[i].operand[0] = remapglobal(a);
2124 prog->statements[i].operand[1] = -1;
2125 prog->statements[i].operand[2] = remapglobal(c);
2126 prog->statements[i].jumpabsolute = -1;
2142 if (a >= prog->progs_numglobals || b >= prog->progs_numglobals)
2143 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2144 prog->statements[i].op = op;
2145 prog->statements[i].operand[0] = remapglobal(a);
2146 prog->statements[i].operand[1] = remapglobal(b);
2147 prog->statements[i].operand[2] = -1;
2148 prog->statements[i].jumpabsolute = -1;
2162 if ( a >= prog->progs_numglobals)
2163 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2164 prog->statements[i].op = op;
2165 prog->statements[i].operand[0] = remapglobal(a);
2166 prog->statements[i].operand[1] = -1;
2167 prog->statements[i].operand[2] = -1;
2168 prog->statements[i].jumpabsolute = -1;
2172 if(prog->numstatements < 1)
2174 prog->error_cmd("PRVM_LoadProgs: empty program in %s", prog->name);
2176 else switch(prog->statements[prog->numstatements - 1].op)
2183 prog->error_cmd("PRVM_LoadProgs: program may fall off the edge (does not end with RETURN, GOTO or DONE) in %s", prog->name);
2187 // we're done with the file now
2188 Mem_Free(dprograms);
2191 // check required functions
2192 for(i=0 ; i < numrequiredfunc ; i++)
2193 if(PRVM_ED_FindFunction(prog, required_func[i]) == 0)
2194 prog->error_cmd("%s: %s not found in %s",prog->name, required_func[i], filename);
2196 PRVM_LoadLNO(prog, filename);
2198 PRVM_Init_Exec(prog);
2200 if(*prvm_language.string)
2201 // in CSQC we really shouldn't be able to change how stuff works... sorry for now
2202 // later idea: include a list of authorized .po file checksums with the csprogs
2204 qboolean deftrans = prog == CLVM_prog;
2205 const char *realfilename = (prog != CLVM_prog ? filename : csqc_progname.string);
2206 if(deftrans) // once we have dotranslate_ strings, ALWAYS use the opt-in method!
2208 for (i=0 ; i<prog->numglobaldefs ; i++)
2211 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2212 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2213 if(name && !strncmp(name, "dotranslate_", 12))
2220 if(!strcmp(prvm_language.string, "dump"))
2222 qfile_t *f = FS_OpenRealFile(va(vabuf, sizeof(vabuf), "%s.pot", realfilename), "w", false);
2223 Con_Printf("Dumping to %s.pot\n", realfilename);
2226 for (i=0 ; i<prog->numglobaldefs ; i++)
2229 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2230 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2231 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2233 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2234 const char *value = PRVM_GetString(prog, val->string);
2237 char buf[MAX_INPUTLINE];
2238 PRVM_PO_UnparseString(buf, value, sizeof(buf));
2239 FS_Printf(f, "msgid \"%s\"\nmsgstr \"\"\n\n", buf);
2248 po_t *po = PRVM_PO_Load(va(vabuf, sizeof(vabuf), "%s.%s.po", realfilename, prvm_language.string), prog->progs_mempool);
2251 for (i=0 ; i<prog->numglobaldefs ; i++)
2254 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2255 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2256 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2258 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2259 const char *value = PRVM_GetString(prog, val->string);
2262 value = PRVM_PO_Lookup(po, value);
2264 val->string = PRVM_SetEngineString(prog, value);
2272 for (i=0 ; i<prog->numglobaldefs ; i++)
2275 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2276 //Con_Printf("found var %s\n", name);
2278 && !strncmp(name, "autocvar_", 9)
2279 && !(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
2282 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2283 cvar_t *cvar = Cvar_FindVar(name + 9);
2284 //Con_Printf("PRVM_LoadProgs: autocvar global %s in %s, processing...\n", name, prog->name);
2289 Con_DPrintf("PRVM_LoadProgs: no cvar for autocvar global %s in %s, creating...\n", name, prog->name);
2290 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2293 if((float)((int)(val->_float)) == val->_float)
2294 dpsnprintf(buf, sizeof(buf), "%i", (int)(val->_float));
2296 dpsnprintf(buf, sizeof(buf), "%.9g", val->_float);
2300 dpsnprintf(buf, sizeof(buf), "%.9g %.9g %.9g", val->vector[0], val->vector[1], val->vector[2]); value = buf;
2303 value = PRVM_GetString(prog, val->string);
2306 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2309 cvar = Cvar_Get(name + 9, value, 0, NULL);
2310 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2312 val->string = PRVM_SetEngineString(prog, cvar->string);
2313 cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2316 prog->error_cmd("PRVM_LoadProgs: could not create cvar for autocvar global %s in %s", name, prog->name);
2317 cvar->globaldefindex_progid[prog - prvm_prog_list] = prog->id;
2318 cvar->globaldefindex[prog - prvm_prog_list] = i;
2320 else if((cvar->flags & CVAR_PRIVATE) == 0)
2322 // MUST BE SYNCED WITH cvar.c Cvar_Set
2325 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2328 val->_float = cvar->value;
2332 VectorClear(val->vector);
2333 for (j = 0;j < 3;j++)
2335 while (*s && ISWHITESPACE(*s))
2339 val->vector[j] = atof(s);
2340 while (!ISWHITESPACE(*s))
2347 val->string = PRVM_SetEngineString(prog, cvar->string);
2348 cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2351 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2354 cvar->globaldefindex_progid[prog - prvm_prog_list] = prog->id;
2355 cvar->globaldefindex[prog - prvm_prog_list] = i;
2358 Con_Printf("PRVM_LoadProgs: private cvar for autocvar global %s in %s\n", name, prog->name);
2364 prog->loaded = TRUE;
2366 // set flags & ddef_ts in prog
2370 PRVM_FindOffsets(prog);
2372 prog->init_cmd(prog);
2375 PRVM_MEM_Alloc(prog);
2379 static void PRVM_Fields_f (void)
2382 int i, j, ednum, used, usedamount;
2384 char tempstring[MAX_INPUTLINE], tempstring2[260];
2394 Con_Print("no progs loaded\n");
2401 Con_Print("prvm_fields <program name>\n");
2405 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2408 counts = (int *)Mem_Alloc(tempmempool, prog->numfielddefs * sizeof(int));
2409 for (ednum = 0;ednum < prog->max_edicts;ednum++)
2411 ed = PRVM_EDICT_NUM(ednum);
2412 if (ed->priv.required->free)
2414 for (i = 1;i < prog->numfielddefs;i++)
2416 d = &prog->fielddefs[i];
2417 name = PRVM_GetString(prog, d->s_name);
2418 if (name[strlen(name)-2] == '_')
2419 continue; // skip _x, _y, _z vars
2420 v = (int *)(ed->fields.vp + d->ofs);
2421 // if the value is still all 0, skip the field
2422 for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
2435 for (i = 0;i < prog->numfielddefs;i++)
2437 d = &prog->fielddefs[i];
2438 name = PRVM_GetString(prog, d->s_name);
2439 if (name[strlen(name)-2] == '_')
2440 continue; // skip _x, _y, _z vars
2441 switch(d->type & ~DEF_SAVEGLOBAL)
2444 strlcat(tempstring, "string ", sizeof(tempstring));
2447 strlcat(tempstring, "entity ", sizeof(tempstring));
2450 strlcat(tempstring, "function ", sizeof(tempstring));
2453 strlcat(tempstring, "field ", sizeof(tempstring));
2456 strlcat(tempstring, "void ", sizeof(tempstring));
2459 strlcat(tempstring, "float ", sizeof(tempstring));
2462 strlcat(tempstring, "vector ", sizeof(tempstring));
2465 strlcat(tempstring, "pointer ", sizeof(tempstring));
2468 dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
2469 strlcat(tempstring, tempstring2, sizeof(tempstring));
2472 if (strlen(name) > sizeof(tempstring2)-4)
2474 memcpy (tempstring2, name, sizeof(tempstring2)-4);
2475 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
2476 tempstring2[sizeof(tempstring2)-1] = 0;
2479 strlcat(tempstring, name, sizeof(tempstring));
2480 for (j = (int)strlen(name);j < 25;j++)
2481 strlcat(tempstring, " ", sizeof(tempstring));
2482 dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
2483 strlcat(tempstring, tempstring2, sizeof(tempstring));
2484 strlcat(tempstring, "\n", sizeof(tempstring));
2485 if (strlen(tempstring) >= sizeof(tempstring)/2)
2487 Con_Print(tempstring);
2493 usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
2497 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);
2500 static void PRVM_Globals_f (void)
2504 const char *wildcard;
2510 Con_Print("no progs loaded\n");
2513 if(Cmd_Argc () < 2 || Cmd_Argc() > 3)
2515 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
2519 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2522 if( Cmd_Argc() == 3)
2523 wildcard = Cmd_Argv(2);
2527 Con_Printf("%s :", prog->name);
2529 for (i = 0;i < prog->numglobaldefs;i++)
2532 if( !matchpattern( PRVM_GetString(prog, prog->globaldefs[i].s_name), wildcard, 1) )
2537 Con_Printf("%s\n", PRVM_GetString(prog, prog->globaldefs[i].s_name));
2539 Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->numglobals, numculled, prog->numglobals * 4);
2547 static void PRVM_Global_f(void)
2551 char valuebuf[MAX_INPUTLINE];
2552 if( Cmd_Argc() != 3 ) {
2553 Con_Printf( "prvm_global <program name> <global name>\n" );
2557 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2560 global = PRVM_ED_FindGlobal( prog, Cmd_Argv(2) );
2562 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2564 Con_Printf( "%s: %s\n", Cmd_Argv(2), PRVM_ValueString( prog, (etype_t)global->type, PRVM_GLOBALFIELDVALUE(global->ofs), valuebuf, sizeof(valuebuf) ) );
2572 static void PRVM_GlobalSet_f(void)
2576 if( Cmd_Argc() != 4 ) {
2577 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2581 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2584 global = PRVM_ED_FindGlobal( prog, Cmd_Argv(2) );
2586 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2588 PRVM_ED_ParseEpair( prog, NULL, global, Cmd_Argv(3), true );
2596 void PRVM_Init (void)
2598 Cmd_AddCommand ("prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2599 Cmd_AddCommand ("prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2600 Cmd_AddCommand ("prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2601 Cmd_AddCommand ("prvm_profile", PRVM_Profile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu)");
2602 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");
2603 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)");
2604 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)");
2605 Cmd_AddCommand ("prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2606 Cmd_AddCommand ("prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2607 Cmd_AddCommand ("prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2608 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)");
2609 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");
2610 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");
2611 Cmd_AddCommand ("prvm_printfunction", PRVM_PrintFunction_f, "prints a disassembly (QuakeC instructions) of the specified function in the selected VM (server, client, menu)");
2612 Cmd_AddCommand ("cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2613 Cmd_AddCommand ("menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2614 Cmd_AddCommand ("sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2616 Cvar_RegisterVariable (&prvm_language);
2617 Cvar_RegisterVariable (&prvm_traceqc);
2618 Cvar_RegisterVariable (&prvm_statementprofiling);
2619 Cvar_RegisterVariable (&prvm_timeprofiling);
2620 Cvar_RegisterVariable (&prvm_backtraceforwarnings);
2621 Cvar_RegisterVariable (&prvm_leaktest);
2622 Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames);
2623 Cvar_RegisterVariable (&prvm_errordump);
2624 Cvar_RegisterVariable (&prvm_reuseedicts_startuptime);
2625 Cvar_RegisterVariable (&prvm_reuseedicts_neverinsameframe);
2627 // COMMANDLINEOPTION: PRVM: -norunaway disables the runaway loop check (it might be impossible to exit DarkPlaces if used!)
2628 prvm_runawaycheck = !COM_CheckParm("-norunaway");
2638 void PRVM_Prog_Init(prvm_prog_t *prog)
2641 PRVM_Prog_Reset(prog);
2643 memset(prog, 0, sizeof(prvm_prog_t));
2644 prog->leaktest_active = prvm_leaktest.integer != 0;
2647 // LordHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
2648 unsigned int PRVM_EDICT_NUM_ERROR(prvm_prog_t *prog, unsigned int n, const char *filename, int fileline)
2650 prog->error_cmd("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", prog->name, n, filename, fileline);
2654 #define PRVM_KNOWNSTRINGBASE 0x40000000
2656 const char *PRVM_GetString(prvm_prog_t *prog, int num)
2661 VM_Warning(prog, "PRVM_GetString: Invalid string offset (%i < 0)\n", num);
2664 else if (num < prog->stringssize)
2666 // constant string from progs.dat
2667 return prog->strings + num;
2669 else if (num <= prog->stringssize + prog->tempstringsbuf.maxsize)
2671 // tempstring returned by engine to QC (becomes invalid after returning to engine)
2672 num -= prog->stringssize;
2673 if (num < prog->tempstringsbuf.cursize)
2674 return (char *)prog->tempstringsbuf.data + num;
2677 VM_Warning(prog, "PRVM_GetString: Invalid temp-string offset (%i >= %i prog->tempstringsbuf.cursize)\n", num, prog->tempstringsbuf.cursize);
2681 else if (num & PRVM_KNOWNSTRINGBASE)
2684 num = num - PRVM_KNOWNSTRINGBASE;
2685 if (num >= 0 && num < prog->numknownstrings)
2687 if (!prog->knownstrings[num])
2689 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
2692 return prog->knownstrings[num];
2696 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
2702 // invalid string offset
2703 VM_Warning(prog, "PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
2708 const char *PRVM_ChangeEngineString(prvm_prog_t *prog, int i, const char *s)
2711 i = i - PRVM_KNOWNSTRINGBASE;
2712 if(i < 0 || i >= prog->numknownstrings)
2713 prog->error_cmd("PRVM_ChangeEngineString: s is not an engine string");
2714 old = prog->knownstrings[i];
2715 prog->knownstrings[i] = s;
2719 int PRVM_SetEngineString(prvm_prog_t *prog, const char *s)
2724 if (s >= prog->strings && s <= prog->strings + prog->stringssize)
2725 prog->error_cmd("PRVM_SetEngineString: s in prog->strings area");
2726 // if it's in the tempstrings area, use a reserved range
2727 // (otherwise we'd get millions of useless string offsets cluttering the database)
2728 if (s >= (char *)prog->tempstringsbuf.data && s < (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.maxsize)
2730 return prog->stringssize + (s - (char *)prog->tempstringsbuf.data);
2732 // see if it's a known string address
2733 for (i = 0;i < prog->numknownstrings;i++)
2734 if (prog->knownstrings[i] == s)
2735 return PRVM_KNOWNSTRINGBASE + i;
2736 // new unknown engine string
2737 if (developer_insane.integer)
2738 Con_DPrintf("new engine string %p = \"%s\"\n", s, s);
2739 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2740 if (!prog->knownstrings[i])
2742 if (i >= prog->numknownstrings)
2744 if (i >= prog->maxknownstrings)
2746 const char **oldstrings = prog->knownstrings;
2747 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2748 const char **oldstrings_origin = prog->knownstrings_origin;
2749 prog->maxknownstrings += 128;
2750 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2751 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2752 if(prog->leaktest_active)
2753 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2754 if (prog->numknownstrings)
2756 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2757 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2758 if(prog->leaktest_active)
2759 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
2762 prog->numknownstrings++;
2764 prog->firstfreeknownstring = i + 1;
2765 prog->knownstrings[i] = s;
2766 prog->knownstrings_freeable[i] = false;
2767 if(prog->leaktest_active)
2768 prog->knownstrings_origin[i] = NULL;
2769 return PRVM_KNOWNSTRINGBASE + i;
2772 // temp string handling
2774 // all tempstrings go into this buffer consecutively, and it is reset
2775 // whenever PRVM_ExecuteProgram returns to the engine
2776 // (technically each PRVM_ExecuteProgram call saves the cursize value and
2777 // restores it on return, so multiple recursive calls can share the same
2779 // the buffer size is automatically grown as needed
2781 int PRVM_SetTempString(prvm_prog_t *prog, const char *s)
2787 size = (int)strlen(s) + 1;
2788 if (developer_insane.integer)
2789 Con_DPrintf("PRVM_SetTempString: cursize %i, size %i\n", prog->tempstringsbuf.cursize, size);
2790 if (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
2792 sizebuf_t old = prog->tempstringsbuf;
2793 if (prog->tempstringsbuf.cursize + size >= 1<<28)
2794 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);
2795 prog->tempstringsbuf.maxsize = max(prog->tempstringsbuf.maxsize, 65536);
2796 while (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
2797 prog->tempstringsbuf.maxsize *= 2;
2798 if (prog->tempstringsbuf.maxsize != old.maxsize || prog->tempstringsbuf.data == NULL)
2800 Con_DPrintf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, prog->tempstringsbuf.maxsize/1024);
2801 prog->tempstringsbuf.data = (unsigned char *) Mem_Alloc(prog->progs_mempool, prog->tempstringsbuf.maxsize);
2803 memcpy(prog->tempstringsbuf.data, old.data, old.cursize);
2808 t = (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.cursize;
2810 prog->tempstringsbuf.cursize += size;
2811 return PRVM_SetEngineString(prog, t);
2814 int PRVM_AllocString(prvm_prog_t *prog, size_t bufferlength, char **pointer)
2819 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2820 if (!prog->knownstrings[i])
2822 if (i >= prog->numknownstrings)
2824 if (i >= prog->maxknownstrings)
2826 const char **oldstrings = prog->knownstrings;
2827 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2828 const char **oldstrings_origin = prog->knownstrings_origin;
2829 prog->maxknownstrings += 128;
2830 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2831 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2832 if(prog->leaktest_active)
2833 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2834 if (prog->numknownstrings)
2836 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2837 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2838 if(prog->leaktest_active)
2839 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
2842 Mem_Free((char **)oldstrings);
2843 if (oldstrings_freeable)
2844 Mem_Free((unsigned char *)oldstrings_freeable);
2845 if (oldstrings_origin)
2846 Mem_Free((char **)oldstrings_origin);
2848 prog->numknownstrings++;
2850 prog->firstfreeknownstring = i + 1;
2851 prog->knownstrings[i] = (char *)PRVM_Alloc(bufferlength);
2852 prog->knownstrings_freeable[i] = true;
2853 if(prog->leaktest_active)
2854 prog->knownstrings_origin[i] = PRVM_AllocationOrigin(prog);
2856 *pointer = (char *)(prog->knownstrings[i]);
2857 return PRVM_KNOWNSTRINGBASE + i;
2860 void PRVM_FreeString(prvm_prog_t *prog, int num)
2863 prog->error_cmd("PRVM_FreeString: attempt to free a NULL string");
2864 else if (num >= 0 && num < prog->stringssize)
2865 prog->error_cmd("PRVM_FreeString: attempt to free a constant string");
2866 else if (num >= PRVM_KNOWNSTRINGBASE && num < PRVM_KNOWNSTRINGBASE + prog->numknownstrings)
2868 num = num - PRVM_KNOWNSTRINGBASE;
2869 if (!prog->knownstrings[num])
2870 prog->error_cmd("PRVM_FreeString: attempt to free a non-existent or already freed string");
2871 if (!prog->knownstrings_freeable[num])
2872 prog->error_cmd("PRVM_FreeString: attempt to free a string owned by the engine");
2873 PRVM_Free((char *)prog->knownstrings[num]);
2874 if(prog->leaktest_active)
2875 if(prog->knownstrings_origin[num])
2876 PRVM_Free((char *)prog->knownstrings_origin[num]);
2877 prog->knownstrings[num] = NULL;
2878 prog->knownstrings_freeable[num] = false;
2879 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
2882 prog->error_cmd("PRVM_FreeString: invalid string offset %i", num);
2885 static qboolean PRVM_IsStringReferenced(prvm_prog_t *prog, string_t string)
2889 for (i = 0;i < prog->numglobaldefs;i++)
2891 ddef_t *d = &prog->globaldefs[i];
2892 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
2894 if(string == PRVM_GLOBALFIELDSTRING(d->ofs))
2898 for(j = 0; j < prog->num_edicts; ++j)
2900 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2901 if (ed->priv.required->free)
2903 for (i=0; i<prog->numfielddefs; ++i)
2905 ddef_t *d = &prog->fielddefs[i];
2906 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
2908 if(string == PRVM_EDICTFIELDSTRING(ed, d->ofs))
2916 static qboolean PRVM_IsEdictRelevant(prvm_prog_t *prog, prvm_edict_t *edict)
2920 if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
2921 return true; // world or clients
2922 if (prog == SVVM_prog)
2924 if(PRVM_serveredictfloat(edict, solid)) // can block other stuff, or is a trigger?
2926 if(PRVM_serveredictfloat(edict, modelindex)) // visible ent?
2928 if(PRVM_serveredictfloat(edict, effects)) // particle effect?
2930 if(PRVM_serveredictfunction(edict, think)) // has a think function?
2931 if(PRVM_serveredictfloat(edict, nextthink) > 0) // that actually will eventually run?
2933 if(PRVM_serveredictfloat(edict, takedamage))
2935 if(*prvm_leaktest_ignore_classnames.string)
2937 if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_serveredictstring(edict, classname)))))
2941 else if (prog == CLVM_prog)
2943 // TODO someone add more stuff here
2944 if(PRVM_clientedictfloat(edict, entnum)) // csqc networked
2946 if(PRVM_clientedictfloat(edict, modelindex)) // visible ent?
2948 if(PRVM_clientedictfloat(edict, effects)) // particle effect?
2950 if(PRVM_clientedictfunction(edict, think)) // has a think function?
2951 if(PRVM_clientedictfloat(edict, nextthink) > 0) // that actually will eventually run?
2953 if(*prvm_leaktest_ignore_classnames.string)
2955 if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_clientedictstring(edict, classname)))))
2961 // menu prog does not have classnames
2966 static qboolean PRVM_IsEdictReferenced(prvm_prog_t *prog, prvm_edict_t *edict, int mark)
2969 int edictnum = PRVM_NUM_FOR_EDICT(edict);
2970 const char *targetname = NULL;
2972 if (prog == SVVM_prog)
2973 targetname = PRVM_GetString(prog, PRVM_serveredictstring(edict, targetname));
2976 if(!*targetname) // ""
2981 for (i = 0;i < prog->numglobaldefs;i++)
2983 ddef_t *d = &prog->globaldefs[i];
2984 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
2986 if(edictnum == PRVM_GLOBALFIELDEDICT(d->ofs))
2991 for(j = 0; j < prog->num_edicts; ++j)
2993 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2994 if (ed->priv.required->mark < mark)
3000 const char *target = PRVM_GetString(prog, PRVM_serveredictstring(ed, target));
3002 if(!strcmp(target, targetname))
3005 for (i=0; i<prog->numfielddefs; ++i)
3007 ddef_t *d = &prog->fielddefs[i];
3008 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3010 if(edictnum == PRVM_EDICTFIELDEDICT(ed, d->ofs))
3018 static void PRVM_MarkReferencedEdicts(prvm_prog_t *prog)
3024 for(j = 0; j < prog->num_edicts; ++j)
3026 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3027 if(ed->priv.required->free)
3029 ed->priv.required->mark = PRVM_IsEdictRelevant(prog, ed) ? 1 : 0;
3036 for(j = 0; j < prog->num_edicts; ++j)
3038 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3039 if(ed->priv.required->free)
3041 if(ed->priv.required->mark)
3043 if(PRVM_IsEdictReferenced(prog, ed, stage))
3045 ed->priv.required->mark = stage + 1;
3052 Con_DPrintf("leak check used %d stages to find all references\n", stage);
3055 void PRVM_LeakTest(prvm_prog_t *prog)
3058 qboolean leaked = false;
3060 if(!prog->leaktest_active)
3064 for (i = 0; i < prog->numknownstrings; ++i)
3066 if(prog->knownstrings[i])
3067 if(prog->knownstrings_freeable[i])
3068 if(prog->knownstrings_origin[i])
3069 if(!PRVM_IsStringReferenced(prog, PRVM_KNOWNSTRINGBASE + i))
3071 Con_Printf("Unreferenced string found!\n Value: %s\n Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
3077 PRVM_MarkReferencedEdicts(prog);
3078 for(j = 0; j < prog->num_edicts; ++j)
3080 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3081 if(ed->priv.required->free)
3083 if(!ed->priv.required->mark)
3084 if(ed->priv.required->allocation_origin)
3086 Con_Printf("Unreferenced edict found!\n Allocated at: %s\n", ed->priv.required->allocation_origin);
3087 PRVM_ED_Print(prog, ed, NULL);
3092 ed->priv.required->mark = 0; // clear marks again when done
3095 for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
3097 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
3099 if(stringbuffer->origin)
3101 Con_Printf("Open string buffer handle found!\n Allocated at: %s\n", stringbuffer->origin);
3106 for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
3108 if(prog->openfiles[i])
3109 if(prog->openfiles_origin[i])
3111 Con_Printf("Open file handle found!\n Allocated at: %s\n", prog->openfiles_origin[i]);
3116 for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
3118 if(prog->opensearches[i])
3119 if(prog->opensearches_origin[i])
3121 Con_Printf("Open search handle found!\n Allocated at: %s\n", prog->opensearches_origin[i]);
3127 Con_Printf("Congratulations. No leaks found.\n");