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: prints every opcode as it executes - warning: this is significant spew
35 cvar_t prvm_traceqc = {0, "prvm_traceqc", "0", "prints every QuakeC statement as it is executed (only for really thorough debugging!)"};
36 // LordHavoc: counts usage of each QuakeC statement
37 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)"};
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 qboolean prvm_runawaycheck = true;
47 // LordHavoc: optional runtime bounds checking (speed drain, but worth it for security, on by default - breaks most QCCX features (used by CRMod and others))
48 // 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)
49 qboolean prvm_boundscheck = true;
51 extern sizebuf_t vm_tempstringsbuf;
53 //============================================================================
61 void PRVM_MEM_Alloc(void)
65 // reserve space for the null entity aka world
66 // check bound of max_edicts
67 prog->max_edicts = bound(1 + prog->reserved_edicts, prog->max_edicts, prog->limit_edicts);
68 prog->num_edicts = bound(1 + prog->reserved_edicts, prog->num_edicts, prog->max_edicts);
70 // edictprivate_size has to be min as big prvm_edict_private_t
71 prog->edictprivate_size = max(prog->edictprivate_size,(int)sizeof(prvm_edict_private_t));
74 prog->edicts = (prvm_edict_t *)Mem_Alloc(prog->progs_mempool,prog->limit_edicts * sizeof(prvm_edict_t));
76 // alloc edict private space
77 prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size);
80 prog->edictsfields = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edict_size);
83 for(i = 0; i < prog->max_edicts; i++)
85 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
86 prog->edicts[i].fields.vp = (void*)((unsigned char *)prog->edictsfields + i * prog->edict_size);
92 PRVM_MEM_IncreaseEdicts
95 void PRVM_MEM_IncreaseEdicts(void)
98 int oldmaxedicts = prog->max_edicts;
99 void *oldedictsfields = prog->edictsfields;
100 void *oldedictprivate = prog->edictprivate;
102 if(prog->max_edicts >= prog->limit_edicts)
105 PRVM_GCALL(begin_increase_edicts)();
108 prog->max_edicts = min(prog->max_edicts + 256, prog->limit_edicts);
110 prog->edictsfields = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edict_size);
111 prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size);
113 memcpy(prog->edictsfields, oldedictsfields, oldmaxedicts * prog->edict_size);
114 memcpy(prog->edictprivate, oldedictprivate, oldmaxedicts * prog->edictprivate_size);
116 //set e and v pointers
117 for(i = 0; i < prog->max_edicts; i++)
119 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
120 prog->edicts[i].fields.vp = (void*)((unsigned char *)prog->edictsfields + i * prog->edict_size);
123 PRVM_GCALL(end_increase_edicts)();
125 Mem_Free(oldedictsfields);
126 Mem_Free(oldedictprivate);
129 //============================================================================
132 int PRVM_ED_FindFieldOffset(const char *field)
135 d = PRVM_ED_FindField(field);
141 int PRVM_ED_FindGlobalOffset(const char *global)
144 d = PRVM_ED_FindGlobal(global);
150 func_t PRVM_ED_FindFunctionOffset(const char *function)
153 f = PRVM_ED_FindFunction(function);
156 return (func_t)(f - prog->functions);
159 qboolean PRVM_ProgLoaded(int prognr)
161 if(prognr < 0 || prognr >= PRVM_MAXPROGS)
164 return (prog_list[prognr].loaded ? TRUE : FALSE);
169 PRVM_SetProgFromString
172 // perhaps add a return value when the str doesnt exist
173 qboolean PRVM_SetProgFromString(const char *str)
176 for(; i < PRVM_MAXPROGS ; i++)
177 if(prog_list[i].name && !strcmp(prog_list[i].name,str))
179 if(prog_list[i].loaded)
181 prog = &prog_list[i];
186 Con_Printf("%s not loaded !\n",PRVM_NAME);
191 Con_Printf("Invalid program name %s !\n", str);
200 void PRVM_SetProg(int prognr)
202 if(0 <= prognr && prognr < PRVM_MAXPROGS)
204 if(prog_list[prognr].loaded)
205 prog = &prog_list[prognr];
207 PRVM_ERROR("%i not loaded !", prognr);
210 PRVM_ERROR("Invalid program number %i", prognr);
217 Sets everything to NULL
220 void PRVM_ED_ClearEdict (prvm_edict_t *e)
222 memset (e->fields.vp, 0, prog->progs->entityfields * 4);
223 e->priv.required->free = false;
225 // AK: Let the init_edict function determine if something needs to be initialized
226 PRVM_GCALL(init_edict)(e);
229 const char *PRVM_AllocationOrigin(void)
232 if(prog->leaktest_active)
233 if(prog->depth > 0) // actually in QC code and not just parsing the entities block of a map/savegame
235 buf = (char *)PRVM_Alloc(128);
236 PRVM_ShortStackTrace(buf, 128);
245 Returns if this particular edict could get allocated by PRVM_ED_Alloc
248 qboolean PRVM_ED_CanAlloc(prvm_edict_t *e)
250 if(!e->priv.required->free)
252 if(realtime <= e->priv.required->freetime && prvm_reuseedicts_neverinsameframe.integer)
253 return false; // never allow reuse in same frame (causes networking trouble)
254 if(e->priv.required->freetime < prog->starttime + prvm_reuseedicts_startuptime.value)
256 if(realtime > e->priv.required->freetime + 1)
258 return false; // entity slot still blocked because the entity was freed less than one second ago
265 Either finds a free edict, or allocates a new one.
266 Try to avoid reusing an entity that was recently freed, because it
267 can cause the client to think the entity morphed into something else
268 instead of being removed and recreated, which can cause interpolated
269 angles and bad trails.
272 prvm_edict_t *PRVM_ED_Alloc (void)
277 // the client qc dont need maxclients
278 // thus it doesnt need to use svs.maxclients
279 // AK: changed i=svs.maxclients+1
280 // AK: changed so the edict 0 wont spawn -> used as reserved/world entity
281 // although the menu/client has no world
282 for (i = prog->reserved_edicts + 1;i < prog->num_edicts;i++)
284 e = PRVM_EDICT_NUM(i);
285 if(PRVM_ED_CanAlloc(e))
287 PRVM_ED_ClearEdict (e);
288 e->priv.required->allocation_origin = PRVM_AllocationOrigin();
293 if (i == prog->limit_edicts)
294 PRVM_ERROR ("%s: PRVM_ED_Alloc: no free edicts",PRVM_NAME);
297 if (prog->num_edicts >= prog->max_edicts)
298 PRVM_MEM_IncreaseEdicts();
300 e = PRVM_EDICT_NUM(i);
301 PRVM_ED_ClearEdict (e);
303 e->priv.required->allocation_origin = PRVM_AllocationOrigin();
312 Marks the edict as free
313 FIXME: walk all entities and NULL out references to this entity
316 void PRVM_ED_Free (prvm_edict_t *ed)
318 // dont delete the null entity (world) or reserved edicts
319 if(PRVM_NUM_FOR_EDICT(ed) <= prog->reserved_edicts )
322 PRVM_GCALL(free_edict)(ed);
324 ed->priv.required->free = true;
325 ed->priv.required->freetime = realtime;
326 if(ed->priv.required->allocation_origin)
328 PRVM_Free((char *)ed->priv.required->allocation_origin);
329 ed->priv.required->allocation_origin = NULL;
333 //===========================================================================
340 ddef_t *PRVM_ED_GlobalAtOfs (int ofs)
345 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
347 def = &prog->globaldefs[i];
359 ddef_t *PRVM_ED_FieldAtOfs (int ofs)
364 for (i=0 ; i<prog->progs->numfielddefs ; i++)
366 def = &prog->fielddefs[i];
378 ddef_t *PRVM_ED_FindField (const char *name)
383 for (i=0 ; i<prog->progs->numfielddefs ; i++)
385 def = &prog->fielddefs[i];
386 if (!strcmp(PRVM_GetString(def->s_name), name))
397 ddef_t *PRVM_ED_FindGlobal (const char *name)
402 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
404 def = &prog->globaldefs[i];
405 if (!strcmp(PRVM_GetString(def->s_name), name))
417 mfunction_t *PRVM_ED_FindFunction (const char *name)
422 for (i=0 ; i<prog->progs->numfunctions ; i++)
424 func = &prog->functions[i];
425 if (!strcmp(PRVM_GetString(func->s_name), name))
436 Returns a string describing *data in a type specific manner
439 char *PRVM_ValueString (etype_t type, prvm_eval_t *val)
441 static char line[MAX_INPUTLINE];
446 type = (etype_t)((int) type & ~DEF_SAVEGLOBAL);
451 strlcpy (line, PRVM_GetString (val->string), sizeof (line));
455 if (n < 0 || n >= prog->limit_edicts)
456 dpsnprintf (line, sizeof(line), "entity %i (invalid!)", n);
458 dpsnprintf (line, sizeof(line), "entity %i", n);
461 f = prog->functions + val->function;
462 dpsnprintf (line, sizeof(line), "%s()", PRVM_GetString(f->s_name));
465 def = PRVM_ED_FieldAtOfs ( val->_int );
466 dpsnprintf (line, sizeof(line), ".%s", PRVM_GetString(def->s_name));
469 dpsnprintf (line, sizeof(line), "void");
472 // LordHavoc: changed from %5.1f to %10.4f
473 dpsnprintf (line, sizeof(line), "%10.4f", val->_float);
476 // LordHavoc: changed from %5.1f to %10.4f
477 dpsnprintf (line, sizeof(line), "'%10.4f %10.4f %10.4f'", val->vector[0], val->vector[1], val->vector[2]);
480 dpsnprintf (line, sizeof(line), "pointer");
483 dpsnprintf (line, sizeof(line), "bad type %i", (int) type);
494 Returns a string describing *data in a type specific manner
495 Easier to parse than PR_ValueString
498 char *PRVM_UglyValueString (etype_t type, prvm_eval_t *val)
500 static char line[MAX_INPUTLINE];
506 type = (etype_t)((int)type & ~DEF_SAVEGLOBAL);
511 // Parse the string a bit to turn special characters
512 // (like newline, specifically) into escape codes,
513 // this fixes saving games from various mods
514 s = PRVM_GetString (val->string);
515 for (i = 0;i < (int)sizeof(line) - 2 && *s;)
544 dpsnprintf (line, sizeof (line), "%i", PRVM_NUM_FOR_EDICT(PRVM_PROG_TO_EDICT(val->edict)));
547 f = prog->functions + val->function;
548 strlcpy (line, PRVM_GetString (f->s_name), sizeof (line));
551 def = PRVM_ED_FieldAtOfs ( val->_int );
552 dpsnprintf (line, sizeof (line), ".%s", PRVM_GetString(def->s_name));
555 dpsnprintf (line, sizeof (line), "void");
558 dpsnprintf (line, sizeof (line), "%f", val->_float);
561 dpsnprintf (line, sizeof (line), "%f %f %f", val->vector[0], val->vector[1], val->vector[2]);
564 dpsnprintf (line, sizeof (line), "bad type %i", type);
575 Returns a string with a description and the contents of a global,
576 padded to 20 field width
579 char *PRVM_GlobalString (int ofs)
585 static char line[128];
587 val = (void *)&prog->globals.generic[ofs];
588 def = PRVM_ED_GlobalAtOfs(ofs);
590 dpsnprintf (line, sizeof(line), "GLOBAL%i", ofs);
593 s = PRVM_ValueString ((etype_t)def->type, (prvm_eval_t *)val);
594 dpsnprintf (line, sizeof(line), "%s (=%s)", PRVM_GetString(def->s_name), s);
598 //for ( ; i<20 ; i++)
599 // strcat (line," ");
605 char *PRVM_GlobalStringNoContents (int ofs)
609 static char line[128];
611 def = PRVM_ED_GlobalAtOfs(ofs);
613 dpsnprintf (line, sizeof(line), "GLOBAL%i", ofs);
615 dpsnprintf (line, sizeof(line), "%s", PRVM_GetString(def->s_name));
618 //for ( ; i<20 ; i++)
619 // strcat (line," ");
633 // LordHavoc: optimized this to print out much more quickly (tempstring)
634 // LordHavoc: changed to print out every 4096 characters (incase there are a lot of fields to print)
635 void PRVM_ED_Print(prvm_edict_t *ed, const char *wildcard_fieldname)
643 char tempstring[MAX_INPUTLINE], tempstring2[260]; // temporary string buffers
645 if (ed->priv.required->free)
647 Con_Printf("%s: FREE\n",PRVM_NAME);
652 dpsnprintf(tempstring, sizeof(tempstring), "\n%s EDICT %i:\n", PRVM_NAME, PRVM_NUM_FOR_EDICT(ed));
653 for (i=1 ; i<prog->progs->numfielddefs ; i++)
655 d = &prog->fielddefs[i];
656 name = PRVM_GetString(d->s_name);
657 if (name[strlen(name)-2] == '_')
658 continue; // skip _x, _y, _z vars
660 // Check Field Name Wildcard
661 if(wildcard_fieldname)
662 if( !matchpattern(name, wildcard_fieldname, 1) )
663 // Didn't match; skip
666 v = (int *)((char *)ed->fields.vp + d->ofs*4);
668 // if the value is still all 0, skip the field
669 type = d->type & ~DEF_SAVEGLOBAL;
671 for (j=0 ; j<prvm_type_size[type] ; j++)
674 if (j == prvm_type_size[type])
677 if (strlen(name) > sizeof(tempstring2)-4)
679 memcpy (tempstring2, name, sizeof(tempstring2)-4);
680 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
681 tempstring2[sizeof(tempstring2)-1] = 0;
684 strlcat(tempstring, name, sizeof(tempstring));
685 for (l = strlen(name);l < 14;l++)
686 strlcat(tempstring, " ", sizeof(tempstring));
687 strlcat(tempstring, " ", sizeof(tempstring));
689 name = PRVM_ValueString((etype_t)d->type, (prvm_eval_t *)v);
690 if (strlen(name) > sizeof(tempstring2)-4)
692 memcpy (tempstring2, name, sizeof(tempstring2)-4);
693 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
694 tempstring2[sizeof(tempstring2)-1] = 0;
697 strlcat(tempstring, name, sizeof(tempstring));
698 strlcat(tempstring, "\n", sizeof(tempstring));
699 if (strlen(tempstring) >= sizeof(tempstring)/2)
701 Con_Print(tempstring);
706 Con_Print(tempstring);
716 extern cvar_t developer_entityparsing;
717 void PRVM_ED_Write (qfile_t *f, prvm_edict_t *ed)
727 if (ed->priv.required->free)
733 for (i=1 ; i<prog->progs->numfielddefs ; i++)
735 d = &prog->fielddefs[i];
736 name = PRVM_GetString(d->s_name);
738 if(developer_entityparsing.integer)
739 Con_Printf("PRVM_ED_Write: at entity %d field %s\n", PRVM_NUM_FOR_EDICT(ed), name);
741 if (name[strlen(name)-2] == '_')
742 continue; // skip _x, _y, _z vars
744 v = (int *)((char *)ed->fields.vp + d->ofs*4);
746 // if the value is still all 0, skip the field
747 type = d->type & ~DEF_SAVEGLOBAL;
748 for (j=0 ; j<prvm_type_size[type] ; j++)
751 if (j == prvm_type_size[type])
754 FS_Printf(f,"\"%s\" ",name);
755 prog->statestring = va("PRVM_ED_Write, ent=%d, name=%s", i, name);
756 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString((etype_t)d->type, (prvm_eval_t *)v));
757 prog->statestring = NULL;
763 void PRVM_ED_PrintNum (int ent, const char *wildcard_fieldname)
765 PRVM_ED_Print(PRVM_EDICT_NUM(ent), wildcard_fieldname);
770 PRVM_ED_PrintEdicts_f
772 For debugging, prints all the entities in the current server
775 void PRVM_ED_PrintEdicts_f (void)
778 const char *wildcard_fieldname;
780 if(Cmd_Argc() < 2 || Cmd_Argc() > 3)
782 Con_Print("prvm_edicts <program name> <optional field name wildcard>\n");
787 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
791 wildcard_fieldname = Cmd_Argv(2);
793 wildcard_fieldname = NULL;
795 Con_Printf("%s: %i entities\n", PRVM_NAME, prog->num_edicts);
796 for (i=0 ; i<prog->num_edicts ; i++)
797 PRVM_ED_PrintNum (i, wildcard_fieldname);
806 For debugging, prints a single edict
809 void PRVM_ED_PrintEdict_f (void)
812 const char *wildcard_fieldname;
814 if(Cmd_Argc() < 3 || Cmd_Argc() > 4)
816 Con_Print("prvm_edict <program name> <edict number> <optional field name wildcard>\n");
821 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
824 i = atoi (Cmd_Argv(2));
825 if (i >= prog->num_edicts)
827 Con_Print("Bad edict number\n");
832 // Optional Wildcard Provided
833 wildcard_fieldname = Cmd_Argv(3);
836 wildcard_fieldname = NULL;
837 PRVM_ED_PrintNum (i, wildcard_fieldname);
849 // 2 possibilities : 1. just displaying the active edict count
850 // 2. making a function pointer [x]
851 void PRVM_ED_Count_f (void)
859 Con_Print("prvm_count <program name>\n");
864 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
867 if(prog->count_edicts)
868 prog->count_edicts();
872 for (i=0 ; i<prog->num_edicts ; i++)
874 ent = PRVM_EDICT_NUM(i);
875 if (ent->priv.required->free)
880 Con_Printf("num_edicts:%3i\n", prog->num_edicts);
881 Con_Printf("active :%3i\n", active);
888 ==============================================================================
892 FIXME: need to tag constants, doesn't really work
893 ==============================================================================
901 void PRVM_ED_WriteGlobals (qfile_t *f)
909 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
911 def = &prog->globaldefs[i];
913 if ( !(def->type & DEF_SAVEGLOBAL) )
915 type &= ~DEF_SAVEGLOBAL;
917 if (type != ev_string && type != ev_float && type != ev_entity)
920 name = PRVM_GetString(def->s_name);
922 if(developer_entityparsing.integer)
923 Con_Printf("PRVM_ED_WriteGlobals: at global %s\n", name);
925 prog->statestring = va("PRVM_ED_WriteGlobals, name=%s", name);
926 FS_Printf(f,"\"%s\" ", name);
927 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString((etype_t)type, (prvm_eval_t *)&prog->globals.generic[def->ofs]));
928 prog->statestring = NULL;
938 void PRVM_ED_ParseGlobals (const char *data)
940 char keyname[MAX_INPUTLINE];
946 if (!COM_ParseToken_Simple(&data, false, false))
947 PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace");
948 if (com_token[0] == '}')
951 if (developer_entityparsing.integer)
952 Con_Printf("Key: \"%s\"", com_token);
954 strlcpy (keyname, com_token, sizeof(keyname));
957 if (!COM_ParseToken_Simple(&data, false, true))
958 PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace");
960 if (developer_entityparsing.integer)
961 Con_Printf(" \"%s\"\n", com_token);
963 if (com_token[0] == '}')
964 PRVM_ERROR ("PRVM_ED_ParseGlobals: closing brace without data");
966 key = PRVM_ED_FindGlobal (keyname);
969 Con_DPrintf("'%s' is not a global on %s\n", keyname, PRVM_NAME);
973 if (!PRVM_ED_ParseEpair(NULL, key, com_token, true))
974 PRVM_ERROR ("PRVM_ED_ParseGlobals: parse error");
978 //============================================================================
985 Can parse either fields or globals
986 returns false if error
989 qboolean PRVM_ED_ParseEpair(prvm_edict_t *ent, ddef_t *key, const char *s, qboolean parsebackslash)
998 val = (prvm_eval_t *)((int *)ent->fields.vp + key->ofs);
1000 val = (prvm_eval_t *)((int *)prog->globals.generic + key->ofs);
1001 switch (key->type & ~DEF_SAVEGLOBAL)
1004 l = (int)strlen(s) + 1;
1005 val->string = PRVM_AllocString(l, &new_p);
1006 for (i = 0;i < l;i++)
1008 if (s[i] == '\\' && s[i+1] && parsebackslash)
1013 else if (s[i] == 'r')
1024 while (*s && ISWHITESPACE(*s))
1026 val->_float = atof(s);
1030 for (i = 0;i < 3;i++)
1032 while (*s && ISWHITESPACE(*s))
1036 val->vector[i] = atof(s);
1037 while (!ISWHITESPACE(*s))
1045 while (*s && ISWHITESPACE(*s))
1048 if (i >= prog->limit_edicts)
1049 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);
1050 while (i >= prog->max_edicts)
1051 PRVM_MEM_IncreaseEdicts();
1052 // if IncreaseEdicts was called the base pointer needs to be updated
1054 val = (prvm_eval_t *)((int *)ent->fields.vp + key->ofs);
1055 val->edict = PRVM_EDICT_TO_PROG(PRVM_EDICT_NUM((int)i));
1061 Con_DPrintf("PRVM_ED_ParseEpair: Bogus field name %s in %s\n", s, PRVM_NAME);
1064 def = PRVM_ED_FindField(s + 1);
1067 Con_DPrintf("PRVM_ED_ParseEpair: Can't find field %s in %s\n", s, PRVM_NAME);
1070 val->_int = def->ofs;
1074 func = PRVM_ED_FindFunction(s);
1077 Con_Printf("PRVM_ED_ParseEpair: Can't find function %s in %s\n", s, PRVM_NAME);
1080 val->function = func - prog->functions;
1084 Con_Printf("PRVM_ED_ParseEpair: Unknown key->type %i for key \"%s\" on %s\n", key->type, PRVM_GetString(key->s_name), PRVM_NAME);
1094 Console command to send a string to QC function GameCommand of the
1098 sv_cmd adminmsg 3 "do not teamkill"
1099 cl_cmd someclientcommand
1100 menu_cmd somemenucommand
1102 All progs can support this extension; sg calls it in server QC, cg in client
1106 void PRVM_GameCommand(const char *whichprogs, const char *whichcmd)
1110 Con_Printf("%s text...\n", whichcmd);
1115 if(!PRVM_SetProgFromString(whichprogs))
1116 // note: this is not PRVM_SetProg because that one aborts "hard" using PRVM_Error
1117 // also, it makes printing error messages easier!
1119 Con_Printf("%s program not loaded.\n", whichprogs);
1123 if(!prog->funcoffsets.GameCommand)
1125 Con_Printf("%s program do not support GameCommand!\n", whichprogs);
1129 int restorevm_tempstringsbuf_cursize;
1134 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
1135 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(s ? s : "");
1136 PRVM_ExecuteProgram (prog->funcoffsets.GameCommand, "QC function GameCommand is missing");
1137 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1142 void PRVM_GameCommand_Server_f(void)
1144 PRVM_GameCommand("server", "sv_cmd");
1146 void PRVM_GameCommand_Client_f(void)
1148 PRVM_GameCommand("client", "cl_cmd");
1150 void PRVM_GameCommand_Menu_f(void)
1152 PRVM_GameCommand("menu", "menu_cmd");
1159 Console command to load a field of a specified edict
1162 void PRVM_ED_EdictGet_f(void)
1169 if(Cmd_Argc() != 4 && Cmd_Argc() != 5)
1171 Con_Print("prvm_edictget <program name> <edict number> <field> [<cvar>]\n");
1176 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1178 Con_Printf("Wrong program name %s !\n", Cmd_Argv(1));
1182 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1184 if((key = PRVM_ED_FindField(Cmd_Argv(3))) == 0)
1186 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1190 v = (prvm_eval_t *)((char *)ed->fields.vp + key->ofs*4);
1191 s = PRVM_UglyValueString(key->type, v);
1194 cvar_t *cvar = Cvar_FindVar(Cmd_Argv(4));
1195 if (cvar && cvar->flags & CVAR_READONLY)
1197 Con_Printf("prvm_edictget: %s is read-only\n", cvar->name);
1200 Cvar_Get(Cmd_Argv(4), s, 0, NULL);
1203 Con_Printf("%s\n", s);
1209 void PRVM_ED_GlobalGet_f(void)
1215 if(Cmd_Argc() != 3 && Cmd_Argc() != 4)
1217 Con_Print("prvm_globalget <program name> <global> [<cvar>]\n");
1222 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1224 Con_Printf("Wrong program name %s !\n", Cmd_Argv(1));
1228 key = PRVM_ED_FindGlobal(Cmd_Argv(2));
1231 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
1235 v = (prvm_eval_t *) &prog->globals.generic[key->ofs];
1236 s = PRVM_UglyValueString(key->type, v);
1239 cvar_t *cvar = Cvar_FindVar(Cmd_Argv(3));
1240 if (cvar && cvar->flags & CVAR_READONLY)
1242 Con_Printf("prvm_globalget: %s is read-only\n", cvar->name);
1245 Cvar_Get(Cmd_Argv(3), s, 0, NULL);
1248 Con_Printf("%s\n", s);
1258 Console command to set a field of a specified edict
1261 void PRVM_ED_EdictSet_f(void)
1268 Con_Print("prvm_edictset <program name> <edict number> <field> <value>\n");
1273 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1275 Con_Printf("Wrong program name %s !\n", Cmd_Argv(1));
1279 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1281 if((key = PRVM_ED_FindField(Cmd_Argv(3))) == 0)
1282 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1284 PRVM_ED_ParseEpair(ed, key, Cmd_Argv(4), true);
1290 ====================
1293 Parses an edict out of the given string, returning the new position
1294 ed should be a properly initialized empty edict.
1295 Used for initial level load and for savegames.
1296 ====================
1298 const char *PRVM_ED_ParseEdict (const char *data, prvm_edict_t *ent)
1308 // go through all the dictionary pairs
1312 if (!COM_ParseToken_Simple(&data, false, false))
1313 PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1314 if (developer_entityparsing.integer)
1315 Con_Printf("Key: \"%s\"", com_token);
1316 if (com_token[0] == '}')
1319 // anglehack is to allow QuakeEd to write single scalar angles
1320 // and allow them to be turned into vectors. (FIXME...)
1321 if (!strcmp(com_token, "angle"))
1323 strlcpy (com_token, "angles", sizeof(com_token));
1329 // FIXME: change light to _light to get rid of this hack
1330 if (!strcmp(com_token, "light"))
1331 strlcpy (com_token, "light_lev", sizeof(com_token)); // hack for single light def
1333 strlcpy (keyname, com_token, sizeof(keyname));
1335 // another hack to fix keynames with trailing spaces
1336 n = strlen(keyname);
1337 while (n && keyname[n-1] == ' ')
1344 if (!COM_ParseToken_Simple(&data, false, false))
1345 PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1346 if (developer_entityparsing.integer)
1347 Con_Printf(" \"%s\"\n", com_token);
1349 if (com_token[0] == '}')
1350 PRVM_ERROR ("PRVM_ED_ParseEdict: closing brace without data");
1354 // ignore attempts to set key "" (this problem occurs in nehahra neh1m8.bsp)
1358 // keynames with a leading underscore are used for utility comments,
1359 // and are immediately discarded by quake
1360 if (keyname[0] == '_')
1363 key = PRVM_ED_FindField (keyname);
1366 Con_DPrintf("%s: '%s' is not a field\n", PRVM_NAME, keyname);
1373 strlcpy (temp, com_token, sizeof(temp));
1374 dpsnprintf (com_token, sizeof(com_token), "0 %s 0", temp);
1377 if (!PRVM_ED_ParseEpair(ent, key, com_token, strcmp(keyname, "wad") != 0))
1378 PRVM_ERROR ("PRVM_ED_ParseEdict: parse error");
1382 ent->priv.required->free = true;
1390 PRVM_ED_LoadFromFile
1392 The entities are directly placed in the array, rather than allocated with
1393 PRVM_ED_Alloc, because otherwise an error loading the map would have entity
1394 number references out of order.
1396 Creates a server's entity / program execution context by
1397 parsing textual entity definitions out of an ent file.
1399 Used for both fresh maps and savegame loads. A fresh map would also need
1400 to call PRVM_ED_CallSpawnFunctions () to let the objects initialize themselves.
1403 void PRVM_ED_LoadFromFile (const char *data)
1406 int parsed, inhibited, spawned, died;
1407 const char *funcname;
1419 // parse the opening brace
1420 if (!COM_ParseToken_Simple(&data, false, false))
1422 if (com_token[0] != '{')
1423 PRVM_ERROR ("PRVM_ED_LoadFromFile: %s: found %s when expecting {", PRVM_NAME, com_token);
1425 // CHANGED: this is not conform to PR_LoadFromFile
1426 if(prog->loadintoworld)
1428 prog->loadintoworld = false;
1429 ent = PRVM_EDICT_NUM(0);
1432 ent = PRVM_ED_Alloc();
1435 if (ent != prog->edicts) // hack
1436 memset (ent->fields.vp, 0, prog->progs->entityfields * 4);
1438 data = PRVM_ED_ParseEdict (data, ent);
1441 // remove the entity ?
1442 if(prog->load_edict && !prog->load_edict(ent))
1449 if (prog->funcoffsets.SV_OnEntityPreSpawnFunction)
1452 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1453 PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityPreSpawnFunction, "QC function SV_OnEntityPreSpawnFunction is missing");
1456 if(ent->priv.required->free)
1463 // immediately call spawn function, but only if there is a self global and a classname
1465 if(!ent->priv.required->free)
1466 if(prog->globaloffsets.self >= 0 && prog->fieldoffsets.classname >= 0)
1468 string_t handle = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.classname)->string;
1471 Con_Print("No classname for:\n");
1472 PRVM_ED_Print(ent, NULL);
1477 // look for the spawn function
1478 funcname = PRVM_GetString(handle);
1479 func = PRVM_ED_FindFunction (va("spawnfunc_%s", funcname));
1481 if(prog->globaloffsets.require_spawnfunc_prefix < 0)
1482 func = PRVM_ED_FindFunction (funcname);
1486 // check for OnEntityNoSpawnFunction
1487 if (prog->funcoffsets.SV_OnEntityNoSpawnFunction)
1490 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1491 PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityNoSpawnFunction, "QC function SV_OnEntityNoSpawnFunction is missing");
1495 if (developer.integer) // don't confuse non-developers with errors
1497 Con_Print("No spawn function for:\n");
1498 PRVM_ED_Print(ent, NULL);
1501 continue; // not included in "inhibited" count
1507 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1508 PRVM_ExecuteProgram (func - prog->functions, "");
1512 if(!ent->priv.required->free)
1513 if (prog->funcoffsets.SV_OnEntityPostSpawnFunction)
1516 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1517 PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityPostSpawnFunction, "QC function SV_OnEntityPostSpawnFunction is missing");
1521 if (ent->priv.required->free)
1525 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);
1528 void PRVM_FindOffsets(void)
1530 // field and global searches use -1 for NULL
1531 memset(&prog->fieldoffsets, -1, sizeof(prog->fieldoffsets));
1532 memset(&prog->globaloffsets, -1, sizeof(prog->globaloffsets));
1533 // functions use 0 for NULL
1534 memset(&prog->funcoffsets, 0, sizeof(prog->funcoffsets));
1536 // server and client qc use a lot of similar fields, so this is combined
1537 prog->fieldoffsets.SendEntity = PRVM_ED_FindFieldOffset("SendEntity");
1538 prog->fieldoffsets.SendFlags = PRVM_ED_FindFieldOffset("SendFlags");
1539 prog->fieldoffsets.Version = PRVM_ED_FindFieldOffset("Version");
1540 prog->fieldoffsets.alpha = PRVM_ED_FindFieldOffset("alpha");
1541 prog->fieldoffsets.ammo_cells1 = PRVM_ED_FindFieldOffset("ammo_cells1");
1542 prog->fieldoffsets.ammo_lava_nails = PRVM_ED_FindFieldOffset("ammo_lava_nails");
1543 prog->fieldoffsets.ammo_multi_rockets = PRVM_ED_FindFieldOffset("ammo_multi_rockets");
1544 prog->fieldoffsets.ammo_nails1 = PRVM_ED_FindFieldOffset("ammo_nails1");
1545 prog->fieldoffsets.ammo_plasma = PRVM_ED_FindFieldOffset("ammo_plasma");
1546 prog->fieldoffsets.ammo_rockets1 = PRVM_ED_FindFieldOffset("ammo_rockets1");
1547 prog->fieldoffsets.ammo_shells1 = PRVM_ED_FindFieldOffset("ammo_shells1");
1548 prog->fieldoffsets.angles = PRVM_ED_FindFieldOffset("angles");
1549 prog->fieldoffsets.button3 = PRVM_ED_FindFieldOffset("button3");
1550 prog->fieldoffsets.button4 = PRVM_ED_FindFieldOffset("button4");
1551 prog->fieldoffsets.button5 = PRVM_ED_FindFieldOffset("button5");
1552 prog->fieldoffsets.button6 = PRVM_ED_FindFieldOffset("button6");
1553 prog->fieldoffsets.button7 = PRVM_ED_FindFieldOffset("button7");
1554 prog->fieldoffsets.button8 = PRVM_ED_FindFieldOffset("button8");
1555 prog->fieldoffsets.button9 = PRVM_ED_FindFieldOffset("button9");
1556 prog->fieldoffsets.button10 = PRVM_ED_FindFieldOffset("button10");
1557 prog->fieldoffsets.button11 = PRVM_ED_FindFieldOffset("button11");
1558 prog->fieldoffsets.button12 = PRVM_ED_FindFieldOffset("button12");
1559 prog->fieldoffsets.button13 = PRVM_ED_FindFieldOffset("button13");
1560 prog->fieldoffsets.button14 = PRVM_ED_FindFieldOffset("button14");
1561 prog->fieldoffsets.button15 = PRVM_ED_FindFieldOffset("button15");
1562 prog->fieldoffsets.button16 = PRVM_ED_FindFieldOffset("button16");
1563 prog->fieldoffsets.buttonchat = PRVM_ED_FindFieldOffset("buttonchat");
1564 prog->fieldoffsets.buttonuse = PRVM_ED_FindFieldOffset("buttonuse");
1565 prog->fieldoffsets.chain = PRVM_ED_FindFieldOffset("chain");
1566 prog->fieldoffsets.classname = PRVM_ED_FindFieldOffset("classname");
1567 prog->fieldoffsets.clientcamera = PRVM_ED_FindFieldOffset("clientcamera");
1568 prog->fieldoffsets.clientcolors = PRVM_ED_FindFieldOffset("clientcolors");
1569 prog->fieldoffsets.clientstatus = PRVM_ED_FindFieldOffset("clientstatus");
1570 prog->fieldoffsets.color = PRVM_ED_FindFieldOffset("color");
1571 prog->fieldoffsets.colormod = PRVM_ED_FindFieldOffset("colormod");
1572 prog->fieldoffsets.contentstransition = PRVM_ED_FindFieldOffset("contentstransition");
1573 prog->fieldoffsets.cursor_active = PRVM_ED_FindFieldOffset("cursor_active");
1574 prog->fieldoffsets.cursor_screen = PRVM_ED_FindFieldOffset("cursor_screen");
1575 prog->fieldoffsets.cursor_trace_endpos = PRVM_ED_FindFieldOffset("cursor_trace_endpos");
1576 prog->fieldoffsets.cursor_trace_ent = PRVM_ED_FindFieldOffset("cursor_trace_ent");
1577 prog->fieldoffsets.cursor_trace_start = PRVM_ED_FindFieldOffset("cursor_trace_start");
1578 prog->fieldoffsets.customizeentityforclient = PRVM_ED_FindFieldOffset("customizeentityforclient");
1579 prog->fieldoffsets.dimension_hit = PRVM_ED_FindFieldOffset("dimension_hit");
1580 prog->fieldoffsets.dimension_solid = PRVM_ED_FindFieldOffset("dimension_solid");
1581 prog->fieldoffsets.disableclientprediction = PRVM_ED_FindFieldOffset("disableclientprediction");
1582 prog->fieldoffsets.dphitcontentsmask = PRVM_ED_FindFieldOffset("dphitcontentsmask");
1583 prog->fieldoffsets.drawonlytoclient = PRVM_ED_FindFieldOffset("drawonlytoclient");
1584 prog->fieldoffsets.exteriormodeltoclient = PRVM_ED_FindFieldOffset("exteriormodeltoclient");
1585 prog->fieldoffsets.fatness = PRVM_ED_FindFieldOffset("fatness");
1586 prog->fieldoffsets.forceshader = PRVM_ED_FindFieldOffset("forceshader");
1587 prog->fieldoffsets.frame = PRVM_ED_FindFieldOffset("frame");
1588 prog->fieldoffsets.frame1time = PRVM_ED_FindFieldOffset("frame1time");
1589 prog->fieldoffsets.frame2 = PRVM_ED_FindFieldOffset("frame2");
1590 prog->fieldoffsets.frame2time = PRVM_ED_FindFieldOffset("frame2time");
1591 prog->fieldoffsets.frame3 = PRVM_ED_FindFieldOffset("frame3");
1592 prog->fieldoffsets.frame3time = PRVM_ED_FindFieldOffset("frame3time");
1593 prog->fieldoffsets.frame4 = PRVM_ED_FindFieldOffset("frame4");
1594 prog->fieldoffsets.frame4time = PRVM_ED_FindFieldOffset("frame4time");
1595 prog->fieldoffsets.fullbright = PRVM_ED_FindFieldOffset("fullbright");
1596 prog->fieldoffsets.glow_color = PRVM_ED_FindFieldOffset("glow_color");
1597 prog->fieldoffsets.glow_size = PRVM_ED_FindFieldOffset("glow_size");
1598 prog->fieldoffsets.glow_trail = PRVM_ED_FindFieldOffset("glow_trail");
1599 prog->fieldoffsets.glowmod = PRVM_ED_FindFieldOffset("glowmod");
1600 prog->fieldoffsets.gravity = PRVM_ED_FindFieldOffset("gravity");
1601 prog->fieldoffsets.groundentity = PRVM_ED_FindFieldOffset("groundentity");
1602 prog->fieldoffsets.hull = PRVM_ED_FindFieldOffset("hull");
1603 prog->fieldoffsets.ideal_yaw = PRVM_ED_FindFieldOffset("ideal_yaw");
1604 prog->fieldoffsets.idealpitch = PRVM_ED_FindFieldOffset("idealpitch");
1605 prog->fieldoffsets.items2 = PRVM_ED_FindFieldOffset("items2");
1606 prog->fieldoffsets.lerpfrac = PRVM_ED_FindFieldOffset("lerpfrac");
1607 prog->fieldoffsets.lerpfrac3 = PRVM_ED_FindFieldOffset("lerpfrac3");
1608 prog->fieldoffsets.lerpfrac4 = PRVM_ED_FindFieldOffset("lerpfrac4");
1609 prog->fieldoffsets.light_lev = PRVM_ED_FindFieldOffset("light_lev");
1610 prog->fieldoffsets.message = PRVM_ED_FindFieldOffset("message");
1611 prog->fieldoffsets.modelflags = PRVM_ED_FindFieldOffset("modelflags");
1612 prog->fieldoffsets.movement = PRVM_ED_FindFieldOffset("movement");
1613 prog->fieldoffsets.movetypesteplandevent = PRVM_ED_FindFieldOffset("movetypesteplandevent");
1614 prog->fieldoffsets.netaddress = PRVM_ED_FindFieldOffset("netaddress");
1615 prog->fieldoffsets.nextthink = PRVM_ED_FindFieldOffset("nextthink");
1616 prog->fieldoffsets.nodrawtoclient = PRVM_ED_FindFieldOffset("nodrawtoclient");
1617 prog->fieldoffsets.pflags = PRVM_ED_FindFieldOffset("pflags");
1618 prog->fieldoffsets.ping = PRVM_ED_FindFieldOffset("ping");
1619 prog->fieldoffsets.pitch_speed = PRVM_ED_FindFieldOffset("pitch_speed");
1620 prog->fieldoffsets.playermodel = PRVM_ED_FindFieldOffset("playermodel");
1621 prog->fieldoffsets.playerskin = PRVM_ED_FindFieldOffset("playerskin");
1622 prog->fieldoffsets.pmodel = PRVM_ED_FindFieldOffset("pmodel");
1623 prog->fieldoffsets.punchvector = PRVM_ED_FindFieldOffset("punchvector");
1624 prog->fieldoffsets.renderamt = PRVM_ED_FindFieldOffset("renderamt"); // HalfLife support
1625 prog->fieldoffsets.renderflags = PRVM_ED_FindFieldOffset("renderflags");
1626 prog->fieldoffsets.rendermode = PRVM_ED_FindFieldOffset("rendermode"); // HalfLife support
1627 prog->fieldoffsets.scale = PRVM_ED_FindFieldOffset("scale");
1628 prog->fieldoffsets.shadertime = PRVM_ED_FindFieldOffset("shadertime");
1629 prog->fieldoffsets.skeletonindex = PRVM_ED_FindFieldOffset("skeletonindex");
1630 prog->fieldoffsets.style = PRVM_ED_FindFieldOffset("style");
1631 prog->fieldoffsets.tag_entity = PRVM_ED_FindFieldOffset("tag_entity");
1632 prog->fieldoffsets.tag_index = PRVM_ED_FindFieldOffset("tag_index");
1633 prog->fieldoffsets.think = PRVM_ED_FindFieldOffset("think");
1634 prog->fieldoffsets.viewmodelforclient = PRVM_ED_FindFieldOffset("viewmodelforclient");
1635 prog->fieldoffsets.viewzoom = PRVM_ED_FindFieldOffset("viewzoom");
1636 prog->fieldoffsets.yaw_speed = PRVM_ED_FindFieldOffset("yaw_speed");
1637 prog->fieldoffsets.bouncefactor = PRVM_ED_FindFieldOffset("bouncefactor");
1638 prog->fieldoffsets.bouncestop = PRVM_ED_FindFieldOffset("bouncestop");
1640 prog->fieldoffsets.solid = PRVM_ED_FindFieldOffset("solid");
1641 prog->fieldoffsets.movetype = PRVM_ED_FindFieldOffset("movetype");
1642 prog->fieldoffsets.modelindex = PRVM_ED_FindFieldOffset("modelindex");
1643 prog->fieldoffsets.mins = PRVM_ED_FindFieldOffset("mins");
1644 prog->fieldoffsets.maxs = PRVM_ED_FindFieldOffset("maxs");
1645 prog->fieldoffsets.mass = PRVM_ED_FindFieldOffset("mass");
1646 prog->fieldoffsets.origin = PRVM_ED_FindFieldOffset("origin");
1647 prog->fieldoffsets.velocity = PRVM_ED_FindFieldOffset("velocity");
1648 //prog->fieldoffsets.axis_forward = PRVM_ED_FindFieldOffset("axis_forward");
1649 //prog->fieldoffsets.axis_left = PRVM_ED_FindFieldOffset("axis_left");
1650 //prog->fieldoffsets.axis_up = PRVM_ED_FindFieldOffset("axis_up");
1651 //prog->fieldoffsets.spinvelocity = PRVM_ED_FindFieldOffset("spinvelocity");
1652 prog->fieldoffsets.angles = PRVM_ED_FindFieldOffset("angles");
1653 prog->fieldoffsets.avelocity = PRVM_ED_FindFieldOffset("avelocity");
1654 prog->fieldoffsets.aiment = PRVM_ED_FindFieldOffset("aiment");
1655 prog->fieldoffsets.enemy = PRVM_ED_FindFieldOffset("enemy");
1656 prog->fieldoffsets.jointtype = PRVM_ED_FindFieldOffset("jointtype");
1657 prog->fieldoffsets.movedir = PRVM_ED_FindFieldOffset("movedir");
1659 prog->funcoffsets.CSQC_ConsoleCommand = PRVM_ED_FindFunctionOffset("CSQC_ConsoleCommand");
1660 prog->funcoffsets.CSQC_Ent_Remove = PRVM_ED_FindFunctionOffset("CSQC_Ent_Remove");
1661 prog->funcoffsets.CSQC_Ent_Spawn = PRVM_ED_FindFunctionOffset("CSQC_Ent_Spawn");
1662 prog->funcoffsets.CSQC_Ent_Update = PRVM_ED_FindFunctionOffset("CSQC_Ent_Update");
1663 prog->funcoffsets.CSQC_Event = PRVM_ED_FindFunctionOffset("CSQC_Event");
1664 prog->funcoffsets.CSQC_Event_Sound = PRVM_ED_FindFunctionOffset("CSQC_Event_Sound");
1665 prog->funcoffsets.CSQC_Init = PRVM_ED_FindFunctionOffset("CSQC_Init");
1666 prog->funcoffsets.CSQC_InputEvent = PRVM_ED_FindFunctionOffset("CSQC_InputEvent");
1667 prog->funcoffsets.CSQC_Parse_CenterPrint = PRVM_ED_FindFunctionOffset("CSQC_Parse_CenterPrint");
1668 prog->funcoffsets.CSQC_Parse_Print = PRVM_ED_FindFunctionOffset("CSQC_Parse_Print");
1669 prog->funcoffsets.CSQC_Parse_StuffCmd = PRVM_ED_FindFunctionOffset("CSQC_Parse_StuffCmd");
1670 prog->funcoffsets.CSQC_Parse_TempEntity = PRVM_ED_FindFunctionOffset("CSQC_Parse_TempEntity");
1671 prog->funcoffsets.CSQC_Shutdown = PRVM_ED_FindFunctionOffset("CSQC_Shutdown");
1672 prog->funcoffsets.CSQC_UpdateView = PRVM_ED_FindFunctionOffset("CSQC_UpdateView");
1673 prog->funcoffsets.EndFrame = PRVM_ED_FindFunctionOffset("EndFrame");
1674 prog->funcoffsets.GameCommand = PRVM_ED_FindFunctionOffset("GameCommand");
1675 prog->funcoffsets.Gecko_Query = PRVM_ED_FindFunctionOffset("Gecko_Query");
1676 prog->funcoffsets.RestoreGame = PRVM_ED_FindFunctionOffset("RestoreGame");
1677 prog->funcoffsets.SV_ChangeTeam = PRVM_ED_FindFunctionOffset("SV_ChangeTeam");
1678 prog->funcoffsets.SV_OnEntityNoSpawnFunction = PRVM_ED_FindFunctionOffset("SV_OnEntityNoSpawnFunction");
1679 prog->funcoffsets.SV_OnEntityPostSpawnFunction = PRVM_ED_FindFunctionOffset("SV_OnEntityPostSpawnFunction");
1680 prog->funcoffsets.SV_OnEntityPreSpawnFunction = PRVM_ED_FindFunctionOffset("SV_OnEntityPreSpawnFunction");
1681 prog->funcoffsets.SV_ParseClientCommand = PRVM_ED_FindFunctionOffset("SV_ParseClientCommand");
1682 prog->funcoffsets.SV_PausedTic = PRVM_ED_FindFunctionOffset("SV_PausedTic");
1683 prog->funcoffsets.SV_PlayerPhysics = PRVM_ED_FindFunctionOffset("SV_PlayerPhysics");
1684 prog->funcoffsets.SV_Shutdown = PRVM_ED_FindFunctionOffset("SV_Shutdown");
1685 prog->funcoffsets.URI_Get_Callback = PRVM_ED_FindFunctionOffset("URI_Get_Callback");
1686 prog->globaloffsets.SV_InitCmd = PRVM_ED_FindGlobalOffset("SV_InitCmd");
1687 prog->globaloffsets.coop = PRVM_ED_FindGlobalOffset("coop");
1688 prog->globaloffsets.deathmatch = PRVM_ED_FindGlobalOffset("deathmatch");
1689 prog->globaloffsets.dmg_origin = PRVM_ED_FindGlobalOffset("dmg_origin");
1690 prog->globaloffsets.dmg_save = PRVM_ED_FindGlobalOffset("dmg_save");
1691 prog->globaloffsets.dmg_take = PRVM_ED_FindGlobalOffset("dmg_take");
1692 prog->globaloffsets.drawfont = PRVM_ED_FindGlobalOffset("drawfont");
1693 prog->globaloffsets.gettaginfo_forward = PRVM_ED_FindGlobalOffset("gettaginfo_forward");
1694 prog->globaloffsets.gettaginfo_name = PRVM_ED_FindGlobalOffset("gettaginfo_name");
1695 prog->globaloffsets.gettaginfo_offset = PRVM_ED_FindGlobalOffset("gettaginfo_offset");
1696 prog->globaloffsets.gettaginfo_parent = PRVM_ED_FindGlobalOffset("gettaginfo_parent");
1697 prog->globaloffsets.gettaginfo_right = PRVM_ED_FindGlobalOffset("gettaginfo_right");
1698 prog->globaloffsets.gettaginfo_up = PRVM_ED_FindGlobalOffset("gettaginfo_up");
1699 prog->globaloffsets.transparent_offset = PRVM_ED_FindGlobalOffset("transparent_offset");
1700 prog->globaloffsets.intermission = PRVM_ED_FindGlobalOffset("intermission");
1701 prog->globaloffsets.require_spawnfunc_prefix = PRVM_ED_FindGlobalOffset("require_spawnfunc_prefix");
1702 prog->globaloffsets.sb_showscores = PRVM_ED_FindGlobalOffset("sb_showscores");
1703 prog->globaloffsets.self = PRVM_ED_FindGlobalOffset("self");
1704 prog->globaloffsets.serverdeltatime = PRVM_ED_FindGlobalOffset("serverdeltatime");
1705 prog->globaloffsets.serverprevtime = PRVM_ED_FindGlobalOffset("serverprevtime");
1706 prog->globaloffsets.servertime = PRVM_ED_FindGlobalOffset("servertime");
1707 prog->globaloffsets.time = PRVM_ED_FindGlobalOffset("time");
1708 prog->globaloffsets.trace_allsolid = PRVM_ED_FindGlobalOffset("trace_allsolid");
1709 prog->globaloffsets.trace_dphitcontents = PRVM_ED_FindGlobalOffset("trace_dphitcontents");
1710 prog->globaloffsets.trace_dphitq3surfaceflags = PRVM_ED_FindGlobalOffset("trace_dphitq3surfaceflags");
1711 prog->globaloffsets.trace_dphittexturename = PRVM_ED_FindGlobalOffset("trace_dphittexturename");
1712 prog->globaloffsets.trace_dpstartcontents = PRVM_ED_FindGlobalOffset("trace_dpstartcontents");
1713 prog->globaloffsets.trace_endpos = PRVM_ED_FindGlobalOffset("trace_endpos");
1714 prog->globaloffsets.trace_ent = PRVM_ED_FindGlobalOffset("trace_ent");
1715 prog->globaloffsets.trace_fraction = PRVM_ED_FindGlobalOffset("trace_fraction");
1716 prog->globaloffsets.trace_inopen = PRVM_ED_FindGlobalOffset("trace_inopen");
1717 prog->globaloffsets.trace_inwater = PRVM_ED_FindGlobalOffset("trace_inwater");
1718 prog->globaloffsets.trace_networkentity = PRVM_ED_FindGlobalOffset("trace_networkentity");
1719 prog->globaloffsets.trace_plane_dist = PRVM_ED_FindGlobalOffset("trace_plane_dist");
1720 prog->globaloffsets.trace_plane_normal = PRVM_ED_FindGlobalOffset("trace_plane_normal");
1721 prog->globaloffsets.trace_startsolid = PRVM_ED_FindGlobalOffset("trace_startsolid");
1722 prog->globaloffsets.v_forward = PRVM_ED_FindGlobalOffset("v_forward");
1723 prog->globaloffsets.v_right = PRVM_ED_FindGlobalOffset("v_right");
1724 prog->globaloffsets.v_up = PRVM_ED_FindGlobalOffset("v_up");
1725 prog->globaloffsets.view_angles = PRVM_ED_FindGlobalOffset("view_angles");
1726 prog->globaloffsets.worldstatus = PRVM_ED_FindGlobalOffset("worldstatus");
1728 // menu qc only uses some functions, nothing else
1729 prog->funcoffsets.m_draw = PRVM_ED_FindFunctionOffset("m_draw");
1730 prog->funcoffsets.m_init = PRVM_ED_FindFunctionOffset("m_init");
1731 prog->funcoffsets.m_keydown = PRVM_ED_FindFunctionOffset("m_keydown");
1732 prog->funcoffsets.m_keyup = PRVM_ED_FindFunctionOffset("m_keyup");
1733 prog->funcoffsets.m_shutdown = PRVM_ED_FindFunctionOffset("m_shutdown");
1734 prog->funcoffsets.m_toggle = PRVM_ED_FindFunctionOffset("m_toggle");
1739 typedef struct dpfield_s
1746 #define DPFIELDS (sizeof(dpfields) / sizeof(dpfield_t))
1748 dpfield_t dpfields[] =
1759 void PRVM_LeakTest(void);
1760 void PRVM_ResetProg(void)
1763 PRVM_GCALL(reset_cmd)();
1764 Mem_FreePool(&prog->progs_mempool);
1765 memset(prog,0,sizeof(prvm_prog_t));
1766 prog->starttime = Sys_DoubleTime();
1774 void PRVM_LoadLNO( const char *progname ) {
1775 fs_offset_t filesize;
1777 unsigned int *header;
1780 FS_StripExtension( progname, filename, sizeof( filename ) );
1781 strlcat( filename, ".lno", sizeof( filename ) );
1783 lno = FS_LoadFile( filename, tempmempool, false, &filesize );
1789 <Spike> SafeWrite (h, &lnotype, sizeof(int));
1790 <Spike> SafeWrite (h, &version, sizeof(int));
1791 <Spike> SafeWrite (h, &numglobaldefs, sizeof(int));
1792 <Spike> SafeWrite (h, &numpr_globals, sizeof(int));
1793 <Spike> SafeWrite (h, &numfielddefs, sizeof(int));
1794 <Spike> SafeWrite (h, &numstatements, sizeof(int));
1795 <Spike> SafeWrite (h, statement_linenums, numstatements*sizeof(int));
1797 if( (unsigned) filesize < (6 + prog->progs->numstatements) * sizeof( int ) ) {
1802 header = (unsigned int *) lno;
1803 if( header[ 0 ] == *(unsigned int *) "LNOF" &&
1804 LittleLong( header[ 1 ] ) == 1 &&
1805 (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs->numglobaldefs &&
1806 (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs->numglobals &&
1807 (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs->numfielddefs &&
1808 (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs->numstatements )
1810 prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs->numstatements * sizeof( int ) );
1811 memcpy( prog->statement_linenums, (int *) lno + 6, prog->progs->numstatements * sizeof( int ) );
1821 void PRVM_LoadProgs (const char * filename, int numrequiredfunc, char **required_func, int numrequiredfields, prvm_required_field_t *required_field, int numrequiredglobals, char **required_global)
1825 ddef_t *infielddefs;
1826 dfunction_t *dfunctions;
1827 fs_offset_t filesize;
1829 if( prog->loaded ) {
1830 PRVM_ERROR ("PRVM_LoadProgs: there is already a %s program loaded!", PRVM_NAME );
1833 prog->progs = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
1834 if (prog->progs == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
1835 PRVM_ERROR ("PRVM_LoadProgs: couldn't load %s for %s", filename, PRVM_NAME);
1836 // TODO bounds check header fields (e.g. numstatements), they must never go behind end of file
1838 Con_DPrintf("%s programs occupy %iK.\n", PRVM_NAME, (int)(filesize/1024));
1840 prog->filecrc = CRC_Block((unsigned char *)prog->progs, filesize);
1842 // byte swap the header
1843 for (i = 0;i < (int) sizeof(*prog->progs) / 4;i++)
1844 ((int *)prog->progs)[i] = LittleLong ( ((int *)prog->progs)[i] );
1846 if (prog->progs->version != PROG_VERSION)
1847 PRVM_ERROR ("%s: %s has wrong version number (%i should be %i)", PRVM_NAME, filename, prog->progs->version, PROG_VERSION);
1848 if (prog->progs->crc != prog->headercrc && prog->progs->crc != prog->headercrc2)
1849 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);
1851 //prog->functions = (dfunction_t *)((unsigned char *)progs + progs->ofs_functions);
1852 dfunctions = (dfunction_t *)((unsigned char *)prog->progs + prog->progs->ofs_functions);
1854 if (prog->progs->ofs_strings + prog->progs->numstrings >= (int)filesize)
1855 PRVM_ERROR ("%s: %s strings go past end of file", PRVM_NAME, filename);
1856 prog->strings = (char *)prog->progs + prog->progs->ofs_strings;
1857 prog->stringssize = prog->progs->numstrings;
1859 prog->numknownstrings = 0;
1860 prog->maxknownstrings = 0;
1861 prog->knownstrings = NULL;
1862 prog->knownstrings_freeable = NULL;
1864 Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
1866 prog->globaldefs = (ddef_t *)((unsigned char *)prog->progs + prog->progs->ofs_globaldefs);
1868 // we need to expand the fielddefs list to include all the engine fields,
1869 // so allocate a new place for it
1870 infielddefs = (ddef_t *)((unsigned char *)prog->progs + prog->progs->ofs_fielddefs);
1872 prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs->numfielddefs + numrequiredfields) * sizeof(ddef_t));
1874 prog->statements = (dstatement_t *)((unsigned char *)prog->progs + prog->progs->ofs_statements);
1876 prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs->numstatements * sizeof(*prog->statement_profile));
1878 // moved edict_size calculation down below field adding code
1880 //pr_global_struct = (globalvars_t *)((unsigned char *)progs + progs->ofs_globals);
1881 prog->globals.generic = (float *)((unsigned char *)prog->progs + prog->progs->ofs_globals);
1883 // byte swap the lumps
1884 for (i=0 ; i<prog->progs->numstatements ; i++)
1886 prog->statements[i].op = LittleShort(prog->statements[i].op);
1887 prog->statements[i].a = LittleShort(prog->statements[i].a);
1888 prog->statements[i].b = LittleShort(prog->statements[i].b);
1889 prog->statements[i].c = LittleShort(prog->statements[i].c);
1892 prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs->numfunctions);
1893 for (i = 0;i < prog->progs->numfunctions;i++)
1895 prog->functions[i].first_statement = LittleLong (dfunctions[i].first_statement);
1896 prog->functions[i].parm_start = LittleLong (dfunctions[i].parm_start);
1897 prog->functions[i].s_name = LittleLong (dfunctions[i].s_name);
1898 prog->functions[i].s_file = LittleLong (dfunctions[i].s_file);
1899 prog->functions[i].numparms = LittleLong (dfunctions[i].numparms);
1900 prog->functions[i].locals = LittleLong (dfunctions[i].locals);
1901 memcpy(prog->functions[i].parm_size, dfunctions[i].parm_size, sizeof(dfunctions[i].parm_size));
1902 if(prog->functions[i].first_statement >= prog->progs->numstatements)
1903 PRVM_ERROR("PRVM_LoadProgs: out of bounds function statement (function %d) in %s", i, PRVM_NAME);
1904 // TODO bounds check parm_start, s_name, s_file, numparms, locals, parm_size
1907 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
1909 prog->globaldefs[i].type = LittleShort (prog->globaldefs[i].type);
1910 prog->globaldefs[i].ofs = LittleShort (prog->globaldefs[i].ofs);
1911 prog->globaldefs[i].s_name = LittleLong (prog->globaldefs[i].s_name);
1912 // TODO bounds check ofs, s_name
1915 // copy the progs fields to the new fields list
1916 for (i = 0;i < prog->progs->numfielddefs;i++)
1918 prog->fielddefs[i].type = LittleShort (infielddefs[i].type);
1919 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
1920 PRVM_ERROR ("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", PRVM_NAME);
1921 prog->fielddefs[i].ofs = LittleShort (infielddefs[i].ofs);
1922 prog->fielddefs[i].s_name = LittleLong (infielddefs[i].s_name);
1923 // TODO bounds check ofs, s_name
1926 // append the required fields
1927 for (i = 0;i < (int) numrequiredfields;i++)
1929 prog->fielddefs[prog->progs->numfielddefs].type = required_field[i].type;
1930 prog->fielddefs[prog->progs->numfielddefs].ofs = prog->progs->entityfields;
1931 prog->fielddefs[prog->progs->numfielddefs].s_name = PRVM_SetEngineString(required_field[i].name);
1932 // TODO bounds check ofs, s_name
1933 if (prog->fielddefs[prog->progs->numfielddefs].type == ev_vector)
1934 prog->progs->entityfields += 3;
1936 prog->progs->entityfields++;
1937 prog->progs->numfielddefs++;
1940 // check required functions
1941 for(i=0 ; i < numrequiredfunc ; i++)
1942 if(PRVM_ED_FindFunction(required_func[i]) == 0)
1943 PRVM_ERROR("%s: %s not found in %s",PRVM_NAME, required_func[i], filename);
1945 // check required globals
1946 for(i=0 ; i < numrequiredglobals ; i++)
1947 if(PRVM_ED_FindGlobal(required_global[i]) == 0)
1948 PRVM_ERROR("%s: %s not found in %s",PRVM_NAME, required_global[i], filename);
1950 for (i=0 ; i<prog->progs->numglobals ; i++)
1951 ((int *)prog->globals.generic)[i] = LittleLong (((int *)prog->globals.generic)[i]);
1953 // moved edict_size calculation down here, below field adding code
1954 // LordHavoc: this no longer includes the prvm_edict_t header
1955 prog->edict_size = prog->progs->entityfields * 4;
1956 prog->edictareasize = prog->edict_size * prog->limit_edicts;
1958 // LordHavoc: bounds check anything static
1959 for (i = 0,st = prog->statements;i < prog->progs->numstatements;i++,st++)
1965 if ((unsigned short) st->a >= prog->progs->numglobals || st->b + i < 0 || st->b + i >= prog->progs->numstatements)
1966 PRVM_ERROR("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", i, PRVM_NAME);
1969 if (st->a + i < 0 || st->a + i >= prog->progs->numstatements)
1970 PRVM_ERROR("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, PRVM_NAME);
1972 // global global global
2007 if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->b >= prog->progs->numglobals || (unsigned short) st->c >= prog->progs->numglobals)
2008 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
2010 // global none global
2016 if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->c >= prog->progs->numglobals)
2017 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
2033 if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->b >= prog->progs->numglobals)
2034 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
2048 if ((unsigned short) st->a >= prog->progs->numglobals)
2049 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
2052 Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", st->op, i, PRVM_NAME);
2056 if(prog->progs->numstatements < 1)
2058 PRVM_ERROR("PRVM_LoadProgs: empty program in %s", PRVM_NAME);
2060 else switch(prog->statements[prog->progs->numstatements - 1].op)
2067 PRVM_ERROR("PRVM_LoadProgs: program may fall off the edge (does not end with RETURN, GOTO or DONE) in %s", PRVM_NAME);
2071 PRVM_LoadLNO(filename);
2075 prog->loaded = TRUE;
2077 // set flags & ddef_ts in prog
2083 PRVM_GCALL(init_cmd)();
2090 void PRVM_Fields_f (void)
2092 int i, j, ednum, used, usedamount;
2094 char tempstring[MAX_INPUTLINE], tempstring2[260];
2104 Con_Print("no progs loaded\n");
2111 Con_Print("prvm_fields <program name>\n");
2116 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
2119 counts = (int *)Mem_Alloc(tempmempool, prog->progs->numfielddefs * sizeof(int));
2120 for (ednum = 0;ednum < prog->max_edicts;ednum++)
2122 ed = PRVM_EDICT_NUM(ednum);
2123 if (ed->priv.required->free)
2125 for (i = 1;i < prog->progs->numfielddefs;i++)
2127 d = &prog->fielddefs[i];
2128 name = PRVM_GetString(d->s_name);
2129 if (name[strlen(name)-2] == '_')
2130 continue; // skip _x, _y, _z vars
2131 v = (int *)((char *)ed->fields.vp + d->ofs*4);
2132 // if the value is still all 0, skip the field
2133 for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
2146 for (i = 0;i < prog->progs->numfielddefs;i++)
2148 d = &prog->fielddefs[i];
2149 name = PRVM_GetString(d->s_name);
2150 if (name[strlen(name)-2] == '_')
2151 continue; // skip _x, _y, _z vars
2152 switch(d->type & ~DEF_SAVEGLOBAL)
2155 strlcat(tempstring, "string ", sizeof(tempstring));
2158 strlcat(tempstring, "entity ", sizeof(tempstring));
2161 strlcat(tempstring, "function ", sizeof(tempstring));
2164 strlcat(tempstring, "field ", sizeof(tempstring));
2167 strlcat(tempstring, "void ", sizeof(tempstring));
2170 strlcat(tempstring, "float ", sizeof(tempstring));
2173 strlcat(tempstring, "vector ", sizeof(tempstring));
2176 strlcat(tempstring, "pointer ", sizeof(tempstring));
2179 dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
2180 strlcat(tempstring, tempstring2, sizeof(tempstring));
2183 if (strlen(name) > sizeof(tempstring2)-4)
2185 memcpy (tempstring2, name, sizeof(tempstring2)-4);
2186 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
2187 tempstring2[sizeof(tempstring2)-1] = 0;
2190 strlcat(tempstring, name, sizeof(tempstring));
2191 for (j = (int)strlen(name);j < 25;j++)
2192 strlcat(tempstring, " ", sizeof(tempstring));
2193 dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
2194 strlcat(tempstring, tempstring2, sizeof(tempstring));
2195 strlcat(tempstring, "\n", sizeof(tempstring));
2196 if (strlen(tempstring) >= sizeof(tempstring)/2)
2198 Con_Print(tempstring);
2204 usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
2208 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);
2213 void PRVM_Globals_f (void)
2216 const char *wildcard;
2222 Con_Print("no progs loaded\n");
2225 if(Cmd_Argc () < 2 || Cmd_Argc() > 3)
2227 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
2232 if(!PRVM_SetProgFromString (Cmd_Argv (1)))
2235 if( Cmd_Argc() == 3)
2236 wildcard = Cmd_Argv(2);
2240 Con_Printf("%s :", PRVM_NAME);
2242 for (i = 0;i < prog->progs->numglobaldefs;i++)
2245 if( !matchpattern( PRVM_GetString(prog->globaldefs[i].s_name), wildcard, 1) )
2250 Con_Printf("%s\n", PRVM_GetString(prog->globaldefs[i].s_name));
2252 Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->progs->numglobals, numculled, prog->progs->numglobals * 4);
2262 void PRVM_Global_f(void)
2265 if( Cmd_Argc() != 3 ) {
2266 Con_Printf( "prvm_global <program name> <global name>\n" );
2271 if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
2274 global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
2276 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2278 Con_Printf( "%s: %s\n", Cmd_Argv(2), PRVM_ValueString( (etype_t)global->type, (prvm_eval_t *) &prog->globals.generic[ global->ofs ] ) );
2287 void PRVM_GlobalSet_f(void)
2290 if( Cmd_Argc() != 4 ) {
2291 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2296 if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
2299 global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
2301 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2303 PRVM_ED_ParseEpair( NULL, global, Cmd_Argv(3), true );
2312 void PRVM_Init (void)
2314 Cmd_AddCommand ("prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2315 Cmd_AddCommand ("prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2316 Cmd_AddCommand ("prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2317 Cmd_AddCommand ("prvm_profile", PRVM_Profile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu)");
2318 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");
2319 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)");
2320 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)");
2321 Cmd_AddCommand ("prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2322 Cmd_AddCommand ("prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2323 Cmd_AddCommand ("prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2324 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)");
2325 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");
2326 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");
2327 Cmd_AddCommand ("prvm_printfunction", PRVM_PrintFunction_f, "prints a disassembly (QuakeC instructions) of the specified function in the selected VM (server, client, menu)");
2328 Cmd_AddCommand ("cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2329 Cmd_AddCommand ("menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2330 Cmd_AddCommand ("sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2332 // COMMANDLINEOPTION: PRVM: -noboundscheck disables the bounds checks (security hole if CSQC is in use!)
2333 prvm_boundscheck = !COM_CheckParm("-noboundscheck");
2335 Cvar_RegisterVariable (&prvm_traceqc);
2336 Cvar_RegisterVariable (&prvm_statementprofiling);
2337 Cvar_RegisterVariable (&prvm_backtraceforwarnings);
2338 Cvar_RegisterVariable (&prvm_leaktest);
2339 Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames);
2340 Cvar_RegisterVariable (&prvm_errordump);
2341 Cvar_RegisterVariable (&prvm_reuseedicts_startuptime);
2342 Cvar_RegisterVariable (&prvm_reuseedicts_neverinsameframe);
2344 // COMMANDLINEOPTION: PRVM: -norunaway disables the runaway loop check (it might be impossible to exit DarkPlaces if used!)
2345 prvm_runawaycheck = !COM_CheckParm("-norunaway");
2355 void PRVM_InitProg(int prognr)
2357 if(prognr < 0 || prognr >= PRVM_MAXPROGS)
2358 Sys_Error("PRVM_InitProg: Invalid program number %i",prognr);
2360 prog = &prog_list[prognr];
2365 memset(prog, 0, sizeof(prvm_prog_t));
2366 prog->starttime = Sys_DoubleTime();
2368 prog->error_cmd = Host_Error;
2369 prog->leaktest_active = prvm_leaktest.integer != 0;
2372 int PRVM_GetProgNr(void)
2374 return prog - prog_list;
2377 void *_PRVM_Alloc(size_t buffersize, const char *filename, int fileline)
2379 return _Mem_Alloc(prog->progs_mempool, buffersize, filename, fileline);
2382 void _PRVM_Free(void *buffer, const char *filename, int fileline)
2384 _Mem_Free(buffer, filename, fileline);
2387 void _PRVM_FreeAll(const char *filename, int fileline)
2390 prog->fielddefs = NULL;
2391 prog->functions = NULL;
2392 _Mem_EmptyPool(prog->progs_mempool, filename, fileline);
2395 // LordHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
2396 unsigned int PRVM_EDICT_NUM_ERROR(unsigned int n, char *filename, int fileline)
2398 PRVM_ERROR ("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", PRVM_NAME, n, filename, fileline);
2403 int NUM_FOR_EDICT_ERROR(prvm_edict_t *e)
2405 PRVM_ERROR ("PRVM_NUM_FOR_EDICT: bad pointer %p (world is %p, entity number would be %i)", e, prog->edicts, e - prog->edicts);
2409 int PRVM_NUM_FOR_EDICT(prvm_edict_t *e)
2412 n = e - prog->edicts;
2413 if ((unsigned int)n >= prog->limit_edicts)
2414 Host_Error ("PRVM_NUM_FOR_EDICT: bad pointer");
2418 //int NoCrash_NUM_FOR_EDICT(prvm_edict_t *e)
2420 // return e - prog->edicts;
2423 //#define PRVM_EDICT_TO_PROG(e) ((unsigned char *)(((prvm_edict_t *)e)->v) - (unsigned char *)(prog->edictsfields))
2424 //#define PRVM_PROG_TO_EDICT(e) (prog->edicts + ((e) / (progs->entityfields * 4)))
2425 int PRVM_EDICT_TO_PROG(prvm_edict_t *e)
2428 n = e - prog->edicts;
2429 if ((unsigned int)n >= (unsigned int)prog->max_edicts)
2430 Host_Error("PRVM_EDICT_TO_PROG: invalid edict %8p (number %i compared to world at %8p)", e, n, prog->edicts);
2431 return n;// EXPERIMENTAL
2432 //return (unsigned char *)e->v - (unsigned char *)prog->edictsfields;
2434 prvm_edict_t *PRVM_PROG_TO_EDICT(int n)
2436 if ((unsigned int)n >= (unsigned int)prog->max_edicts)
2437 Host_Error("PRVM_PROG_TO_EDICT: invalid edict number %i", n);
2438 return prog->edicts + n; // EXPERIMENTAL
2439 //return prog->edicts + ((n) / (progs->entityfields * 4));
2444 sizebuf_t vm_tempstringsbuf;
2446 const char *PRVM_GetString(int num)
2450 if (num < prog->stringssize)
2451 return prog->strings + num;
2454 if (num <= prog->stringssize + vm_tempstringsbuf.maxsize)
2456 num -= prog->stringssize;
2457 if (num < vm_tempstringsbuf.cursize)
2458 return (char *)vm_tempstringsbuf.data + num;
2461 VM_Warning("PRVM_GetString: Invalid temp-string offset (%i >= %i vm_tempstringsbuf.cursize)\n", num, vm_tempstringsbuf.cursize);
2468 VM_Warning("PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
2478 // special range reserved for tempstrings
2480 if (num < vm_tempstringsbuf.cursize)
2481 return (char *)vm_tempstringsbuf.data + num;
2484 VM_Warning("PRVM_GetString: Invalid temp-string offset (%i >= %i vm_tempstringsbuf.cursize)\n", num, vm_tempstringsbuf.cursize);
2490 if (num < prog->numknownstrings)
2492 if (!prog->knownstrings[num])
2494 VM_Warning("PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
2497 return prog->knownstrings[num];
2501 VM_Warning("PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
2507 int PRVM_SetEngineString(const char *s)
2512 if (s >= prog->strings && s <= prog->strings + prog->stringssize)
2513 PRVM_ERROR("PRVM_SetEngineString: s in prog->strings area");
2514 // if it's in the tempstrings area, use a reserved range
2515 // (otherwise we'd get millions of useless string offsets cluttering the database)
2516 if (s >= (char *)vm_tempstringsbuf.data && s < (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.maxsize)
2518 return prog->stringssize + (s - (char *)vm_tempstringsbuf.data);
2520 return -1 - ((1<<30) + (s - (char *)vm_tempstringsbuf.data));
2522 // see if it's a known string address
2523 for (i = 0;i < prog->numknownstrings;i++)
2524 if (prog->knownstrings[i] == s)
2526 // new unknown engine string
2527 if (developer.integer >= 200)
2528 Con_Printf("new engine string %p = \"%s\"\n", s, s);
2529 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2530 if (!prog->knownstrings[i])
2532 if (i >= prog->numknownstrings)
2534 if (i >= prog->maxknownstrings)
2536 const char **oldstrings = prog->knownstrings;
2537 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2538 const char **oldstrings_origin = prog->knownstrings_origin;
2539 prog->maxknownstrings += 128;
2540 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2541 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2542 if(prog->leaktest_active)
2543 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2544 if (prog->numknownstrings)
2546 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2547 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2548 if(prog->leaktest_active)
2549 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
2552 prog->numknownstrings++;
2554 prog->firstfreeknownstring = i + 1;
2555 prog->knownstrings[i] = s;
2556 prog->knownstrings_freeable[i] = false;
2557 if(prog->leaktest_active)
2558 prog->knownstrings_origin[i] = NULL;
2562 // temp string handling
2564 // all tempstrings go into this buffer consecutively, and it is reset
2565 // whenever PRVM_ExecuteProgram returns to the engine
2566 // (technically each PRVM_ExecuteProgram call saves the cursize value and
2567 // restores it on return, so multiple recursive calls can share the same
2569 // the buffer size is automatically grown as needed
2571 int PRVM_SetTempString(const char *s)
2577 size = (int)strlen(s) + 1;
2578 if (developer.integer >= 300)
2579 Con_Printf("PRVM_SetTempString: cursize %i, size %i\n", vm_tempstringsbuf.cursize, size);
2580 if (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
2582 sizebuf_t old = vm_tempstringsbuf;
2583 if (vm_tempstringsbuf.cursize + size >= 1<<28)
2584 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);
2585 vm_tempstringsbuf.maxsize = max(vm_tempstringsbuf.maxsize, 65536);
2586 while (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
2587 vm_tempstringsbuf.maxsize *= 2;
2588 if (vm_tempstringsbuf.maxsize != old.maxsize || vm_tempstringsbuf.data == NULL)
2590 if (developer.integer >= 100)
2591 Con_Printf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, vm_tempstringsbuf.maxsize/1024);
2592 vm_tempstringsbuf.data = (unsigned char *) Mem_Alloc(sv_mempool, vm_tempstringsbuf.maxsize);
2594 memcpy(vm_tempstringsbuf.data, old.data, old.cursize);
2599 t = (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.cursize;
2601 vm_tempstringsbuf.cursize += size;
2602 return PRVM_SetEngineString(t);
2605 int PRVM_AllocString(size_t bufferlength, char **pointer)
2610 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2611 if (!prog->knownstrings[i])
2613 if (i >= prog->numknownstrings)
2615 if (i >= prog->maxknownstrings)
2617 const char **oldstrings = prog->knownstrings;
2618 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2619 const char **oldstrings_origin = prog->knownstrings_origin;
2620 prog->maxknownstrings += 128;
2621 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2622 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2623 if(prog->leaktest_active)
2624 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2625 if (prog->numknownstrings)
2627 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2628 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2629 if(prog->leaktest_active)
2630 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
2632 // TODO why not Mem_Free the old ones?
2634 prog->numknownstrings++;
2636 prog->firstfreeknownstring = i + 1;
2637 prog->knownstrings[i] = (char *)PRVM_Alloc(bufferlength);
2638 prog->knownstrings_freeable[i] = true;
2639 if(prog->leaktest_active)
2640 prog->knownstrings_origin[i] = PRVM_AllocationOrigin();
2642 *pointer = (char *)(prog->knownstrings[i]);
2646 void PRVM_FreeString(int num)
2649 PRVM_ERROR("PRVM_FreeString: attempt to free a NULL string");
2650 else if (num >= 0 && num < prog->stringssize)
2651 PRVM_ERROR("PRVM_FreeString: attempt to free a constant string");
2652 else if (num < 0 && num >= -prog->numknownstrings)
2655 if (!prog->knownstrings[num])
2656 PRVM_ERROR("PRVM_FreeString: attempt to free a non-existent or already freed string");
2657 if (!prog->knownstrings_freeable[num])
2658 PRVM_ERROR("PRVM_FreeString: attempt to free a string owned by the engine");
2659 PRVM_Free((char *)prog->knownstrings[num]);
2660 if(prog->leaktest_active)
2661 if(prog->knownstrings_origin[num])
2662 PRVM_Free((char *)prog->knownstrings_origin[num]);
2663 prog->knownstrings[num] = NULL;
2664 prog->knownstrings_freeable[num] = false;
2665 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
2668 PRVM_ERROR("PRVM_FreeString: invalid string offset %i", num);
2671 static qboolean PRVM_IsStringReferenced(string_t string)
2675 for (i = 0;i < prog->progs->numglobaldefs;i++)
2677 ddef_t *d = &prog->globaldefs[i];
2678 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
2680 if(string == ((prvm_eval_t *) &prog->globals.generic[d->ofs])->string)
2684 for(j = 0; j < prog->num_edicts; ++j)
2686 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2687 if (ed->priv.required->free)
2689 for (i=0; i<prog->progs->numfielddefs; ++i)
2691 ddef_t *d = &prog->fielddefs[i];
2692 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
2694 if(string == ((prvm_eval_t *) &((float*)ed->fields.vp)[d->ofs])->string)
2702 static qboolean PRVM_IsEdictRelevant(prvm_edict_t *edict)
2704 if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
2705 return true; // world or clients
2706 switch(prog - prog_list)
2708 case PRVM_SERVERPROG:
2710 entvars_t *ev = edict->fields.server;
2711 if(ev->solid) // can block other stuff, or is a trigger?
2713 if(ev->modelindex) // visible ent?
2715 if(ev->effects) // particle effect?
2717 if(ev->think) // has a think function?
2718 if(ev->nextthink > 0) // that actually will eventually run?
2722 if(*prvm_leaktest_ignore_classnames.string)
2724 if(strstr(va(" %s ", prvm_leaktest_ignore_classnames.string), va(" %s ", PRVM_GetString(ev->classname))))
2729 case PRVM_CLIENTPROG:
2731 // TODO someone add more stuff here
2732 cl_entvars_t *ev = edict->fields.client;
2733 if(ev->entnum) // csqc networked
2735 if(ev->modelindex) // visible ent?
2737 if(ev->effects) // particle effect?
2739 if(ev->think) // has a think function?
2740 if(ev->nextthink > 0) // that actually will eventually run?
2742 if(*prvm_leaktest_ignore_classnames.string)
2744 if(strstr(va(" %s ", prvm_leaktest_ignore_classnames.string), va(" %s ", PRVM_GetString(ev->classname))))
2750 // menu prog does not have classnames
2756 static qboolean PRVM_IsEdictReferenced(prvm_edict_t *edict, int mark)
2759 int edictnum = PRVM_NUM_FOR_EDICT(edict);
2760 const char *targetname = NULL;
2762 switch(prog - prog_list)
2764 case PRVM_SERVERPROG:
2765 targetname = PRVM_GetString(edict->fields.server->targetname);
2770 if(!*targetname) // ""
2773 for (i = 0;i < prog->progs->numglobaldefs;i++)
2775 ddef_t *d = &prog->globaldefs[i];
2776 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
2778 if(edictnum == ((prvm_eval_t *) &prog->globals.generic[d->ofs])->edict)
2782 for(j = 0; j < prog->num_edicts; ++j)
2784 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2785 if (ed->priv.required->mark < mark)
2791 const char *target = PRVM_GetString(ed->fields.server->target);
2793 if(!strcmp(target, targetname))
2796 for (i=0; i<prog->progs->numfielddefs; ++i)
2798 ddef_t *d = &prog->fielddefs[i];
2799 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
2801 if(edictnum == ((prvm_eval_t *) &((float*)ed->fields.vp)[d->ofs])->edict)
2809 static void PRVM_MarkReferencedEdicts(void)
2815 for(j = 0; j < prog->num_edicts; ++j)
2817 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2818 if(ed->priv.required->free)
2820 ed->priv.required->mark = PRVM_IsEdictRelevant(ed) ? 1 : 0;
2827 for(j = 0; j < prog->num_edicts; ++j)
2829 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2830 if(ed->priv.required->free)
2832 if(ed->priv.required->mark)
2834 if(PRVM_IsEdictReferenced(ed, stage))
2836 ed->priv.required->mark = stage + 1;
2843 Con_DPrintf("leak check used %d stages to find all references\n", stage);
2846 void PRVM_LeakTest(void)
2849 qboolean leaked = false;
2851 if(!prog->leaktest_active)
2855 for (i = 0; i < prog->numknownstrings; ++i)
2857 if(prog->knownstrings[i])
2858 if(prog->knownstrings_freeable[i])
2859 if(prog->knownstrings_origin[i])
2860 if(!PRVM_IsStringReferenced(-1 - i))
2862 Con_Printf("Unreferenced string found!\n Value: %s\n Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
2868 PRVM_MarkReferencedEdicts();
2869 for(j = 0; j < prog->num_edicts; ++j)
2871 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2872 if(ed->priv.required->free)
2874 if(!ed->priv.required->mark)
2875 if(ed->priv.required->allocation_origin)
2877 Con_Printf("Unreferenced edict found!\n Allocated at: %s\n", ed->priv.required->allocation_origin);
2878 PRVM_ED_Print(ed, NULL);
2884 for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
2886 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
2888 if(stringbuffer->origin)
2890 Con_Printf("Open string buffer handle found!\n Allocated at: %s\n", stringbuffer->origin);
2895 for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
2897 if(prog->openfiles[i])
2898 if(prog->openfiles_origin[i])
2900 Con_Printf("Open file handle found!\n Allocated at: %s\n", prog->openfiles_origin[i]);
2905 for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
2907 if(prog->opensearches[i])
2908 if(prog->opensearches_origin[i])
2910 Con_Printf("Open search handle found!\n Allocated at: %s\n", prog->opensearches_origin[i]);
2916 Con_Printf("Congratulations. No leaks found.\n");