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.
27 static prvm_prog_t prog_list[PRVM_MAXPROGS];
29 int prvm_type_size[8] = {1,sizeof(string_t)/4,1,3,1,1,sizeof(func_t)/4,sizeof(void *)/4};
31 ddef_t *PRVM_ED_FieldAtOfs(int ofs);
32 qboolean PRVM_ED_ParseEpair(prvm_edict_t *ent, ddef_t *key, const char *s, qboolean parsebackslash);
34 // LordHavoc: optional runtime bounds checking (speed drain, but worth it for security, on by default - breaks most QCCX features (used by CRMod and others))
35 #ifdef PRVM_BOUNDSCHECK_CVAR
36 cvar_t prvm_boundscheck = {0, "prvm_boundscheck", "1", "enables detection of out of bounds memory access in the QuakeC code being run (in other words, prevents really exceedingly bad QuakeC code from doing nasty things to your computer)"};
38 // LordHavoc: prints every opcode as it executes - warning: this is significant spew
39 cvar_t prvm_traceqc = {0, "prvm_traceqc", "0", "prints every QuakeC statement as it is executed (only for really thorough debugging!)"};
40 // LordHavoc: counts usage of each QuakeC statement
41 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)"};
42 cvar_t prvm_backtraceforwarnings = {0, "prvm_backtraceforwarnings", "0", "print a backtrace for warnings too"};
43 cvar_t prvm_leaktest = {0, "prvm_leaktest", "0", "try to detect memory leaks in strings or entities"};
44 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)"};
45 cvar_t prvm_errordump = {0, "prvm_errordump", "0", "write a savegame on crash to crash-server.dmp"};
47 extern sizebuf_t vm_tempstringsbuf;
49 //============================================================================
57 void PRVM_MEM_Alloc(void)
61 // reserve space for the null entity aka world
62 // check bound of max_edicts
63 prog->max_edicts = bound(1 + prog->reserved_edicts, prog->max_edicts, prog->limit_edicts);
64 prog->num_edicts = bound(1 + prog->reserved_edicts, prog->num_edicts, prog->max_edicts);
66 // edictprivate_size has to be min as big prvm_edict_private_t
67 prog->edictprivate_size = max(prog->edictprivate_size,(int)sizeof(prvm_edict_private_t));
70 prog->edicts = (prvm_edict_t *)Mem_Alloc(prog->progs_mempool,prog->limit_edicts * sizeof(prvm_edict_t));
72 // alloc edict private space
73 prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size);
76 prog->edictsfields = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edict_size);
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 = (void*)((unsigned char *)prog->edictsfields + i * prog->edict_size);
88 PRVM_MEM_IncreaseEdicts
91 void PRVM_MEM_IncreaseEdicts(void)
94 int oldmaxedicts = prog->max_edicts;
95 void *oldedictsfields = prog->edictsfields;
96 void *oldedictprivate = prog->edictprivate;
98 if(prog->max_edicts >= prog->limit_edicts)
101 PRVM_GCALL(begin_increase_edicts)();
104 prog->max_edicts = min(prog->max_edicts + 256, prog->limit_edicts);
106 prog->edictsfields = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edict_size);
107 prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size);
109 memcpy(prog->edictsfields, oldedictsfields, oldmaxedicts * prog->edict_size);
110 memcpy(prog->edictprivate, oldedictprivate, oldmaxedicts * prog->edictprivate_size);
112 //set e and v pointers
113 for(i = 0; i < prog->max_edicts; i++)
115 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
116 prog->edicts[i].fields.vp = (void*)((unsigned char *)prog->edictsfields + i * prog->edict_size);
119 PRVM_GCALL(end_increase_edicts)();
121 Mem_Free(oldedictsfields);
122 Mem_Free(oldedictprivate);
125 //============================================================================
128 int PRVM_ED_FindFieldOffset(const char *field)
131 d = PRVM_ED_FindField(field);
137 int PRVM_ED_FindGlobalOffset(const char *global)
140 d = PRVM_ED_FindGlobal(global);
146 func_t PRVM_ED_FindFunctionOffset(const char *function)
149 f = PRVM_ED_FindFunction(function);
152 return (func_t)(f - prog->functions);
155 qboolean PRVM_ProgLoaded(int prognr)
157 if(prognr < 0 || prognr >= PRVM_MAXPROGS)
160 return (prog_list[prognr].loaded ? TRUE : FALSE);
165 PRVM_SetProgFromString
168 // perhaps add a return value when the str doesnt exist
169 qboolean PRVM_SetProgFromString(const char *str)
172 for(; i < PRVM_MAXPROGS ; i++)
173 if(prog_list[i].name && !strcmp(prog_list[i].name,str))
175 if(prog_list[i].loaded)
177 prog = &prog_list[i];
182 Con_Printf("%s not loaded !\n",PRVM_NAME);
187 Con_Printf("Invalid program name %s !\n", str);
196 void PRVM_SetProg(int prognr)
198 if(0 <= prognr && prognr < PRVM_MAXPROGS)
200 if(prog_list[prognr].loaded)
201 prog = &prog_list[prognr];
203 PRVM_ERROR("%i not loaded !", prognr);
206 PRVM_ERROR("Invalid program number %i", prognr);
213 Sets everything to NULL
216 void PRVM_ED_ClearEdict (prvm_edict_t *e)
218 memset (e->fields.vp, 0, prog->progs->entityfields * 4);
219 e->priv.required->free = false;
221 // AK: Let the init_edict function determine if something needs to be initialized
222 PRVM_GCALL(init_edict)(e);
225 const char *PRVM_AllocationOrigin()
228 if(prog->leaktest_active)
229 if(prog->depth > 0) // actually in QC code and not just parsing the entities block of a map/savegame
231 buf = (char *)PRVM_Alloc(128);
232 PRVM_ShortStackTrace(buf, 128);
241 Either finds a free edict, or allocates a new one.
242 Try to avoid reusing an entity that was recently freed, because it
243 can cause the client to think the entity morphed into something else
244 instead of being removed and recreated, which can cause interpolated
245 angles and bad trails.
248 prvm_edict_t *PRVM_ED_Alloc (void)
253 // the client qc dont need maxclients
254 // thus it doesnt need to use svs.maxclients
255 // AK: changed i=svs.maxclients+1
256 // AK: changed so the edict 0 wont spawn -> used as reserved/world entity
257 // although the menu/client has no world
258 for (i = prog->reserved_edicts + 1;i < prog->num_edicts;i++)
260 e = PRVM_EDICT_NUM(i);
261 // the first couple seconds of server time can involve a lot of
262 // freeing and allocating, so relax the replacement policy
263 if (e->priv.required->free && ( e->priv.required->freetime < 2 || prog->globaloffsets.time < 0 || (PRVM_GLOBALFIELDVALUE(prog->globaloffsets.time)->_float - e->priv.required->freetime) > 0.5 ) )
265 PRVM_ED_ClearEdict (e);
266 e->priv.required->allocation_origin = PRVM_AllocationOrigin();
271 if (i == prog->limit_edicts)
272 PRVM_ERROR ("%s: PRVM_ED_Alloc: no free edicts",PRVM_NAME);
275 if (prog->num_edicts >= prog->max_edicts)
276 PRVM_MEM_IncreaseEdicts();
278 e = PRVM_EDICT_NUM(i);
279 PRVM_ED_ClearEdict (e);
281 e->priv.required->allocation_origin = PRVM_AllocationOrigin();
290 Marks the edict as free
291 FIXME: walk all entities and NULL out references to this entity
294 void PRVM_ED_Free (prvm_edict_t *ed)
296 // dont delete the null entity (world) or reserved edicts
297 if(PRVM_NUM_FOR_EDICT(ed) <= prog->reserved_edicts )
300 PRVM_GCALL(free_edict)(ed);
302 ed->priv.required->free = true;
303 ed->priv.required->freetime = prog->globaloffsets.time >= 0 ? PRVM_GLOBALFIELDVALUE(prog->globaloffsets.time)->_float : 0;
304 if(ed->priv.required->allocation_origin)
306 PRVM_Free((char *)ed->priv.required->allocation_origin);
307 ed->priv.required->allocation_origin = NULL;
311 //===========================================================================
318 ddef_t *PRVM_ED_GlobalAtOfs (int ofs)
323 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
325 def = &prog->globaldefs[i];
337 ddef_t *PRVM_ED_FieldAtOfs (int ofs)
342 for (i=0 ; i<prog->progs->numfielddefs ; i++)
344 def = &prog->fielddefs[i];
356 ddef_t *PRVM_ED_FindField (const char *name)
361 for (i=0 ; i<prog->progs->numfielddefs ; i++)
363 def = &prog->fielddefs[i];
364 if (!strcmp(PRVM_GetString(def->s_name), name))
375 ddef_t *PRVM_ED_FindGlobal (const char *name)
380 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
382 def = &prog->globaldefs[i];
383 if (!strcmp(PRVM_GetString(def->s_name), name))
395 mfunction_t *PRVM_ED_FindFunction (const char *name)
400 for (i=0 ; i<prog->progs->numfunctions ; i++)
402 func = &prog->functions[i];
403 if (!strcmp(PRVM_GetString(func->s_name), name))
414 Returns a string describing *data in a type specific manner
417 char *PRVM_ValueString (etype_t type, prvm_eval_t *val)
419 static char line[MAX_INPUTLINE];
424 type = (etype_t)((int) type & ~DEF_SAVEGLOBAL);
429 strlcpy (line, PRVM_GetString (val->string), sizeof (line));
433 if (n < 0 || n >= prog->limit_edicts)
434 dpsnprintf (line, sizeof(line), "entity %i (invalid!)", n);
436 dpsnprintf (line, sizeof(line), "entity %i", n);
439 f = prog->functions + val->function;
440 dpsnprintf (line, sizeof(line), "%s()", PRVM_GetString(f->s_name));
443 def = PRVM_ED_FieldAtOfs ( val->_int );
444 dpsnprintf (line, sizeof(line), ".%s", PRVM_GetString(def->s_name));
447 dpsnprintf (line, sizeof(line), "void");
450 // LordHavoc: changed from %5.1f to %10.4f
451 dpsnprintf (line, sizeof(line), "%10.4f", val->_float);
454 // LordHavoc: changed from %5.1f to %10.4f
455 dpsnprintf (line, sizeof(line), "'%10.4f %10.4f %10.4f'", val->vector[0], val->vector[1], val->vector[2]);
458 dpsnprintf (line, sizeof(line), "pointer");
461 dpsnprintf (line, sizeof(line), "bad type %i", (int) type);
472 Returns a string describing *data in a type specific manner
473 Easier to parse than PR_ValueString
476 char *PRVM_UglyValueString (etype_t type, prvm_eval_t *val)
478 static char line[MAX_INPUTLINE];
484 type = (etype_t)((int)type & ~DEF_SAVEGLOBAL);
489 // Parse the string a bit to turn special characters
490 // (like newline, specifically) into escape codes,
491 // this fixes saving games from various mods
492 s = PRVM_GetString (val->string);
493 for (i = 0;i < (int)sizeof(line) - 2 && *s;)
522 dpsnprintf (line, sizeof (line), "%i", PRVM_NUM_FOR_EDICT(PRVM_PROG_TO_EDICT(val->edict)));
525 f = prog->functions + val->function;
526 strlcpy (line, PRVM_GetString (f->s_name), sizeof (line));
529 def = PRVM_ED_FieldAtOfs ( val->_int );
530 dpsnprintf (line, sizeof (line), ".%s", PRVM_GetString(def->s_name));
533 dpsnprintf (line, sizeof (line), "void");
536 dpsnprintf (line, sizeof (line), "%f", val->_float);
539 dpsnprintf (line, sizeof (line), "%f %f %f", val->vector[0], val->vector[1], val->vector[2]);
542 dpsnprintf (line, sizeof (line), "bad type %i", type);
553 Returns a string with a description and the contents of a global,
554 padded to 20 field width
557 char *PRVM_GlobalString (int ofs)
563 static char line[128];
565 val = (void *)&prog->globals.generic[ofs];
566 def = PRVM_ED_GlobalAtOfs(ofs);
568 dpsnprintf (line, sizeof(line), "GLOBAL%i", ofs);
571 s = PRVM_ValueString ((etype_t)def->type, (prvm_eval_t *)val);
572 dpsnprintf (line, sizeof(line), "%s (=%s)", PRVM_GetString(def->s_name), s);
576 //for ( ; i<20 ; i++)
577 // strcat (line," ");
583 char *PRVM_GlobalStringNoContents (int ofs)
587 static char line[128];
589 def = PRVM_ED_GlobalAtOfs(ofs);
591 dpsnprintf (line, sizeof(line), "GLOBAL%i", ofs);
593 dpsnprintf (line, sizeof(line), "%s", PRVM_GetString(def->s_name));
596 //for ( ; i<20 ; i++)
597 // strcat (line," ");
611 // LordHavoc: optimized this to print out much more quickly (tempstring)
612 // LordHavoc: changed to print out every 4096 characters (incase there are a lot of fields to print)
613 void PRVM_ED_Print(prvm_edict_t *ed, const char *wildcard_fieldname)
621 char tempstring[MAX_INPUTLINE], tempstring2[260]; // temporary string buffers
623 if (ed->priv.required->free)
625 Con_Printf("%s: FREE\n",PRVM_NAME);
630 dpsnprintf(tempstring, sizeof(tempstring), "\n%s EDICT %i:\n", PRVM_NAME, PRVM_NUM_FOR_EDICT(ed));
631 for (i=1 ; i<prog->progs->numfielddefs ; i++)
633 d = &prog->fielddefs[i];
634 name = PRVM_GetString(d->s_name);
635 if (name[strlen(name)-2] == '_')
636 continue; // skip _x, _y, _z vars
638 // Check Field Name Wildcard
639 if(wildcard_fieldname)
640 if( !matchpattern(name, wildcard_fieldname, 1) )
641 // Didn't match; skip
644 v = (int *)((char *)ed->fields.vp + d->ofs*4);
646 // if the value is still all 0, skip the field
647 type = d->type & ~DEF_SAVEGLOBAL;
649 for (j=0 ; j<prvm_type_size[type] ; j++)
652 if (j == prvm_type_size[type])
655 if (strlen(name) > sizeof(tempstring2)-4)
657 memcpy (tempstring2, name, sizeof(tempstring2)-4);
658 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
659 tempstring2[sizeof(tempstring2)-1] = 0;
662 strlcat(tempstring, name, sizeof(tempstring));
663 for (l = strlen(name);l < 14;l++)
664 strlcat(tempstring, " ", sizeof(tempstring));
665 strlcat(tempstring, " ", sizeof(tempstring));
667 name = PRVM_ValueString((etype_t)d->type, (prvm_eval_t *)v);
668 if (strlen(name) > sizeof(tempstring2)-4)
670 memcpy (tempstring2, name, sizeof(tempstring2)-4);
671 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
672 tempstring2[sizeof(tempstring2)-1] = 0;
675 strlcat(tempstring, name, sizeof(tempstring));
676 strlcat(tempstring, "\n", sizeof(tempstring));
677 if (strlen(tempstring) >= sizeof(tempstring)/2)
679 Con_Print(tempstring);
684 Con_Print(tempstring);
694 extern cvar_t developer_entityparsing;
695 void PRVM_ED_Write (qfile_t *f, prvm_edict_t *ed)
705 if (ed->priv.required->free)
711 for (i=1 ; i<prog->progs->numfielddefs ; i++)
713 d = &prog->fielddefs[i];
714 name = PRVM_GetString(d->s_name);
716 if(developer_entityparsing.integer)
717 Con_Printf("PRVM_ED_Write: at entity %d field %s\n", PRVM_NUM_FOR_EDICT(ed), name);
719 if (name[strlen(name)-2] == '_')
720 continue; // skip _x, _y, _z vars
722 v = (int *)((char *)ed->fields.vp + d->ofs*4);
724 // if the value is still all 0, skip the field
725 type = d->type & ~DEF_SAVEGLOBAL;
726 for (j=0 ; j<prvm_type_size[type] ; j++)
729 if (j == prvm_type_size[type])
732 FS_Printf(f,"\"%s\" ",name);
733 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString((etype_t)d->type, (prvm_eval_t *)v));
739 void PRVM_ED_PrintNum (int ent, const char *wildcard_fieldname)
741 PRVM_ED_Print(PRVM_EDICT_NUM(ent), wildcard_fieldname);
746 PRVM_ED_PrintEdicts_f
748 For debugging, prints all the entities in the current server
751 void PRVM_ED_PrintEdicts_f (void)
754 const char *wildcard_fieldname;
756 if(Cmd_Argc() < 2 || Cmd_Argc() > 3)
758 Con_Print("prvm_edicts <program name> <optional field name wildcard>\n");
763 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
767 wildcard_fieldname = Cmd_Argv(2);
769 wildcard_fieldname = NULL;
771 Con_Printf("%s: %i entities\n", PRVM_NAME, prog->num_edicts);
772 for (i=0 ; i<prog->num_edicts ; i++)
773 PRVM_ED_PrintNum (i, wildcard_fieldname);
782 For debugging, prints a single edict
785 void PRVM_ED_PrintEdict_f (void)
788 const char *wildcard_fieldname;
790 if(Cmd_Argc() < 3 || Cmd_Argc() > 4)
792 Con_Print("prvm_edict <program name> <edict number> <optional field name wildcard>\n");
797 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
800 i = atoi (Cmd_Argv(2));
801 if (i >= prog->num_edicts)
803 Con_Print("Bad edict number\n");
808 // Optional Wildcard Provided
809 wildcard_fieldname = Cmd_Argv(3);
812 wildcard_fieldname = NULL;
813 PRVM_ED_PrintNum (i, wildcard_fieldname);
825 // 2 possibilities : 1. just displaying the active edict count
826 // 2. making a function pointer [x]
827 void PRVM_ED_Count_f (void)
835 Con_Print("prvm_count <program name>\n");
840 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
843 if(prog->count_edicts)
844 prog->count_edicts();
848 for (i=0 ; i<prog->num_edicts ; i++)
850 ent = PRVM_EDICT_NUM(i);
851 if (ent->priv.required->free)
856 Con_Printf("num_edicts:%3i\n", prog->num_edicts);
857 Con_Printf("active :%3i\n", active);
864 ==============================================================================
868 FIXME: need to tag constants, doesn't really work
869 ==============================================================================
877 void PRVM_ED_WriteGlobals (qfile_t *f)
885 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
887 def = &prog->globaldefs[i];
889 if ( !(def->type & DEF_SAVEGLOBAL) )
891 type &= ~DEF_SAVEGLOBAL;
893 if (type != ev_string && type != ev_float && type != ev_entity)
896 name = PRVM_GetString(def->s_name);
898 if(developer_entityparsing.integer)
899 Con_Printf("PRVM_ED_WriteGlobals: at global %s\n", name);
901 FS_Printf(f,"\"%s\" ", name);
902 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString((etype_t)type, (prvm_eval_t *)&prog->globals.generic[def->ofs]));
912 void PRVM_ED_ParseGlobals (const char *data)
914 char keyname[MAX_INPUTLINE];
920 if (!COM_ParseToken_Simple(&data, false, false))
921 PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace");
922 if (com_token[0] == '}')
925 if (developer_entityparsing.integer)
926 Con_Printf("Key: \"%s\"", com_token);
928 strlcpy (keyname, com_token, sizeof(keyname));
931 if (!COM_ParseToken_Simple(&data, false, true))
932 PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace");
934 if (developer_entityparsing.integer)
935 Con_Printf(" \"%s\"\n", com_token);
937 if (com_token[0] == '}')
938 PRVM_ERROR ("PRVM_ED_ParseGlobals: closing brace without data");
940 key = PRVM_ED_FindGlobal (keyname);
943 Con_DPrintf("'%s' is not a global on %s\n", keyname, PRVM_NAME);
947 if (!PRVM_ED_ParseEpair(NULL, key, com_token, true))
948 PRVM_ERROR ("PRVM_ED_ParseGlobals: parse error");
952 //============================================================================
959 Can parse either fields or globals
960 returns false if error
963 qboolean PRVM_ED_ParseEpair(prvm_edict_t *ent, ddef_t *key, const char *s, qboolean parsebackslash)
972 val = (prvm_eval_t *)((int *)ent->fields.vp + key->ofs);
974 val = (prvm_eval_t *)((int *)prog->globals.generic + key->ofs);
975 switch (key->type & ~DEF_SAVEGLOBAL)
978 l = (int)strlen(s) + 1;
979 val->string = PRVM_AllocString(l, &new_p);
980 for (i = 0;i < l;i++)
982 if (s[i] == '\\' && s[i+1] && parsebackslash)
987 else if (s[i] == 'r')
998 while (*s && *s <= ' ')
1000 val->_float = atof(s);
1004 for (i = 0;i < 3;i++)
1006 while (*s && *s <= ' ')
1010 val->vector[i] = atof(s);
1019 while (*s && *s <= ' ')
1022 if (i >= prog->limit_edicts)
1023 Con_Printf("PRVM_ED_ParseEpair: ev_entity reference too large (edict %u >= MAX_EDICTS %u) on %s\n", (unsigned int)i, (unsigned int)MAX_EDICTS, PRVM_NAME);
1024 while (i >= prog->max_edicts)
1025 PRVM_MEM_IncreaseEdicts();
1026 // if IncreaseEdicts was called the base pointer needs to be updated
1028 val = (prvm_eval_t *)((int *)ent->fields.vp + key->ofs);
1029 val->edict = PRVM_EDICT_TO_PROG(PRVM_EDICT_NUM((int)i));
1035 Con_DPrintf("PRVM_ED_ParseEpair: Bogus field name %s in %s\n", s, PRVM_NAME);
1038 def = PRVM_ED_FindField(s + 1);
1041 Con_DPrintf("PRVM_ED_ParseEpair: Can't find field %s in %s\n", s, PRVM_NAME);
1044 val->_int = def->ofs;
1048 func = PRVM_ED_FindFunction(s);
1051 Con_Printf("PRVM_ED_ParseEpair: Can't find function %s in %s\n", s, PRVM_NAME);
1054 val->function = func - prog->functions;
1058 Con_Printf("PRVM_ED_ParseEpair: Unknown key->type %i for key \"%s\" on %s\n", key->type, PRVM_GetString(key->s_name), PRVM_NAME);
1068 Console command to send a string to QC function GameCommand of the
1072 sv_cmd adminmsg 3 "do not teamkill"
1073 cl_cmd someclientcommand
1074 menu_cmd somemenucommand
1076 All progs can support this extension; sg calls it in server QC, cg in client
1080 void PRVM_GameCommand(const char *whichprogs, const char *whichcmd)
1084 Con_Printf("%s text...\n", whichcmd);
1089 if(!PRVM_SetProgFromString(whichprogs))
1090 // note: this is not PRVM_SetProg because that one aborts "hard" using PRVM_Error
1091 // also, it makes printing error messages easier!
1093 Con_Printf("%s program not loaded.\n", whichprogs);
1097 if(!prog->funcoffsets.GameCommand)
1099 Con_Printf("%s program do not support GameCommand!\n", whichprogs);
1103 int restorevm_tempstringsbuf_cursize;
1108 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
1109 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(s ? s : "");
1110 PRVM_ExecuteProgram (prog->funcoffsets.GameCommand, "QC function GameCommand is missing");
1111 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1116 void PRVM_GameCommand_Server_f(void)
1118 PRVM_GameCommand("server", "sv_cmd");
1120 void PRVM_GameCommand_Client_f(void)
1122 PRVM_GameCommand("client", "cl_cmd");
1124 void PRVM_GameCommand_Menu_f(void)
1126 PRVM_GameCommand("menu", "menu_cmd");
1133 Console command to set a field of a specified edict
1136 void PRVM_ED_EdictSet_f(void)
1143 Con_Print("prvm_edictset <program name> <edict number> <field> <value>\n");
1148 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1150 Con_Printf("Wrong program name %s !\n", Cmd_Argv(1));
1154 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1156 if((key = PRVM_ED_FindField(Cmd_Argv(3))) == 0)
1157 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1159 PRVM_ED_ParseEpair(ed, key, Cmd_Argv(4), true);
1165 ====================
1168 Parses an edict out of the given string, returning the new position
1169 ed should be a properly initialized empty edict.
1170 Used for initial level load and for savegames.
1171 ====================
1173 const char *PRVM_ED_ParseEdict (const char *data, prvm_edict_t *ent)
1183 // go through all the dictionary pairs
1187 if (!COM_ParseToken_Simple(&data, false, false))
1188 PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1189 if (developer_entityparsing.integer)
1190 Con_Printf("Key: \"%s\"", com_token);
1191 if (com_token[0] == '}')
1194 // anglehack is to allow QuakeEd to write single scalar angles
1195 // and allow them to be turned into vectors. (FIXME...)
1196 if (!strcmp(com_token, "angle"))
1198 strlcpy (com_token, "angles", sizeof(com_token));
1204 // FIXME: change light to _light to get rid of this hack
1205 if (!strcmp(com_token, "light"))
1206 strlcpy (com_token, "light_lev", sizeof(com_token)); // hack for single light def
1208 strlcpy (keyname, com_token, sizeof(keyname));
1210 // another hack to fix keynames with trailing spaces
1211 n = strlen(keyname);
1212 while (n && keyname[n-1] == ' ')
1219 if (!COM_ParseToken_Simple(&data, false, false))
1220 PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1221 if (developer_entityparsing.integer)
1222 Con_Printf(" \"%s\"\n", com_token);
1224 if (com_token[0] == '}')
1225 PRVM_ERROR ("PRVM_ED_ParseEdict: closing brace without data");
1229 // ignore attempts to set key "" (this problem occurs in nehahra neh1m8.bsp)
1233 // keynames with a leading underscore are used for utility comments,
1234 // and are immediately discarded by quake
1235 if (keyname[0] == '_')
1238 key = PRVM_ED_FindField (keyname);
1241 Con_DPrintf("%s: '%s' is not a field\n", PRVM_NAME, keyname);
1248 strlcpy (temp, com_token, sizeof(temp));
1249 dpsnprintf (com_token, sizeof(com_token), "0 %s 0", temp);
1252 if (!PRVM_ED_ParseEpair(ent, key, com_token, strcmp(keyname, "wad") != 0))
1253 PRVM_ERROR ("PRVM_ED_ParseEdict: parse error");
1257 ent->priv.required->free = true;
1265 PRVM_ED_LoadFromFile
1267 The entities are directly placed in the array, rather than allocated with
1268 PRVM_ED_Alloc, because otherwise an error loading the map would have entity
1269 number references out of order.
1271 Creates a server's entity / program execution context by
1272 parsing textual entity definitions out of an ent file.
1274 Used for both fresh maps and savegame loads. A fresh map would also need
1275 to call PRVM_ED_CallSpawnFunctions () to let the objects initialize themselves.
1278 void PRVM_ED_LoadFromFile (const char *data)
1281 int parsed, inhibited, spawned, died;
1282 const char *funcname;
1294 // parse the opening brace
1295 if (!COM_ParseToken_Simple(&data, false, false))
1297 if (com_token[0] != '{')
1298 PRVM_ERROR ("PRVM_ED_LoadFromFile: %s: found %s when expecting {", PRVM_NAME, com_token);
1300 // CHANGED: this is not conform to PR_LoadFromFile
1301 if(prog->loadintoworld)
1303 prog->loadintoworld = false;
1304 ent = PRVM_EDICT_NUM(0);
1307 ent = PRVM_ED_Alloc();
1310 if (ent != prog->edicts) // hack
1311 memset (ent->fields.vp, 0, prog->progs->entityfields * 4);
1313 data = PRVM_ED_ParseEdict (data, ent);
1316 // remove the entity ?
1317 if(prog->load_edict && !prog->load_edict(ent))
1325 // immediately call spawn function, but only if there is a self global and a classname
1327 if(prog->globaloffsets.self >= 0 && prog->fieldoffsets.classname >= 0)
1329 string_t handle = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.classname)->string;
1332 Con_Print("No classname for:\n");
1333 PRVM_ED_Print(ent, NULL);
1338 // look for the spawn function
1339 funcname = PRVM_GetString(handle);
1340 func = PRVM_ED_FindFunction (va("spawnfunc_%s", funcname));
1342 if(prog->globaloffsets.require_spawnfunc_prefix < 0)
1343 func = PRVM_ED_FindFunction (funcname);
1347 // check for OnEntityNoSpawnFunction
1348 if (prog->funcoffsets.SV_OnEntityNoSpawnFunction)
1351 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1352 PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityNoSpawnFunction, "QC function SV_OnEntityNoSpawnFunction is missing");
1356 if (developer.integer) // don't confuse non-developers with errors
1358 Con_Print("No spawn function for:\n");
1359 PRVM_ED_Print(ent, NULL);
1368 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1369 PRVM_ExecuteProgram (func - prog->functions, "");
1374 if (ent->priv.required->free)
1378 Con_DPrintf("%s: %i new entities parsed, %i new inhibited, %i (%i new) spawned (whereas %i removed self, %i stayed)\n", PRVM_NAME, parsed, inhibited, prog->num_edicts, spawned, died, spawned - died);
1381 void PRVM_FindOffsets(void)
1383 // field and global searches use -1 for NULL
1384 memset(&prog->fieldoffsets, -1, sizeof(prog->fieldoffsets));
1385 memset(&prog->globaloffsets, -1, sizeof(prog->globaloffsets));
1386 // functions use 0 for NULL
1387 memset(&prog->funcoffsets, 0, sizeof(prog->funcoffsets));
1389 // server and client qc use a lot of similar fields, so this is combined
1390 prog->fieldoffsets.SendEntity = PRVM_ED_FindFieldOffset("SendEntity");
1391 prog->fieldoffsets.SendFlags = PRVM_ED_FindFieldOffset("SendFlags");
1392 prog->fieldoffsets.Version = PRVM_ED_FindFieldOffset("Version");
1393 prog->fieldoffsets.alpha = PRVM_ED_FindFieldOffset("alpha");
1394 prog->fieldoffsets.ammo_cells1 = PRVM_ED_FindFieldOffset("ammo_cells1");
1395 prog->fieldoffsets.ammo_lava_nails = PRVM_ED_FindFieldOffset("ammo_lava_nails");
1396 prog->fieldoffsets.ammo_multi_rockets = PRVM_ED_FindFieldOffset("ammo_multi_rockets");
1397 prog->fieldoffsets.ammo_nails1 = PRVM_ED_FindFieldOffset("ammo_nails1");
1398 prog->fieldoffsets.ammo_plasma = PRVM_ED_FindFieldOffset("ammo_plasma");
1399 prog->fieldoffsets.ammo_rockets1 = PRVM_ED_FindFieldOffset("ammo_rockets1");
1400 prog->fieldoffsets.ammo_shells1 = PRVM_ED_FindFieldOffset("ammo_shells1");
1401 prog->fieldoffsets.angles = PRVM_ED_FindFieldOffset("angles");
1402 prog->fieldoffsets.button3 = PRVM_ED_FindFieldOffset("button3");
1403 prog->fieldoffsets.button4 = PRVM_ED_FindFieldOffset("button4");
1404 prog->fieldoffsets.button5 = PRVM_ED_FindFieldOffset("button5");
1405 prog->fieldoffsets.button6 = PRVM_ED_FindFieldOffset("button6");
1406 prog->fieldoffsets.button7 = PRVM_ED_FindFieldOffset("button7");
1407 prog->fieldoffsets.button8 = PRVM_ED_FindFieldOffset("button8");
1408 prog->fieldoffsets.button9 = PRVM_ED_FindFieldOffset("button9");
1409 prog->fieldoffsets.button10 = PRVM_ED_FindFieldOffset("button10");
1410 prog->fieldoffsets.button11 = PRVM_ED_FindFieldOffset("button11");
1411 prog->fieldoffsets.button12 = PRVM_ED_FindFieldOffset("button12");
1412 prog->fieldoffsets.button13 = PRVM_ED_FindFieldOffset("button13");
1413 prog->fieldoffsets.button14 = PRVM_ED_FindFieldOffset("button14");
1414 prog->fieldoffsets.button15 = PRVM_ED_FindFieldOffset("button15");
1415 prog->fieldoffsets.button16 = PRVM_ED_FindFieldOffset("button16");
1416 prog->fieldoffsets.buttonchat = PRVM_ED_FindFieldOffset("buttonchat");
1417 prog->fieldoffsets.buttonuse = PRVM_ED_FindFieldOffset("buttonuse");
1418 prog->fieldoffsets.chain = PRVM_ED_FindFieldOffset("chain");
1419 prog->fieldoffsets.classname = PRVM_ED_FindFieldOffset("classname");
1420 prog->fieldoffsets.clientcolors = PRVM_ED_FindFieldOffset("clientcolors");
1421 prog->fieldoffsets.color = PRVM_ED_FindFieldOffset("color");
1422 prog->fieldoffsets.colormod = PRVM_ED_FindFieldOffset("colormod");
1423 prog->fieldoffsets.contentstransition = PRVM_ED_FindFieldOffset("contentstransition");
1424 prog->fieldoffsets.cursor_active = PRVM_ED_FindFieldOffset("cursor_active");
1425 prog->fieldoffsets.cursor_screen = PRVM_ED_FindFieldOffset("cursor_screen");
1426 prog->fieldoffsets.cursor_trace_endpos = PRVM_ED_FindFieldOffset("cursor_trace_endpos");
1427 prog->fieldoffsets.cursor_trace_ent = PRVM_ED_FindFieldOffset("cursor_trace_ent");
1428 prog->fieldoffsets.cursor_trace_start = PRVM_ED_FindFieldOffset("cursor_trace_start");
1429 prog->fieldoffsets.customizeentityforclient = PRVM_ED_FindFieldOffset("customizeentityforclient");
1430 prog->fieldoffsets.dimension_hit = PRVM_ED_FindFieldOffset("dimension_hit");
1431 prog->fieldoffsets.dimension_solid = PRVM_ED_FindFieldOffset("dimension_solid");
1432 prog->fieldoffsets.disableclientprediction = PRVM_ED_FindFieldOffset("disableclientprediction");
1433 prog->fieldoffsets.dphitcontentsmask = PRVM_ED_FindFieldOffset("dphitcontentsmask");
1434 prog->fieldoffsets.drawonlytoclient = PRVM_ED_FindFieldOffset("drawonlytoclient");
1435 prog->fieldoffsets.exteriormodeltoclient = PRVM_ED_FindFieldOffset("exteriormodeltoclient");
1436 prog->fieldoffsets.fatness = PRVM_ED_FindFieldOffset("fatness");
1437 prog->fieldoffsets.forceshader = PRVM_ED_FindFieldOffset("forceshader");
1438 prog->fieldoffsets.frame = PRVM_ED_FindFieldOffset("frame");
1439 prog->fieldoffsets.frame1time = PRVM_ED_FindFieldOffset("frame1time");
1440 prog->fieldoffsets.frame2 = PRVM_ED_FindFieldOffset("frame2");
1441 prog->fieldoffsets.frame2time = PRVM_ED_FindFieldOffset("frame2time");
1442 prog->fieldoffsets.fullbright = PRVM_ED_FindFieldOffset("fullbright");
1443 prog->fieldoffsets.glow_color = PRVM_ED_FindFieldOffset("glow_color");
1444 prog->fieldoffsets.glow_size = PRVM_ED_FindFieldOffset("glow_size");
1445 prog->fieldoffsets.glow_trail = PRVM_ED_FindFieldOffset("glow_trail");
1446 prog->fieldoffsets.gravity = PRVM_ED_FindFieldOffset("gravity");
1447 prog->fieldoffsets.groundentity = PRVM_ED_FindFieldOffset("groundentity");
1448 prog->fieldoffsets.hull = PRVM_ED_FindFieldOffset("hull");
1449 prog->fieldoffsets.ideal_yaw = PRVM_ED_FindFieldOffset("ideal_yaw");
1450 prog->fieldoffsets.idealpitch = PRVM_ED_FindFieldOffset("idealpitch");
1451 prog->fieldoffsets.items2 = PRVM_ED_FindFieldOffset("items2");
1452 prog->fieldoffsets.lerpfrac = PRVM_ED_FindFieldOffset("lerpfrac");
1453 prog->fieldoffsets.light_lev = PRVM_ED_FindFieldOffset("light_lev");
1454 prog->fieldoffsets.message = PRVM_ED_FindFieldOffset("message");
1455 prog->fieldoffsets.modelflags = PRVM_ED_FindFieldOffset("modelflags");
1456 prog->fieldoffsets.movement = PRVM_ED_FindFieldOffset("movement");
1457 prog->fieldoffsets.movetypesteplandevent = PRVM_ED_FindFieldOffset("movetypesteplandevent");
1458 prog->fieldoffsets.netaddress = PRVM_ED_FindFieldOffset("netaddress");
1459 prog->fieldoffsets.nextthink = PRVM_ED_FindFieldOffset("nextthink");
1460 prog->fieldoffsets.nodrawtoclient = PRVM_ED_FindFieldOffset("nodrawtoclient");
1461 prog->fieldoffsets.pflags = PRVM_ED_FindFieldOffset("pflags");
1462 prog->fieldoffsets.ping = PRVM_ED_FindFieldOffset("ping");
1463 prog->fieldoffsets.pitch_speed = PRVM_ED_FindFieldOffset("pitch_speed");
1464 prog->fieldoffsets.playermodel = PRVM_ED_FindFieldOffset("playermodel");
1465 prog->fieldoffsets.playerskin = PRVM_ED_FindFieldOffset("playerskin");
1466 prog->fieldoffsets.pmodel = PRVM_ED_FindFieldOffset("pmodel");
1467 prog->fieldoffsets.punchvector = PRVM_ED_FindFieldOffset("punchvector");
1468 prog->fieldoffsets.renderamt = PRVM_ED_FindFieldOffset("renderamt"); // HalfLife support
1469 prog->fieldoffsets.renderflags = PRVM_ED_FindFieldOffset("renderflags");
1470 prog->fieldoffsets.rendermode = PRVM_ED_FindFieldOffset("rendermode"); // HalfLife support
1471 prog->fieldoffsets.scale = PRVM_ED_FindFieldOffset("scale");
1472 prog->fieldoffsets.style = PRVM_ED_FindFieldOffset("style");
1473 prog->fieldoffsets.tag_entity = PRVM_ED_FindFieldOffset("tag_entity");
1474 prog->fieldoffsets.tag_index = PRVM_ED_FindFieldOffset("tag_index");
1475 prog->fieldoffsets.think = PRVM_ED_FindFieldOffset("think");
1476 prog->fieldoffsets.viewmodelforclient = PRVM_ED_FindFieldOffset("viewmodelforclient");
1477 prog->fieldoffsets.viewzoom = PRVM_ED_FindFieldOffset("viewzoom");
1478 prog->fieldoffsets.yaw_speed = PRVM_ED_FindFieldOffset("yaw_speed");
1479 prog->fieldoffsets.clientcamera = PRVM_ED_FindFieldOffset("clientcamera");
1480 prog->fieldoffsets.clientstatus = PRVM_ED_FindFieldOffset("clientstatus");
1481 prog->funcoffsets.CSQC_ConsoleCommand = PRVM_ED_FindFunctionOffset("CSQC_ConsoleCommand");
1482 prog->funcoffsets.CSQC_Ent_Remove = PRVM_ED_FindFunctionOffset("CSQC_Ent_Remove");
1483 prog->funcoffsets.CSQC_Ent_Update = PRVM_ED_FindFunctionOffset("CSQC_Ent_Update");
1484 prog->funcoffsets.CSQC_Ent_Spawn = PRVM_ED_FindFunctionOffset("CSQC_Ent_Spawn");
1485 prog->funcoffsets.CSQC_Event = PRVM_ED_FindFunctionOffset("CSQC_Event");
1486 prog->funcoffsets.CSQC_Event_Sound = PRVM_ED_FindFunctionOffset("CSQC_Event_Sound");
1487 prog->funcoffsets.CSQC_Init = PRVM_ED_FindFunctionOffset("CSQC_Init");
1488 prog->funcoffsets.CSQC_InputEvent = PRVM_ED_FindFunctionOffset("CSQC_InputEvent");
1489 prog->funcoffsets.CSQC_Parse_CenterPrint = PRVM_ED_FindFunctionOffset("CSQC_Parse_CenterPrint");
1490 prog->funcoffsets.CSQC_Parse_Print = PRVM_ED_FindFunctionOffset("CSQC_Parse_Print");
1491 prog->funcoffsets.CSQC_Parse_StuffCmd = PRVM_ED_FindFunctionOffset("CSQC_Parse_StuffCmd");
1492 prog->funcoffsets.CSQC_Parse_TempEntity = PRVM_ED_FindFunctionOffset("CSQC_Parse_TempEntity");
1493 prog->funcoffsets.CSQC_Shutdown = PRVM_ED_FindFunctionOffset("CSQC_Shutdown");
1494 prog->funcoffsets.CSQC_UpdateView = PRVM_ED_FindFunctionOffset("CSQC_UpdateView");
1495 prog->funcoffsets.Gecko_Query = PRVM_ED_FindFunctionOffset("Gecko_Query");
1496 prog->funcoffsets.EndFrame = PRVM_ED_FindFunctionOffset("EndFrame");
1497 prog->funcoffsets.RestoreGame = PRVM_ED_FindFunctionOffset("RestoreGame");
1498 prog->funcoffsets.SV_ChangeTeam = PRVM_ED_FindFunctionOffset("SV_ChangeTeam");
1499 prog->funcoffsets.SV_ParseClientCommand = PRVM_ED_FindFunctionOffset("SV_ParseClientCommand");
1500 prog->funcoffsets.SV_PlayerPhysics = PRVM_ED_FindFunctionOffset("SV_PlayerPhysics");
1501 prog->funcoffsets.SV_OnEntityNoSpawnFunction = PRVM_ED_FindFunctionOffset("SV_OnEntityNoSpawnFunction");
1502 prog->funcoffsets.GameCommand = PRVM_ED_FindFunctionOffset("GameCommand");
1503 prog->funcoffsets.SV_Shutdown = PRVM_ED_FindFunctionOffset("SV_Shutdown");
1504 prog->funcoffsets.URI_Get_Callback = PRVM_ED_FindFunctionOffset("URI_Get_Callback");
1505 prog->globaloffsets.SV_InitCmd = PRVM_ED_FindGlobalOffset("SV_InitCmd");
1506 prog->globaloffsets.self = PRVM_ED_FindGlobalOffset("self");
1507 prog->globaloffsets.time = PRVM_ED_FindGlobalOffset("time");
1508 prog->globaloffsets.v_forward = PRVM_ED_FindGlobalOffset("v_forward");
1509 prog->globaloffsets.v_right = PRVM_ED_FindGlobalOffset("v_right");
1510 prog->globaloffsets.v_up = PRVM_ED_FindGlobalOffset("v_up");
1511 prog->globaloffsets.view_angles = PRVM_ED_FindGlobalOffset("view_angles");
1512 prog->globaloffsets.trace_allsolid = PRVM_ED_FindGlobalOffset("trace_allsolid");
1513 prog->globaloffsets.trace_startsolid = PRVM_ED_FindGlobalOffset("trace_startsolid");
1514 prog->globaloffsets.trace_fraction = PRVM_ED_FindGlobalOffset("trace_fraction");
1515 prog->globaloffsets.trace_inwater = PRVM_ED_FindGlobalOffset("trace_inwater");
1516 prog->globaloffsets.trace_inopen = PRVM_ED_FindGlobalOffset("trace_inopen");
1517 prog->globaloffsets.trace_endpos = PRVM_ED_FindGlobalOffset("trace_endpos");
1518 prog->globaloffsets.trace_plane_normal = PRVM_ED_FindGlobalOffset("trace_plane_normal");
1519 prog->globaloffsets.trace_plane_dist = PRVM_ED_FindGlobalOffset("trace_plane_dist");
1520 prog->globaloffsets.trace_ent = PRVM_ED_FindGlobalOffset("trace_ent");
1521 prog->globaloffsets.trace_dphitcontents = PRVM_ED_FindGlobalOffset("trace_dphitcontents");
1522 prog->globaloffsets.trace_dphitq3surfaceflags = PRVM_ED_FindGlobalOffset("trace_dphitq3surfaceflags");
1523 prog->globaloffsets.trace_dphittexturename = PRVM_ED_FindGlobalOffset("trace_dphittexturename");
1524 prog->globaloffsets.trace_dpstartcontents = PRVM_ED_FindGlobalOffset("trace_dpstartcontents");
1525 prog->globaloffsets.intermission = PRVM_ED_FindGlobalOffset("intermission");
1526 prog->globaloffsets.coop = PRVM_ED_FindGlobalOffset("coop");
1527 prog->globaloffsets.deathmatch = PRVM_ED_FindGlobalOffset("deathmatch");
1528 prog->globaloffsets.dmg_take = PRVM_ED_FindGlobalOffset("dmg_take");
1529 prog->globaloffsets.dmg_save = PRVM_ED_FindGlobalOffset("dmg_save");
1530 prog->globaloffsets.dmg_origin = PRVM_ED_FindGlobalOffset("dmg_origin");
1531 prog->globaloffsets.sb_showscores = PRVM_ED_FindGlobalOffset("sb_showscores");
1532 prog->globaloffsets.drawfont = PRVM_ED_FindGlobalOffset("drawfont");
1533 prog->globaloffsets.require_spawnfunc_prefix = PRVM_ED_FindGlobalOffset("require_spawnfunc_prefix");
1534 prog->globaloffsets.worldstatus = PRVM_ED_FindGlobalOffset("worldstatus");
1536 // menu qc only uses some functions, nothing else
1537 prog->funcoffsets.m_draw = PRVM_ED_FindFunctionOffset("m_draw");
1538 prog->funcoffsets.m_init = PRVM_ED_FindFunctionOffset("m_init");
1539 prog->funcoffsets.m_keydown = PRVM_ED_FindFunctionOffset("m_keydown");
1540 prog->funcoffsets.m_keyup = PRVM_ED_FindFunctionOffset("m_keyup");
1541 prog->funcoffsets.m_shutdown = PRVM_ED_FindFunctionOffset("m_shutdown");
1542 prog->funcoffsets.m_toggle = PRVM_ED_FindFunctionOffset("m_toggle");
1547 typedef struct dpfield_s
1554 #define DPFIELDS (sizeof(dpfields) / sizeof(dpfield_t))
1556 dpfield_t dpfields[] =
1567 void PRVM_LeakTest();
1568 void PRVM_ResetProg()
1571 PRVM_GCALL(reset_cmd)();
1572 Mem_FreePool(&prog->progs_mempool);
1573 memset(prog,0,sizeof(prvm_prog_t));
1574 prog->starttime = Sys_DoubleTime();
1582 void PRVM_LoadLNO( const char *progname ) {
1583 fs_offset_t filesize;
1585 unsigned int *header;
1588 FS_StripExtension( progname, filename, sizeof( filename ) );
1589 strlcat( filename, ".lno", sizeof( filename ) );
1591 lno = FS_LoadFile( filename, tempmempool, false, &filesize );
1597 <Spike> SafeWrite (h, &lnotype, sizeof(int));
1598 <Spike> SafeWrite (h, &version, sizeof(int));
1599 <Spike> SafeWrite (h, &numglobaldefs, sizeof(int));
1600 <Spike> SafeWrite (h, &numpr_globals, sizeof(int));
1601 <Spike> SafeWrite (h, &numfielddefs, sizeof(int));
1602 <Spike> SafeWrite (h, &numstatements, sizeof(int));
1603 <Spike> SafeWrite (h, statement_linenums, numstatements*sizeof(int));
1605 if( (unsigned) filesize < (6 + prog->progs->numstatements) * sizeof( int ) ) {
1610 header = (unsigned int *) lno;
1611 if( header[ 0 ] == *(unsigned int *) "LNOF" &&
1612 LittleLong( header[ 1 ] ) == 1 &&
1613 (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs->numglobaldefs &&
1614 (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs->numglobals &&
1615 (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs->numfielddefs &&
1616 (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs->numstatements )
1618 prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs->numstatements * sizeof( int ) );
1619 memcpy( prog->statement_linenums, (int *) lno + 6, prog->progs->numstatements * sizeof( int ) );
1629 void PRVM_LoadProgs (const char * filename, int numrequiredfunc, char **required_func, int numrequiredfields, prvm_required_field_t *required_field, int numrequiredglobals, char **required_global)
1633 ddef_t *infielddefs;
1634 dfunction_t *dfunctions;
1635 fs_offset_t filesize;
1637 if( prog->loaded ) {
1638 PRVM_ERROR ("PRVM_LoadProgs: there is already a %s program loaded!", PRVM_NAME );
1641 prog->progs = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
1642 if (prog->progs == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
1643 PRVM_ERROR ("PRVM_LoadProgs: couldn't load %s for %s", filename, PRVM_NAME);
1645 Con_DPrintf("%s programs occupy %iK.\n", PRVM_NAME, (int)(filesize/1024));
1647 prog->filecrc = CRC_Block((unsigned char *)prog->progs, filesize);
1649 // byte swap the header
1650 for (i = 0;i < (int) sizeof(*prog->progs) / 4;i++)
1651 ((int *)prog->progs)[i] = LittleLong ( ((int *)prog->progs)[i] );
1653 if (prog->progs->version != PROG_VERSION)
1654 PRVM_ERROR ("%s: %s has wrong version number (%i should be %i)", PRVM_NAME, filename, prog->progs->version, PROG_VERSION);
1655 if (prog->progs->crc != prog->headercrc && prog->progs->crc != prog->headercrc2)
1656 PRVM_ERROR ("%s: %s system vars have been modified (CRC of progs.dat systemvars %i != engine %i), progdefs.h is out of date", PRVM_NAME, filename, prog->progs->crc, prog->headercrc);
1658 //prog->functions = (dfunction_t *)((unsigned char *)progs + progs->ofs_functions);
1659 dfunctions = (dfunction_t *)((unsigned char *)prog->progs + prog->progs->ofs_functions);
1661 if (prog->progs->ofs_strings + prog->progs->numstrings >= (int)filesize)
1662 PRVM_ERROR ("%s: %s strings go past end of file", PRVM_NAME, filename);
1663 prog->strings = (char *)prog->progs + prog->progs->ofs_strings;
1664 prog->stringssize = prog->progs->numstrings;
1666 prog->numknownstrings = 0;
1667 prog->maxknownstrings = 0;
1668 prog->knownstrings = NULL;
1669 prog->knownstrings_freeable = NULL;
1671 Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
1673 prog->globaldefs = (ddef_t *)((unsigned char *)prog->progs + prog->progs->ofs_globaldefs);
1675 // we need to expand the fielddefs list to include all the engine fields,
1676 // so allocate a new place for it
1677 infielddefs = (ddef_t *)((unsigned char *)prog->progs + prog->progs->ofs_fielddefs);
1679 prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs->numfielddefs + numrequiredfields) * sizeof(ddef_t));
1681 prog->statements = (dstatement_t *)((unsigned char *)prog->progs + prog->progs->ofs_statements);
1683 prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs->numstatements * sizeof(*prog->statement_profile));
1685 // moved edict_size calculation down below field adding code
1687 //pr_global_struct = (globalvars_t *)((unsigned char *)progs + progs->ofs_globals);
1688 prog->globals.generic = (float *)((unsigned char *)prog->progs + prog->progs->ofs_globals);
1690 // byte swap the lumps
1691 for (i=0 ; i<prog->progs->numstatements ; i++)
1693 prog->statements[i].op = LittleShort(prog->statements[i].op);
1694 prog->statements[i].a = LittleShort(prog->statements[i].a);
1695 prog->statements[i].b = LittleShort(prog->statements[i].b);
1696 prog->statements[i].c = LittleShort(prog->statements[i].c);
1699 prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs->numfunctions);
1700 for (i = 0;i < prog->progs->numfunctions;i++)
1702 prog->functions[i].first_statement = LittleLong (dfunctions[i].first_statement);
1703 prog->functions[i].parm_start = LittleLong (dfunctions[i].parm_start);
1704 prog->functions[i].s_name = LittleLong (dfunctions[i].s_name);
1705 prog->functions[i].s_file = LittleLong (dfunctions[i].s_file);
1706 prog->functions[i].numparms = LittleLong (dfunctions[i].numparms);
1707 prog->functions[i].locals = LittleLong (dfunctions[i].locals);
1708 memcpy(prog->functions[i].parm_size, dfunctions[i].parm_size, sizeof(dfunctions[i].parm_size));
1711 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
1713 prog->globaldefs[i].type = LittleShort (prog->globaldefs[i].type);
1714 prog->globaldefs[i].ofs = LittleShort (prog->globaldefs[i].ofs);
1715 prog->globaldefs[i].s_name = LittleLong (prog->globaldefs[i].s_name);
1718 // copy the progs fields to the new fields list
1719 for (i = 0;i < prog->progs->numfielddefs;i++)
1721 prog->fielddefs[i].type = LittleShort (infielddefs[i].type);
1722 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
1723 PRVM_ERROR ("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", PRVM_NAME);
1724 prog->fielddefs[i].ofs = LittleShort (infielddefs[i].ofs);
1725 prog->fielddefs[i].s_name = LittleLong (infielddefs[i].s_name);
1728 // append the required fields
1729 for (i = 0;i < (int) numrequiredfields;i++)
1731 prog->fielddefs[prog->progs->numfielddefs].type = required_field[i].type;
1732 prog->fielddefs[prog->progs->numfielddefs].ofs = prog->progs->entityfields;
1733 prog->fielddefs[prog->progs->numfielddefs].s_name = PRVM_SetEngineString(required_field[i].name);
1734 if (prog->fielddefs[prog->progs->numfielddefs].type == ev_vector)
1735 prog->progs->entityfields += 3;
1737 prog->progs->entityfields++;
1738 prog->progs->numfielddefs++;
1741 // check required functions
1742 for(i=0 ; i < numrequiredfunc ; i++)
1743 if(PRVM_ED_FindFunction(required_func[i]) == 0)
1744 PRVM_ERROR("%s: %s not found in %s",PRVM_NAME, required_func[i], filename);
1746 // check required globals
1747 for(i=0 ; i < numrequiredglobals ; i++)
1748 if(PRVM_ED_FindGlobal(required_global[i]) == 0)
1749 PRVM_ERROR("%s: %s not found in %s",PRVM_NAME, required_global[i], filename);
1751 for (i=0 ; i<prog->progs->numglobals ; i++)
1752 ((int *)prog->globals.generic)[i] = LittleLong (((int *)prog->globals.generic)[i]);
1754 // moved edict_size calculation down here, below field adding code
1755 // LordHavoc: this no longer includes the prvm_edict_t header
1756 prog->edict_size = prog->progs->entityfields * 4;
1757 prog->edictareasize = prog->edict_size * prog->limit_edicts;
1759 // LordHavoc: bounds check anything static
1760 for (i = 0,st = prog->statements;i < prog->progs->numstatements;i++,st++)
1766 if ((unsigned short) st->a >= prog->progs->numglobals || st->b + i < 0 || st->b + i >= prog->progs->numstatements)
1767 PRVM_ERROR("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", i, PRVM_NAME);
1770 if (st->a + i < 0 || st->a + i >= prog->progs->numstatements)
1771 PRVM_ERROR("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, PRVM_NAME);
1773 // global global global
1808 if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->b >= prog->progs->numglobals || (unsigned short) st->c >= prog->progs->numglobals)
1809 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
1811 // global none global
1817 if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->c >= prog->progs->numglobals)
1818 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
1834 if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->b >= prog->progs->numglobals)
1835 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
1849 if ((unsigned short) st->a >= prog->progs->numglobals)
1850 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
1853 Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", st->op, i, PRVM_NAME);
1858 PRVM_LoadLNO(filename);
1862 prog->loaded = TRUE;
1864 // set flags & ddef_ts in prog
1870 PRVM_GCALL(init_cmd)();
1877 void PRVM_Fields_f (void)
1879 int i, j, ednum, used, usedamount;
1881 char tempstring[MAX_INPUTLINE], tempstring2[260];
1891 Con_Print("no progs loaded\n");
1898 Con_Print("prvm_fields <program name>\n");
1903 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1906 counts = (int *)Mem_Alloc(tempmempool, prog->progs->numfielddefs * sizeof(int));
1907 for (ednum = 0;ednum < prog->max_edicts;ednum++)
1909 ed = PRVM_EDICT_NUM(ednum);
1910 if (ed->priv.required->free)
1912 for (i = 1;i < prog->progs->numfielddefs;i++)
1914 d = &prog->fielddefs[i];
1915 name = PRVM_GetString(d->s_name);
1916 if (name[strlen(name)-2] == '_')
1917 continue; // skip _x, _y, _z vars
1918 v = (int *)((char *)ed->fields.vp + d->ofs*4);
1919 // if the value is still all 0, skip the field
1920 for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
1933 for (i = 0;i < prog->progs->numfielddefs;i++)
1935 d = &prog->fielddefs[i];
1936 name = PRVM_GetString(d->s_name);
1937 if (name[strlen(name)-2] == '_')
1938 continue; // skip _x, _y, _z vars
1939 switch(d->type & ~DEF_SAVEGLOBAL)
1942 strlcat(tempstring, "string ", sizeof(tempstring));
1945 strlcat(tempstring, "entity ", sizeof(tempstring));
1948 strlcat(tempstring, "function ", sizeof(tempstring));
1951 strlcat(tempstring, "field ", sizeof(tempstring));
1954 strlcat(tempstring, "void ", sizeof(tempstring));
1957 strlcat(tempstring, "float ", sizeof(tempstring));
1960 strlcat(tempstring, "vector ", sizeof(tempstring));
1963 strlcat(tempstring, "pointer ", sizeof(tempstring));
1966 dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
1967 strlcat(tempstring, tempstring2, sizeof(tempstring));
1970 if (strlen(name) > sizeof(tempstring2)-4)
1972 memcpy (tempstring2, name, sizeof(tempstring2)-4);
1973 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
1974 tempstring2[sizeof(tempstring2)-1] = 0;
1977 strlcat(tempstring, name, sizeof(tempstring));
1978 for (j = (int)strlen(name);j < 25;j++)
1979 strlcat(tempstring, " ", sizeof(tempstring));
1980 dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
1981 strlcat(tempstring, tempstring2, sizeof(tempstring));
1982 strlcat(tempstring, "\n", sizeof(tempstring));
1983 if (strlen(tempstring) >= sizeof(tempstring)/2)
1985 Con_Print(tempstring);
1991 usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
1995 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", PRVM_NAME, prog->progs->entityfields, used, prog->progs->entityfields * 4, usedamount * 4, prog->max_edicts, prog->progs->entityfields * 4 * prog->max_edicts, usedamount * 4 * prog->max_edicts);
2000 void PRVM_Globals_f (void)
2003 const char *wildcard;
2009 Con_Print("no progs loaded\n");
2012 if(Cmd_Argc () < 2 || Cmd_Argc() > 3)
2014 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
2019 if(!PRVM_SetProgFromString (Cmd_Argv (1)))
2022 if( Cmd_Argc() == 3)
2023 wildcard = Cmd_Argv(2);
2027 Con_Printf("%s :", PRVM_NAME);
2029 for (i = 0;i < prog->progs->numglobaldefs;i++)
2032 if( !matchpattern( PRVM_GetString(prog->globaldefs[i].s_name), wildcard, 1) )
2037 Con_Printf("%s\n", PRVM_GetString(prog->globaldefs[i].s_name));
2039 Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->progs->numglobals, numculled, prog->progs->numglobals * 4);
2049 void PRVM_Global_f(void)
2052 if( Cmd_Argc() != 3 ) {
2053 Con_Printf( "prvm_global <program name> <global name>\n" );
2058 if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
2061 global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
2063 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2065 Con_Printf( "%s: %s\n", Cmd_Argv(2), PRVM_ValueString( (etype_t)global->type, (prvm_eval_t *) &prog->globals.generic[ global->ofs ] ) );
2074 void PRVM_GlobalSet_f(void)
2077 if( Cmd_Argc() != 4 ) {
2078 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2083 if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
2086 global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
2088 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2090 PRVM_ED_ParseEpair( NULL, global, Cmd_Argv(3), true );
2099 void PRVM_Init (void)
2101 Cmd_AddCommand ("prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2102 Cmd_AddCommand ("prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2103 Cmd_AddCommand ("prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2104 Cmd_AddCommand ("prvm_profile", PRVM_Profile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu)");
2105 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)");
2106 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)");
2107 Cmd_AddCommand ("prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2108 Cmd_AddCommand ("prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2109 Cmd_AddCommand ("prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2110 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)");
2111 Cmd_AddCommand ("prvm_printfunction", PRVM_PrintFunction_f, "prints a disassembly (QuakeC instructions) of the specified function in the selected VM (server, client, menu)");
2112 Cmd_AddCommand ("cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2113 Cmd_AddCommand ("menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2114 Cmd_AddCommand ("sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2115 // LordHavoc: optional runtime bounds checking (speed drain, but worth it for security, on by default - breaks most QCCX features (used by CRMod and others))
2116 #ifdef PRVM_BOUNDSCHECK_CVAR
2117 Cvar_RegisterVariable (&prvm_boundscheck);
2119 Cvar_RegisterVariable (&prvm_traceqc);
2120 Cvar_RegisterVariable (&prvm_statementprofiling);
2121 Cvar_RegisterVariable (&prvm_backtraceforwarnings);
2122 Cvar_RegisterVariable (&prvm_leaktest);
2123 Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames);
2124 Cvar_RegisterVariable (&prvm_errordump);
2134 void PRVM_InitProg(int prognr)
2136 if(prognr < 0 || prognr >= PRVM_MAXPROGS)
2137 Sys_Error("PRVM_InitProg: Invalid program number %i",prognr);
2139 prog = &prog_list[prognr];
2144 memset(prog, 0, sizeof(prvm_prog_t));
2145 prog->starttime = Sys_DoubleTime();
2147 prog->error_cmd = Host_Error;
2148 prog->leaktest_active = prvm_leaktest.integer;
2151 int PRVM_GetProgNr()
2153 return prog - prog_list;
2156 void *_PRVM_Alloc(size_t buffersize, const char *filename, int fileline)
2158 return _Mem_Alloc(prog->progs_mempool, buffersize, filename, fileline);
2161 void _PRVM_Free(void *buffer, const char *filename, int fileline)
2163 _Mem_Free(buffer, filename, fileline);
2166 void _PRVM_FreeAll(const char *filename, int fileline)
2169 prog->fielddefs = NULL;
2170 prog->functions = NULL;
2171 _Mem_EmptyPool(prog->progs_mempool, filename, fileline);
2174 // LordHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
2175 unsigned int PRVM_EDICT_NUM_ERROR(unsigned int n, char *filename, int fileline)
2177 PRVM_ERROR ("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", PRVM_NAME, n, filename, fileline);
2182 int NUM_FOR_EDICT_ERROR(prvm_edict_t *e)
2184 PRVM_ERROR ("PRVM_NUM_FOR_EDICT: bad pointer %p (world is %p, entity number would be %i)", e, prog->edicts, e - prog->edicts);
2188 int PRVM_NUM_FOR_EDICT(prvm_edict_t *e)
2191 n = e - prog->edicts;
2192 if ((unsigned int)n >= prog->limit_edicts)
2193 Host_Error ("PRVM_NUM_FOR_EDICT: bad pointer");
2197 //int NoCrash_NUM_FOR_EDICT(prvm_edict_t *e)
2199 // return e - prog->edicts;
2202 //#define PRVM_EDICT_TO_PROG(e) ((unsigned char *)(((prvm_edict_t *)e)->v) - (unsigned char *)(prog->edictsfields))
2203 //#define PRVM_PROG_TO_EDICT(e) (prog->edicts + ((e) / (progs->entityfields * 4)))
2204 int PRVM_EDICT_TO_PROG(prvm_edict_t *e)
2207 n = e - prog->edicts;
2208 if ((unsigned int)n >= (unsigned int)prog->max_edicts)
2209 Host_Error("PRVM_EDICT_TO_PROG: invalid edict %8p (number %i compared to world at %8p)", e, n, prog->edicts);
2210 return n;// EXPERIMENTAL
2211 //return (unsigned char *)e->v - (unsigned char *)prog->edictsfields;
2213 prvm_edict_t *PRVM_PROG_TO_EDICT(int n)
2215 if ((unsigned int)n >= (unsigned int)prog->max_edicts)
2216 Host_Error("PRVM_PROG_TO_EDICT: invalid edict number %i", n);
2217 return prog->edicts + n; // EXPERIMENTAL
2218 //return prog->edicts + ((n) / (progs->entityfields * 4));
2223 sizebuf_t vm_tempstringsbuf;
2225 const char *PRVM_GetString(int num)
2229 if (num < prog->stringssize)
2230 return prog->strings + num;
2233 if (num <= prog->stringssize + vm_tempstringsbuf.maxsize)
2235 num -= prog->stringssize;
2236 if (num < vm_tempstringsbuf.cursize)
2237 return (char *)vm_tempstringsbuf.data + num;
2240 VM_Warning("PRVM_GetString: Invalid temp-string offset (%i >= %i vm_tempstringsbuf.cursize)\n", num, vm_tempstringsbuf.cursize);
2247 VM_Warning("PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
2257 // special range reserved for tempstrings
2259 if (num < vm_tempstringsbuf.cursize)
2260 return (char *)vm_tempstringsbuf.data + num;
2263 VM_Warning("PRVM_GetString: Invalid temp-string offset (%i >= %i vm_tempstringsbuf.cursize)\n", num, vm_tempstringsbuf.cursize);
2269 if (num < prog->numknownstrings)
2271 if (!prog->knownstrings[num])
2272 VM_Warning("PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
2273 return prog->knownstrings[num];
2277 VM_Warning("PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
2283 int PRVM_SetEngineString(const char *s)
2288 if (s >= prog->strings && s <= prog->strings + prog->stringssize)
2289 PRVM_ERROR("PRVM_SetEngineString: s in prog->strings area");
2290 // if it's in the tempstrings area, use a reserved range
2291 // (otherwise we'd get millions of useless string offsets cluttering the database)
2292 if (s >= (char *)vm_tempstringsbuf.data && s < (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.maxsize)
2294 return prog->stringssize + (s - (char *)vm_tempstringsbuf.data);
2296 return -1 - ((1<<30) + (s - (char *)vm_tempstringsbuf.data));
2298 // see if it's a known string address
2299 for (i = 0;i < prog->numknownstrings;i++)
2300 if (prog->knownstrings[i] == s)
2302 // new unknown engine string
2303 if (developer.integer >= 200)
2304 Con_Printf("new engine string %p = \"%s\"\n", s, s);
2305 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2306 if (!prog->knownstrings[i])
2308 if (i >= prog->numknownstrings)
2310 if (i >= prog->maxknownstrings)
2312 const char **oldstrings = prog->knownstrings;
2313 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2314 const char **oldstrings_origin = prog->knownstrings_origin;
2315 prog->maxknownstrings += 128;
2316 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2317 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2318 if(prog->leaktest_active)
2319 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2320 if (prog->numknownstrings)
2322 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2323 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2324 if(prog->leaktest_active)
2325 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
2328 prog->numknownstrings++;
2330 prog->firstfreeknownstring = i + 1;
2331 prog->knownstrings[i] = s;
2332 prog->knownstrings_freeable[i] = false;
2333 if(prog->leaktest_active)
2334 prog->knownstrings_origin[i] = NULL;
2338 // temp string handling
2340 // all tempstrings go into this buffer consecutively, and it is reset
2341 // whenever PRVM_ExecuteProgram returns to the engine
2342 // (technically each PRVM_ExecuteProgram call saves the cursize value and
2343 // restores it on return, so multiple recursive calls can share the same
2345 // the buffer size is automatically grown as needed
2347 int PRVM_SetTempString(const char *s)
2353 size = (int)strlen(s) + 1;
2354 if (developer.integer >= 300)
2355 Con_Printf("PRVM_SetTempString: cursize %i, size %i\n", vm_tempstringsbuf.cursize, size);
2356 if (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
2358 sizebuf_t old = vm_tempstringsbuf;
2359 if (vm_tempstringsbuf.cursize + size >= 1<<28)
2360 PRVM_ERROR("PRVM_SetTempString: ran out of tempstring memory! (refusing to grow tempstring buffer over 256MB, cursize %i, size %i)\n", vm_tempstringsbuf.cursize, size);
2361 vm_tempstringsbuf.maxsize = max(vm_tempstringsbuf.maxsize, 65536);
2362 while (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
2363 vm_tempstringsbuf.maxsize *= 2;
2364 if (vm_tempstringsbuf.maxsize != old.maxsize || vm_tempstringsbuf.data == NULL)
2366 if (developer.integer >= 100)
2367 Con_Printf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, vm_tempstringsbuf.maxsize/1024);
2368 vm_tempstringsbuf.data = Mem_Alloc(sv_mempool, vm_tempstringsbuf.maxsize);
2370 memcpy(vm_tempstringsbuf.data, old.data, old.cursize);
2375 t = (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.cursize;
2377 vm_tempstringsbuf.cursize += size;
2378 return PRVM_SetEngineString(t);
2381 int PRVM_AllocString(size_t bufferlength, char **pointer)
2386 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2387 if (!prog->knownstrings[i])
2389 if (i >= prog->numknownstrings)
2391 if (i >= prog->maxknownstrings)
2393 const char **oldstrings = prog->knownstrings;
2394 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2395 const char **oldstrings_origin = prog->knownstrings_origin;
2396 prog->maxknownstrings += 128;
2397 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2398 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2399 if(prog->leaktest_active)
2400 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2401 if (prog->numknownstrings)
2403 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2404 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2405 if(prog->leaktest_active)
2406 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
2408 // TODO why not Mem_Free the old ones?
2410 prog->numknownstrings++;
2412 prog->firstfreeknownstring = i + 1;
2413 prog->knownstrings[i] = (char *)PRVM_Alloc(bufferlength);
2414 prog->knownstrings_freeable[i] = true;
2415 if(prog->leaktest_active)
2416 prog->knownstrings_origin[i] = PRVM_AllocationOrigin();
2418 *pointer = (char *)(prog->knownstrings[i]);
2422 void PRVM_FreeString(int num)
2425 PRVM_ERROR("PRVM_FreeString: attempt to free a NULL string");
2426 else if (num >= 0 && num < prog->stringssize)
2427 PRVM_ERROR("PRVM_FreeString: attempt to free a constant string");
2428 else if (num < 0 && num >= -prog->numknownstrings)
2431 if (!prog->knownstrings[num])
2432 PRVM_ERROR("PRVM_FreeString: attempt to free a non-existent or already freed string");
2433 if (!prog->knownstrings_freeable[num])
2434 PRVM_ERROR("PRVM_FreeString: attempt to free a string owned by the engine");
2435 PRVM_Free((char *)prog->knownstrings[num]);
2436 if(prog->leaktest_active)
2437 if(prog->knownstrings_origin[num])
2438 PRVM_Free((char *)prog->knownstrings_origin[num]);
2439 prog->knownstrings[num] = NULL;
2440 prog->knownstrings_freeable[num] = false;
2441 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
2444 PRVM_ERROR("PRVM_FreeString: invalid string offset %i", num);
2447 static qboolean PRVM_IsStringReferenced(string_t string)
2451 for (i = 0;i < prog->progs->numglobaldefs;i++)
2453 ddef_t *d = &prog->globaldefs[i];
2454 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
2456 if(string == ((prvm_eval_t *) &prog->globals.generic[d->ofs])->string)
2460 for(j = 0; j < prog->num_edicts; ++j)
2462 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2463 if (ed->priv.required->free)
2465 for (i=0; i<prog->progs->numfielddefs; ++i)
2467 ddef_t *d = &prog->fielddefs[i];
2468 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
2470 if(string == ((prvm_eval_t *) &((float*)ed->fields.vp)[d->ofs])->string)
2478 static qboolean PRVM_IsEdictRelevant(prvm_edict_t *edict)
2480 if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
2481 return true; // world or clients
2482 switch(prog - prog_list)
2484 case PRVM_SERVERPROG:
2486 entvars_t *ev = edict->fields.server;
2487 if(ev->solid) // can block other stuff, or is a trigger?
2489 if(ev->modelindex) // visible ent?
2491 if(ev->effects) // particle effect?
2493 if(ev->think) // has a think function?
2494 if(ev->nextthink > 0) // that actually will eventually run?
2498 if(*prvm_leaktest_ignore_classnames.string)
2500 if(strstr(va(" %s ", prvm_leaktest_ignore_classnames.string), va(" %s ", PRVM_GetString(ev->classname))))
2505 case PRVM_CLIENTPROG:
2507 // TODO someone add more stuff here
2508 cl_entvars_t *ev = edict->fields.client;
2509 if(ev->entnum) // csqc networked
2511 if(ev->modelindex) // visible ent?
2513 if(ev->effects) // particle effect?
2515 if(ev->think) // has a think function?
2516 if(ev->nextthink > 0) // that actually will eventually run?
2518 if(*prvm_leaktest_ignore_classnames.string)
2520 if(strstr(va(" %s ", prvm_leaktest_ignore_classnames.string), va(" %s ", PRVM_GetString(ev->classname))))
2526 // menu prog does not have classnames
2532 static qboolean PRVM_IsEdictReferenced(prvm_edict_t *edict, int mark)
2535 int edictnum = PRVM_NUM_FOR_EDICT(edict);
2536 const char *targetname = NULL;
2538 switch(prog - prog_list)
2540 case PRVM_SERVERPROG:
2541 targetname = PRVM_GetString(edict->fields.server->targetname);
2546 if(!*targetname) // ""
2549 for (i = 0;i < prog->progs->numglobaldefs;i++)
2551 ddef_t *d = &prog->globaldefs[i];
2552 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
2554 if(edictnum == ((prvm_eval_t *) &prog->globals.generic[d->ofs])->edict)
2558 for(j = 0; j < prog->num_edicts; ++j)
2560 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2561 if (ed->priv.required->mark < mark)
2567 const char *target = PRVM_GetString(ed->fields.server->target);
2569 if(!strcmp(target, targetname))
2572 for (i=0; i<prog->progs->numfielddefs; ++i)
2574 ddef_t *d = &prog->fielddefs[i];
2575 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
2577 if(edictnum == ((prvm_eval_t *) &((float*)ed->fields.vp)[d->ofs])->edict)
2585 static void PRVM_MarkReferencedEdicts()
2591 for(j = 0; j < prog->num_edicts; ++j)
2593 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2594 if(ed->priv.required->free)
2596 ed->priv.required->mark = PRVM_IsEdictRelevant(ed) ? 1 : 0;
2603 for(j = 0; j < prog->num_edicts; ++j)
2605 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2606 if(ed->priv.required->free)
2608 if(ed->priv.required->mark)
2610 if(PRVM_IsEdictReferenced(ed, stage))
2612 ed->priv.required->mark = stage + 1;
2619 Con_DPrintf("leak check used %d stages to find all references\n", stage);
2622 void PRVM_LeakTest()
2625 qboolean leaked = false;
2627 if(!prog->leaktest_active)
2631 for (i = 0; i < prog->numknownstrings; ++i)
2633 if(prog->knownstrings[i])
2634 if(prog->knownstrings_freeable[i])
2635 if(prog->knownstrings_origin[i])
2636 if(!PRVM_IsStringReferenced(-1 - i))
2638 Con_Printf("Unreferenced string found!\n Value: %s\n Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
2644 PRVM_MarkReferencedEdicts();
2645 for(j = 0; j < prog->num_edicts; ++j)
2647 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2648 if(ed->priv.required->free)
2650 if(!ed->priv.required->mark)
2651 if(ed->priv.required->allocation_origin)
2653 Con_Printf("Unreferenced edict found!\n Allocated at: %s\n", ed->priv.required->allocation_origin);
2654 PRVM_ED_Print(ed, NULL);
2660 for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
2662 prvm_stringbuffer_t *stringbuffer = Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
2664 if(stringbuffer->origin)
2666 Con_Printf("Open string buffer handle found!\n Allocated at: %s\n", stringbuffer->origin);
2671 for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
2673 if(prog->openfiles[i])
2674 if(prog->openfiles_origin[i])
2676 Con_Printf("Open file handle found!\n Allocated at: %s\n", prog->openfiles_origin[i]);
2681 for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
2683 if(prog->opensearches[i])
2684 if(prog->opensearches_origin[i])
2686 Con_Printf("Open search handle found!\n Allocated at: %s\n", prog->opensearches_origin[i]);
2692 Con_Printf("Congratulations. No leaks found.\n");