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"};
43 qboolean prvm_runawaycheck = true;
45 // LordHavoc: optional runtime bounds checking (speed drain, but worth it for security, on by default - breaks most QCCX features (used by CRMod and others))
46 // 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)
47 qboolean prvm_boundscheck = true;
49 extern sizebuf_t vm_tempstringsbuf;
51 //============================================================================
59 void PRVM_MEM_Alloc(void)
63 // reserve space for the null entity aka world
64 // check bound of max_edicts
65 prog->max_edicts = bound(1 + prog->reserved_edicts, prog->max_edicts, prog->limit_edicts);
66 prog->num_edicts = bound(1 + prog->reserved_edicts, prog->num_edicts, prog->max_edicts);
68 // edictprivate_size has to be min as big prvm_edict_private_t
69 prog->edictprivate_size = max(prog->edictprivate_size,(int)sizeof(prvm_edict_private_t));
72 prog->edicts = (prvm_edict_t *)Mem_Alloc(prog->progs_mempool,prog->limit_edicts * sizeof(prvm_edict_t));
74 // alloc edict private space
75 prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size);
78 prog->edictsfields = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edict_size);
81 for(i = 0; i < prog->max_edicts; i++)
83 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
84 prog->edicts[i].fields.vp = (void*)((unsigned char *)prog->edictsfields + i * prog->edict_size);
90 PRVM_MEM_IncreaseEdicts
93 void PRVM_MEM_IncreaseEdicts(void)
96 int oldmaxedicts = prog->max_edicts;
97 void *oldedictsfields = prog->edictsfields;
98 void *oldedictprivate = prog->edictprivate;
100 if(prog->max_edicts >= prog->limit_edicts)
103 PRVM_GCALL(begin_increase_edicts)();
106 prog->max_edicts = min(prog->max_edicts + 256, prog->limit_edicts);
108 prog->edictsfields = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edict_size);
109 prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size);
111 memcpy(prog->edictsfields, oldedictsfields, oldmaxedicts * prog->edict_size);
112 memcpy(prog->edictprivate, oldedictprivate, oldmaxedicts * prog->edictprivate_size);
114 //set e and v pointers
115 for(i = 0; i < prog->max_edicts; i++)
117 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
118 prog->edicts[i].fields.vp = (void*)((unsigned char *)prog->edictsfields + i * prog->edict_size);
121 PRVM_GCALL(end_increase_edicts)();
123 Mem_Free(oldedictsfields);
124 Mem_Free(oldedictprivate);
127 //============================================================================
130 int PRVM_ED_FindFieldOffset(const char *field)
133 d = PRVM_ED_FindField(field);
139 int PRVM_ED_FindGlobalOffset(const char *global)
142 d = PRVM_ED_FindGlobal(global);
148 func_t PRVM_ED_FindFunctionOffset(const char *function)
151 f = PRVM_ED_FindFunction(function);
154 return (func_t)(f - prog->functions);
157 qboolean PRVM_ProgLoaded(int prognr)
159 if(prognr < 0 || prognr >= PRVM_MAXPROGS)
162 return (prog_list[prognr].loaded ? TRUE : FALSE);
167 PRVM_SetProgFromString
170 // perhaps add a return value when the str doesnt exist
171 qboolean PRVM_SetProgFromString(const char *str)
174 for(; i < PRVM_MAXPROGS ; i++)
175 if(prog_list[i].name && !strcmp(prog_list[i].name,str))
177 if(prog_list[i].loaded)
179 prog = &prog_list[i];
184 Con_Printf("%s not loaded !\n",PRVM_NAME);
189 Con_Printf("Invalid program name %s !\n", str);
198 void PRVM_SetProg(int prognr)
200 if(0 <= prognr && prognr < PRVM_MAXPROGS)
202 if(prog_list[prognr].loaded)
203 prog = &prog_list[prognr];
205 PRVM_ERROR("%i not loaded !", prognr);
208 PRVM_ERROR("Invalid program number %i", prognr);
215 Sets everything to NULL
218 void PRVM_ED_ClearEdict (prvm_edict_t *e)
220 memset (e->fields.vp, 0, prog->progs->entityfields * 4);
221 e->priv.required->free = false;
223 // AK: Let the init_edict function determine if something needs to be initialized
224 PRVM_GCALL(init_edict)(e);
227 const char *PRVM_AllocationOrigin(void)
230 if(prog->leaktest_active)
231 if(prog->depth > 0) // actually in QC code and not just parsing the entities block of a map/savegame
233 buf = (char *)PRVM_Alloc(128);
234 PRVM_ShortStackTrace(buf, 128);
243 Returns if this particular edict could get allocated by PRVM_ED_Alloc
246 qboolean PRVM_ED_CanAlloc(prvm_edict_t *e)
248 if(!e->priv.required->free)
250 if(e->priv.required->freetime < prog->starttime + 2)
252 if(realtime > e->priv.required->freetime + 1)
254 return false; // entity slot still blocked because the entity was freed less than one second ago
261 Either finds a free edict, or allocates a new one.
262 Try to avoid reusing an entity that was recently freed, because it
263 can cause the client to think the entity morphed into something else
264 instead of being removed and recreated, which can cause interpolated
265 angles and bad trails.
268 prvm_edict_t *PRVM_ED_Alloc (void)
273 // the client qc dont need maxclients
274 // thus it doesnt need to use svs.maxclients
275 // AK: changed i=svs.maxclients+1
276 // AK: changed so the edict 0 wont spawn -> used as reserved/world entity
277 // although the menu/client has no world
278 for (i = prog->reserved_edicts + 1;i < prog->num_edicts;i++)
280 e = PRVM_EDICT_NUM(i);
281 if(PRVM_ED_CanAlloc(e))
283 PRVM_ED_ClearEdict (e);
284 e->priv.required->allocation_origin = PRVM_AllocationOrigin();
289 if (i == prog->limit_edicts)
290 PRVM_ERROR ("%s: PRVM_ED_Alloc: no free edicts",PRVM_NAME);
293 if (prog->num_edicts >= prog->max_edicts)
294 PRVM_MEM_IncreaseEdicts();
296 e = PRVM_EDICT_NUM(i);
297 PRVM_ED_ClearEdict (e);
299 e->priv.required->allocation_origin = PRVM_AllocationOrigin();
308 Marks the edict as free
309 FIXME: walk all entities and NULL out references to this entity
312 void PRVM_ED_Free (prvm_edict_t *ed)
314 // dont delete the null entity (world) or reserved edicts
315 if(PRVM_NUM_FOR_EDICT(ed) <= prog->reserved_edicts )
318 PRVM_GCALL(free_edict)(ed);
320 ed->priv.required->free = true;
321 ed->priv.required->freetime = realtime;
322 if(ed->priv.required->allocation_origin)
324 PRVM_Free((char *)ed->priv.required->allocation_origin);
325 ed->priv.required->allocation_origin = NULL;
329 //===========================================================================
336 ddef_t *PRVM_ED_GlobalAtOfs (int ofs)
341 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
343 def = &prog->globaldefs[i];
355 ddef_t *PRVM_ED_FieldAtOfs (int ofs)
360 for (i=0 ; i<prog->progs->numfielddefs ; i++)
362 def = &prog->fielddefs[i];
374 ddef_t *PRVM_ED_FindField (const char *name)
379 for (i=0 ; i<prog->progs->numfielddefs ; i++)
381 def = &prog->fielddefs[i];
382 if (!strcmp(PRVM_GetString(def->s_name), name))
393 ddef_t *PRVM_ED_FindGlobal (const char *name)
398 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
400 def = &prog->globaldefs[i];
401 if (!strcmp(PRVM_GetString(def->s_name), name))
413 mfunction_t *PRVM_ED_FindFunction (const char *name)
418 for (i=0 ; i<prog->progs->numfunctions ; i++)
420 func = &prog->functions[i];
421 if (!strcmp(PRVM_GetString(func->s_name), name))
432 Returns a string describing *data in a type specific manner
435 char *PRVM_ValueString (etype_t type, prvm_eval_t *val)
437 static char line[MAX_INPUTLINE];
442 type = (etype_t)((int) type & ~DEF_SAVEGLOBAL);
447 strlcpy (line, PRVM_GetString (val->string), sizeof (line));
451 if (n < 0 || n >= prog->limit_edicts)
452 dpsnprintf (line, sizeof(line), "entity %i (invalid!)", n);
454 dpsnprintf (line, sizeof(line), "entity %i", n);
457 f = prog->functions + val->function;
458 dpsnprintf (line, sizeof(line), "%s()", PRVM_GetString(f->s_name));
461 def = PRVM_ED_FieldAtOfs ( val->_int );
462 dpsnprintf (line, sizeof(line), ".%s", PRVM_GetString(def->s_name));
465 dpsnprintf (line, sizeof(line), "void");
468 // LordHavoc: changed from %5.1f to %10.4f
469 dpsnprintf (line, sizeof(line), "%10.4f", val->_float);
472 // LordHavoc: changed from %5.1f to %10.4f
473 dpsnprintf (line, sizeof(line), "'%10.4f %10.4f %10.4f'", val->vector[0], val->vector[1], val->vector[2]);
476 dpsnprintf (line, sizeof(line), "pointer");
479 dpsnprintf (line, sizeof(line), "bad type %i", (int) type);
490 Returns a string describing *data in a type specific manner
491 Easier to parse than PR_ValueString
494 char *PRVM_UglyValueString (etype_t type, prvm_eval_t *val)
496 static char line[MAX_INPUTLINE];
502 type = (etype_t)((int)type & ~DEF_SAVEGLOBAL);
507 // Parse the string a bit to turn special characters
508 // (like newline, specifically) into escape codes,
509 // this fixes saving games from various mods
510 s = PRVM_GetString (val->string);
511 for (i = 0;i < (int)sizeof(line) - 2 && *s;)
540 dpsnprintf (line, sizeof (line), "%i", PRVM_NUM_FOR_EDICT(PRVM_PROG_TO_EDICT(val->edict)));
543 f = prog->functions + val->function;
544 strlcpy (line, PRVM_GetString (f->s_name), sizeof (line));
547 def = PRVM_ED_FieldAtOfs ( val->_int );
548 dpsnprintf (line, sizeof (line), ".%s", PRVM_GetString(def->s_name));
551 dpsnprintf (line, sizeof (line), "void");
554 dpsnprintf (line, sizeof (line), "%f", val->_float);
557 dpsnprintf (line, sizeof (line), "%f %f %f", val->vector[0], val->vector[1], val->vector[2]);
560 dpsnprintf (line, sizeof (line), "bad type %i", type);
571 Returns a string with a description and the contents of a global,
572 padded to 20 field width
575 char *PRVM_GlobalString (int ofs)
581 static char line[128];
583 val = (void *)&prog->globals.generic[ofs];
584 def = PRVM_ED_GlobalAtOfs(ofs);
586 dpsnprintf (line, sizeof(line), "GLOBAL%i", ofs);
589 s = PRVM_ValueString ((etype_t)def->type, (prvm_eval_t *)val);
590 dpsnprintf (line, sizeof(line), "%s (=%s)", PRVM_GetString(def->s_name), s);
594 //for ( ; i<20 ; i++)
595 // strcat (line," ");
601 char *PRVM_GlobalStringNoContents (int ofs)
605 static char line[128];
607 def = PRVM_ED_GlobalAtOfs(ofs);
609 dpsnprintf (line, sizeof(line), "GLOBAL%i", ofs);
611 dpsnprintf (line, sizeof(line), "%s", PRVM_GetString(def->s_name));
614 //for ( ; i<20 ; i++)
615 // strcat (line," ");
629 // LordHavoc: optimized this to print out much more quickly (tempstring)
630 // LordHavoc: changed to print out every 4096 characters (incase there are a lot of fields to print)
631 void PRVM_ED_Print(prvm_edict_t *ed, const char *wildcard_fieldname)
639 char tempstring[MAX_INPUTLINE], tempstring2[260]; // temporary string buffers
641 if (ed->priv.required->free)
643 Con_Printf("%s: FREE\n",PRVM_NAME);
648 dpsnprintf(tempstring, sizeof(tempstring), "\n%s EDICT %i:\n", PRVM_NAME, PRVM_NUM_FOR_EDICT(ed));
649 for (i=1 ; i<prog->progs->numfielddefs ; i++)
651 d = &prog->fielddefs[i];
652 name = PRVM_GetString(d->s_name);
653 if (name[strlen(name)-2] == '_')
654 continue; // skip _x, _y, _z vars
656 // Check Field Name Wildcard
657 if(wildcard_fieldname)
658 if( !matchpattern(name, wildcard_fieldname, 1) )
659 // Didn't match; skip
662 v = (int *)((char *)ed->fields.vp + d->ofs*4);
664 // if the value is still all 0, skip the field
665 type = d->type & ~DEF_SAVEGLOBAL;
667 for (j=0 ; j<prvm_type_size[type] ; j++)
670 if (j == prvm_type_size[type])
673 if (strlen(name) > sizeof(tempstring2)-4)
675 memcpy (tempstring2, name, sizeof(tempstring2)-4);
676 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
677 tempstring2[sizeof(tempstring2)-1] = 0;
680 strlcat(tempstring, name, sizeof(tempstring));
681 for (l = strlen(name);l < 14;l++)
682 strlcat(tempstring, " ", sizeof(tempstring));
683 strlcat(tempstring, " ", sizeof(tempstring));
685 name = PRVM_ValueString((etype_t)d->type, (prvm_eval_t *)v);
686 if (strlen(name) > sizeof(tempstring2)-4)
688 memcpy (tempstring2, name, sizeof(tempstring2)-4);
689 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
690 tempstring2[sizeof(tempstring2)-1] = 0;
693 strlcat(tempstring, name, sizeof(tempstring));
694 strlcat(tempstring, "\n", sizeof(tempstring));
695 if (strlen(tempstring) >= sizeof(tempstring)/2)
697 Con_Print(tempstring);
702 Con_Print(tempstring);
712 extern cvar_t developer_entityparsing;
713 void PRVM_ED_Write (qfile_t *f, prvm_edict_t *ed)
723 if (ed->priv.required->free)
729 for (i=1 ; i<prog->progs->numfielddefs ; i++)
731 d = &prog->fielddefs[i];
732 name = PRVM_GetString(d->s_name);
734 if(developer_entityparsing.integer)
735 Con_Printf("PRVM_ED_Write: at entity %d field %s\n", PRVM_NUM_FOR_EDICT(ed), name);
737 if (name[strlen(name)-2] == '_')
738 continue; // skip _x, _y, _z vars
740 v = (int *)((char *)ed->fields.vp + d->ofs*4);
742 // if the value is still all 0, skip the field
743 type = d->type & ~DEF_SAVEGLOBAL;
744 for (j=0 ; j<prvm_type_size[type] ; j++)
747 if (j == prvm_type_size[type])
750 FS_Printf(f,"\"%s\" ",name);
751 prog->statestring = va("PRVM_ED_Write, ent=%d, name=%s", i, name);
752 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString((etype_t)d->type, (prvm_eval_t *)v));
753 prog->statestring = NULL;
759 void PRVM_ED_PrintNum (int ent, const char *wildcard_fieldname)
761 PRVM_ED_Print(PRVM_EDICT_NUM(ent), wildcard_fieldname);
766 PRVM_ED_PrintEdicts_f
768 For debugging, prints all the entities in the current server
771 void PRVM_ED_PrintEdicts_f (void)
774 const char *wildcard_fieldname;
776 if(Cmd_Argc() < 2 || Cmd_Argc() > 3)
778 Con_Print("prvm_edicts <program name> <optional field name wildcard>\n");
783 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
787 wildcard_fieldname = Cmd_Argv(2);
789 wildcard_fieldname = NULL;
791 Con_Printf("%s: %i entities\n", PRVM_NAME, prog->num_edicts);
792 for (i=0 ; i<prog->num_edicts ; i++)
793 PRVM_ED_PrintNum (i, wildcard_fieldname);
802 For debugging, prints a single edict
805 void PRVM_ED_PrintEdict_f (void)
808 const char *wildcard_fieldname;
810 if(Cmd_Argc() < 3 || Cmd_Argc() > 4)
812 Con_Print("prvm_edict <program name> <edict number> <optional field name wildcard>\n");
817 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
820 i = atoi (Cmd_Argv(2));
821 if (i >= prog->num_edicts)
823 Con_Print("Bad edict number\n");
828 // Optional Wildcard Provided
829 wildcard_fieldname = Cmd_Argv(3);
832 wildcard_fieldname = NULL;
833 PRVM_ED_PrintNum (i, wildcard_fieldname);
845 // 2 possibilities : 1. just displaying the active edict count
846 // 2. making a function pointer [x]
847 void PRVM_ED_Count_f (void)
855 Con_Print("prvm_count <program name>\n");
860 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
863 if(prog->count_edicts)
864 prog->count_edicts();
868 for (i=0 ; i<prog->num_edicts ; i++)
870 ent = PRVM_EDICT_NUM(i);
871 if (ent->priv.required->free)
876 Con_Printf("num_edicts:%3i\n", prog->num_edicts);
877 Con_Printf("active :%3i\n", active);
884 ==============================================================================
888 FIXME: need to tag constants, doesn't really work
889 ==============================================================================
897 void PRVM_ED_WriteGlobals (qfile_t *f)
905 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
907 def = &prog->globaldefs[i];
909 if ( !(def->type & DEF_SAVEGLOBAL) )
911 type &= ~DEF_SAVEGLOBAL;
913 if (type != ev_string && type != ev_float && type != ev_entity)
916 name = PRVM_GetString(def->s_name);
918 if(developer_entityparsing.integer)
919 Con_Printf("PRVM_ED_WriteGlobals: at global %s\n", name);
921 prog->statestring = va("PRVM_ED_WriteGlobals, name=%s", name);
922 FS_Printf(f,"\"%s\" ", name);
923 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString((etype_t)type, (prvm_eval_t *)&prog->globals.generic[def->ofs]));
924 prog->statestring = NULL;
934 void PRVM_ED_ParseGlobals (const char *data)
936 char keyname[MAX_INPUTLINE];
942 if (!COM_ParseToken_Simple(&data, false, false))
943 PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace");
944 if (com_token[0] == '}')
947 if (developer_entityparsing.integer)
948 Con_Printf("Key: \"%s\"", com_token);
950 strlcpy (keyname, com_token, sizeof(keyname));
953 if (!COM_ParseToken_Simple(&data, false, true))
954 PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace");
956 if (developer_entityparsing.integer)
957 Con_Printf(" \"%s\"\n", com_token);
959 if (com_token[0] == '}')
960 PRVM_ERROR ("PRVM_ED_ParseGlobals: closing brace without data");
962 key = PRVM_ED_FindGlobal (keyname);
965 Con_DPrintf("'%s' is not a global on %s\n", keyname, PRVM_NAME);
969 if (!PRVM_ED_ParseEpair(NULL, key, com_token, true))
970 PRVM_ERROR ("PRVM_ED_ParseGlobals: parse error");
974 //============================================================================
981 Can parse either fields or globals
982 returns false if error
985 qboolean PRVM_ED_ParseEpair(prvm_edict_t *ent, ddef_t *key, const char *s, qboolean parsebackslash)
994 val = (prvm_eval_t *)((int *)ent->fields.vp + key->ofs);
996 val = (prvm_eval_t *)((int *)prog->globals.generic + key->ofs);
997 switch (key->type & ~DEF_SAVEGLOBAL)
1000 l = (int)strlen(s) + 1;
1001 val->string = PRVM_AllocString(l, &new_p);
1002 for (i = 0;i < l;i++)
1004 if (s[i] == '\\' && s[i+1] && parsebackslash)
1009 else if (s[i] == 'r')
1020 while (*s && ISWHITESPACE(*s))
1022 val->_float = atof(s);
1026 for (i = 0;i < 3;i++)
1028 while (*s && ISWHITESPACE(*s))
1032 val->vector[i] = atof(s);
1033 while (!ISWHITESPACE(*s))
1041 while (*s && ISWHITESPACE(*s))
1044 if (i >= prog->limit_edicts)
1045 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);
1046 while (i >= prog->max_edicts)
1047 PRVM_MEM_IncreaseEdicts();
1048 // if IncreaseEdicts was called the base pointer needs to be updated
1050 val = (prvm_eval_t *)((int *)ent->fields.vp + key->ofs);
1051 val->edict = PRVM_EDICT_TO_PROG(PRVM_EDICT_NUM((int)i));
1057 Con_DPrintf("PRVM_ED_ParseEpair: Bogus field name %s in %s\n", s, PRVM_NAME);
1060 def = PRVM_ED_FindField(s + 1);
1063 Con_DPrintf("PRVM_ED_ParseEpair: Can't find field %s in %s\n", s, PRVM_NAME);
1066 val->_int = def->ofs;
1070 func = PRVM_ED_FindFunction(s);
1073 Con_Printf("PRVM_ED_ParseEpair: Can't find function %s in %s\n", s, PRVM_NAME);
1076 val->function = func - prog->functions;
1080 Con_Printf("PRVM_ED_ParseEpair: Unknown key->type %i for key \"%s\" on %s\n", key->type, PRVM_GetString(key->s_name), PRVM_NAME);
1090 Console command to send a string to QC function GameCommand of the
1094 sv_cmd adminmsg 3 "do not teamkill"
1095 cl_cmd someclientcommand
1096 menu_cmd somemenucommand
1098 All progs can support this extension; sg calls it in server QC, cg in client
1102 void PRVM_GameCommand(const char *whichprogs, const char *whichcmd)
1106 Con_Printf("%s text...\n", whichcmd);
1111 if(!PRVM_SetProgFromString(whichprogs))
1112 // note: this is not PRVM_SetProg because that one aborts "hard" using PRVM_Error
1113 // also, it makes printing error messages easier!
1115 Con_Printf("%s program not loaded.\n", whichprogs);
1119 if(!prog->funcoffsets.GameCommand)
1121 Con_Printf("%s program do not support GameCommand!\n", whichprogs);
1125 int restorevm_tempstringsbuf_cursize;
1130 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
1131 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(s ? s : "");
1132 PRVM_ExecuteProgram (prog->funcoffsets.GameCommand, "QC function GameCommand is missing");
1133 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1138 void PRVM_GameCommand_Server_f(void)
1140 PRVM_GameCommand("server", "sv_cmd");
1142 void PRVM_GameCommand_Client_f(void)
1144 PRVM_GameCommand("client", "cl_cmd");
1146 void PRVM_GameCommand_Menu_f(void)
1148 PRVM_GameCommand("menu", "menu_cmd");
1155 Console command to load a field of a specified edict
1158 void PRVM_ED_EdictGet_f(void)
1165 if(Cmd_Argc() != 4 && Cmd_Argc() != 5)
1167 Con_Print("prvm_edictget <program name> <edict number> <field> [<cvar>]\n");
1172 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1174 Con_Printf("Wrong program name %s !\n", Cmd_Argv(1));
1178 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1180 if((key = PRVM_ED_FindField(Cmd_Argv(3))) == 0)
1182 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1186 v = (prvm_eval_t *)((char *)ed->fields.vp + key->ofs*4);
1187 s = PRVM_UglyValueString(key->type, v);
1190 cvar_t *cvar = Cvar_FindVar(Cmd_Argv(4));
1191 if (cvar && cvar->flags & CVAR_READONLY)
1193 Con_Printf("prvm_edictget: %s is read-only\n", cvar->name);
1196 Cvar_Get(Cmd_Argv(4), s, 0, NULL);
1199 Con_Printf("%s\n", s);
1205 void PRVM_ED_GlobalGet_f(void)
1211 if(Cmd_Argc() != 3 && Cmd_Argc() != 4)
1213 Con_Print("prvm_globalget <program name> <global> [<cvar>]\n");
1218 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1220 Con_Printf("Wrong program name %s !\n", Cmd_Argv(1));
1224 key = PRVM_ED_FindGlobal(Cmd_Argv(2));
1227 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
1231 v = (prvm_eval_t *) &prog->globals.generic[key->ofs];
1232 s = PRVM_UglyValueString(key->type, v);
1235 cvar_t *cvar = Cvar_FindVar(Cmd_Argv(3));
1236 if (cvar && cvar->flags & CVAR_READONLY)
1238 Con_Printf("prvm_globalget: %s is read-only\n", cvar->name);
1241 Cvar_Get(Cmd_Argv(3), s, 0, NULL);
1244 Con_Printf("%s\n", s);
1254 Console command to set a field of a specified edict
1257 void PRVM_ED_EdictSet_f(void)
1264 Con_Print("prvm_edictset <program name> <edict number> <field> <value>\n");
1269 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1271 Con_Printf("Wrong program name %s !\n", Cmd_Argv(1));
1275 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1277 if((key = PRVM_ED_FindField(Cmd_Argv(3))) == 0)
1278 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1280 PRVM_ED_ParseEpair(ed, key, Cmd_Argv(4), true);
1286 ====================
1289 Parses an edict out of the given string, returning the new position
1290 ed should be a properly initialized empty edict.
1291 Used for initial level load and for savegames.
1292 ====================
1294 const char *PRVM_ED_ParseEdict (const char *data, prvm_edict_t *ent)
1304 // go through all the dictionary pairs
1308 if (!COM_ParseToken_Simple(&data, false, false))
1309 PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1310 if (developer_entityparsing.integer)
1311 Con_Printf("Key: \"%s\"", com_token);
1312 if (com_token[0] == '}')
1315 // anglehack is to allow QuakeEd to write single scalar angles
1316 // and allow them to be turned into vectors. (FIXME...)
1317 if (!strcmp(com_token, "angle"))
1319 strlcpy (com_token, "angles", sizeof(com_token));
1325 // FIXME: change light to _light to get rid of this hack
1326 if (!strcmp(com_token, "light"))
1327 strlcpy (com_token, "light_lev", sizeof(com_token)); // hack for single light def
1329 strlcpy (keyname, com_token, sizeof(keyname));
1331 // another hack to fix keynames with trailing spaces
1332 n = strlen(keyname);
1333 while (n && keyname[n-1] == ' ')
1340 if (!COM_ParseToken_Simple(&data, false, false))
1341 PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1342 if (developer_entityparsing.integer)
1343 Con_Printf(" \"%s\"\n", com_token);
1345 if (com_token[0] == '}')
1346 PRVM_ERROR ("PRVM_ED_ParseEdict: closing brace without data");
1350 // ignore attempts to set key "" (this problem occurs in nehahra neh1m8.bsp)
1354 // keynames with a leading underscore are used for utility comments,
1355 // and are immediately discarded by quake
1356 if (keyname[0] == '_')
1359 key = PRVM_ED_FindField (keyname);
1362 Con_DPrintf("%s: '%s' is not a field\n", PRVM_NAME, keyname);
1369 strlcpy (temp, com_token, sizeof(temp));
1370 dpsnprintf (com_token, sizeof(com_token), "0 %s 0", temp);
1373 if (!PRVM_ED_ParseEpair(ent, key, com_token, strcmp(keyname, "wad") != 0))
1374 PRVM_ERROR ("PRVM_ED_ParseEdict: parse error");
1378 ent->priv.required->free = true;
1386 PRVM_ED_LoadFromFile
1388 The entities are directly placed in the array, rather than allocated with
1389 PRVM_ED_Alloc, because otherwise an error loading the map would have entity
1390 number references out of order.
1392 Creates a server's entity / program execution context by
1393 parsing textual entity definitions out of an ent file.
1395 Used for both fresh maps and savegame loads. A fresh map would also need
1396 to call PRVM_ED_CallSpawnFunctions () to let the objects initialize themselves.
1399 void PRVM_ED_LoadFromFile (const char *data)
1402 int parsed, inhibited, spawned, died;
1403 const char *funcname;
1415 // parse the opening brace
1416 if (!COM_ParseToken_Simple(&data, false, false))
1418 if (com_token[0] != '{')
1419 PRVM_ERROR ("PRVM_ED_LoadFromFile: %s: found %s when expecting {", PRVM_NAME, com_token);
1421 // CHANGED: this is not conform to PR_LoadFromFile
1422 if(prog->loadintoworld)
1424 prog->loadintoworld = false;
1425 ent = PRVM_EDICT_NUM(0);
1428 ent = PRVM_ED_Alloc();
1431 if (ent != prog->edicts) // hack
1432 memset (ent->fields.vp, 0, prog->progs->entityfields * 4);
1434 data = PRVM_ED_ParseEdict (data, ent);
1437 // remove the entity ?
1438 if(prog->load_edict && !prog->load_edict(ent))
1445 if (prog->funcoffsets.SV_OnEntityPreSpawnFunction)
1448 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1449 PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityPreSpawnFunction, "QC function SV_OnEntityPreSpawnFunction is missing");
1452 if(ent->priv.required->free)
1459 // immediately call spawn function, but only if there is a self global and a classname
1461 if(!ent->priv.required->free)
1462 if(prog->globaloffsets.self >= 0 && prog->fieldoffsets.classname >= 0)
1464 string_t handle = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.classname)->string;
1467 Con_Print("No classname for:\n");
1468 PRVM_ED_Print(ent, NULL);
1473 // look for the spawn function
1474 funcname = PRVM_GetString(handle);
1475 func = PRVM_ED_FindFunction (va("spawnfunc_%s", funcname));
1477 if(prog->globaloffsets.require_spawnfunc_prefix < 0)
1478 func = PRVM_ED_FindFunction (funcname);
1482 // check for OnEntityNoSpawnFunction
1483 if (prog->funcoffsets.SV_OnEntityNoSpawnFunction)
1486 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1487 PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityNoSpawnFunction, "QC function SV_OnEntityNoSpawnFunction is missing");
1491 if (developer.integer) // don't confuse non-developers with errors
1493 Con_Print("No spawn function for:\n");
1494 PRVM_ED_Print(ent, NULL);
1497 continue; // not included in "inhibited" count
1503 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1504 PRVM_ExecuteProgram (func - prog->functions, "");
1508 if(!ent->priv.required->free)
1509 if (prog->funcoffsets.SV_OnEntityPostSpawnFunction)
1512 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1513 PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityPostSpawnFunction, "QC function SV_OnEntityPostSpawnFunction is missing");
1517 if (ent->priv.required->free)
1521 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);
1524 void PRVM_FindOffsets(void)
1526 // field and global searches use -1 for NULL
1527 memset(&prog->fieldoffsets, -1, sizeof(prog->fieldoffsets));
1528 memset(&prog->globaloffsets, -1, sizeof(prog->globaloffsets));
1529 // functions use 0 for NULL
1530 memset(&prog->funcoffsets, 0, sizeof(prog->funcoffsets));
1532 // server and client qc use a lot of similar fields, so this is combined
1533 prog->fieldoffsets.SendEntity = PRVM_ED_FindFieldOffset("SendEntity");
1534 prog->fieldoffsets.SendFlags = PRVM_ED_FindFieldOffset("SendFlags");
1535 prog->fieldoffsets.Version = PRVM_ED_FindFieldOffset("Version");
1536 prog->fieldoffsets.alpha = PRVM_ED_FindFieldOffset("alpha");
1537 prog->fieldoffsets.ammo_cells1 = PRVM_ED_FindFieldOffset("ammo_cells1");
1538 prog->fieldoffsets.ammo_lava_nails = PRVM_ED_FindFieldOffset("ammo_lava_nails");
1539 prog->fieldoffsets.ammo_multi_rockets = PRVM_ED_FindFieldOffset("ammo_multi_rockets");
1540 prog->fieldoffsets.ammo_nails1 = PRVM_ED_FindFieldOffset("ammo_nails1");
1541 prog->fieldoffsets.ammo_plasma = PRVM_ED_FindFieldOffset("ammo_plasma");
1542 prog->fieldoffsets.ammo_rockets1 = PRVM_ED_FindFieldOffset("ammo_rockets1");
1543 prog->fieldoffsets.ammo_shells1 = PRVM_ED_FindFieldOffset("ammo_shells1");
1544 prog->fieldoffsets.angles = PRVM_ED_FindFieldOffset("angles");
1545 prog->fieldoffsets.button3 = PRVM_ED_FindFieldOffset("button3");
1546 prog->fieldoffsets.button4 = PRVM_ED_FindFieldOffset("button4");
1547 prog->fieldoffsets.button5 = PRVM_ED_FindFieldOffset("button5");
1548 prog->fieldoffsets.button6 = PRVM_ED_FindFieldOffset("button6");
1549 prog->fieldoffsets.button7 = PRVM_ED_FindFieldOffset("button7");
1550 prog->fieldoffsets.button8 = PRVM_ED_FindFieldOffset("button8");
1551 prog->fieldoffsets.button9 = PRVM_ED_FindFieldOffset("button9");
1552 prog->fieldoffsets.button10 = PRVM_ED_FindFieldOffset("button10");
1553 prog->fieldoffsets.button11 = PRVM_ED_FindFieldOffset("button11");
1554 prog->fieldoffsets.button12 = PRVM_ED_FindFieldOffset("button12");
1555 prog->fieldoffsets.button13 = PRVM_ED_FindFieldOffset("button13");
1556 prog->fieldoffsets.button14 = PRVM_ED_FindFieldOffset("button14");
1557 prog->fieldoffsets.button15 = PRVM_ED_FindFieldOffset("button15");
1558 prog->fieldoffsets.button16 = PRVM_ED_FindFieldOffset("button16");
1559 prog->fieldoffsets.buttonchat = PRVM_ED_FindFieldOffset("buttonchat");
1560 prog->fieldoffsets.buttonuse = PRVM_ED_FindFieldOffset("buttonuse");
1561 prog->fieldoffsets.chain = PRVM_ED_FindFieldOffset("chain");
1562 prog->fieldoffsets.classname = PRVM_ED_FindFieldOffset("classname");
1563 prog->fieldoffsets.clientcamera = PRVM_ED_FindFieldOffset("clientcamera");
1564 prog->fieldoffsets.clientcolors = PRVM_ED_FindFieldOffset("clientcolors");
1565 prog->fieldoffsets.clientstatus = PRVM_ED_FindFieldOffset("clientstatus");
1566 prog->fieldoffsets.color = PRVM_ED_FindFieldOffset("color");
1567 prog->fieldoffsets.colormod = PRVM_ED_FindFieldOffset("colormod");
1568 prog->fieldoffsets.contentstransition = PRVM_ED_FindFieldOffset("contentstransition");
1569 prog->fieldoffsets.cursor_active = PRVM_ED_FindFieldOffset("cursor_active");
1570 prog->fieldoffsets.cursor_screen = PRVM_ED_FindFieldOffset("cursor_screen");
1571 prog->fieldoffsets.cursor_trace_endpos = PRVM_ED_FindFieldOffset("cursor_trace_endpos");
1572 prog->fieldoffsets.cursor_trace_ent = PRVM_ED_FindFieldOffset("cursor_trace_ent");
1573 prog->fieldoffsets.cursor_trace_start = PRVM_ED_FindFieldOffset("cursor_trace_start");
1574 prog->fieldoffsets.customizeentityforclient = PRVM_ED_FindFieldOffset("customizeentityforclient");
1575 prog->fieldoffsets.dimension_hit = PRVM_ED_FindFieldOffset("dimension_hit");
1576 prog->fieldoffsets.dimension_solid = PRVM_ED_FindFieldOffset("dimension_solid");
1577 prog->fieldoffsets.disableclientprediction = PRVM_ED_FindFieldOffset("disableclientprediction");
1578 prog->fieldoffsets.dphitcontentsmask = PRVM_ED_FindFieldOffset("dphitcontentsmask");
1579 prog->fieldoffsets.drawonlytoclient = PRVM_ED_FindFieldOffset("drawonlytoclient");
1580 prog->fieldoffsets.exteriormodeltoclient = PRVM_ED_FindFieldOffset("exteriormodeltoclient");
1581 prog->fieldoffsets.fatness = PRVM_ED_FindFieldOffset("fatness");
1582 prog->fieldoffsets.forceshader = PRVM_ED_FindFieldOffset("forceshader");
1583 prog->fieldoffsets.frame = PRVM_ED_FindFieldOffset("frame");
1584 prog->fieldoffsets.frame1time = PRVM_ED_FindFieldOffset("frame1time");
1585 prog->fieldoffsets.frame2 = PRVM_ED_FindFieldOffset("frame2");
1586 prog->fieldoffsets.frame2time = PRVM_ED_FindFieldOffset("frame2time");
1587 prog->fieldoffsets.frame3 = PRVM_ED_FindFieldOffset("frame3");
1588 prog->fieldoffsets.frame3time = PRVM_ED_FindFieldOffset("frame3time");
1589 prog->fieldoffsets.frame4 = PRVM_ED_FindFieldOffset("frame4");
1590 prog->fieldoffsets.frame4time = PRVM_ED_FindFieldOffset("frame4time");
1591 prog->fieldoffsets.fullbright = PRVM_ED_FindFieldOffset("fullbright");
1592 prog->fieldoffsets.glow_color = PRVM_ED_FindFieldOffset("glow_color");
1593 prog->fieldoffsets.glow_size = PRVM_ED_FindFieldOffset("glow_size");
1594 prog->fieldoffsets.glow_trail = PRVM_ED_FindFieldOffset("glow_trail");
1595 prog->fieldoffsets.gravity = PRVM_ED_FindFieldOffset("gravity");
1596 prog->fieldoffsets.groundentity = PRVM_ED_FindFieldOffset("groundentity");
1597 prog->fieldoffsets.hull = PRVM_ED_FindFieldOffset("hull");
1598 prog->fieldoffsets.ideal_yaw = PRVM_ED_FindFieldOffset("ideal_yaw");
1599 prog->fieldoffsets.idealpitch = PRVM_ED_FindFieldOffset("idealpitch");
1600 prog->fieldoffsets.items2 = PRVM_ED_FindFieldOffset("items2");
1601 prog->fieldoffsets.lerpfrac = PRVM_ED_FindFieldOffset("lerpfrac");
1602 prog->fieldoffsets.lerpfrac3 = PRVM_ED_FindFieldOffset("lerpfrac3");
1603 prog->fieldoffsets.lerpfrac4 = PRVM_ED_FindFieldOffset("lerpfrac4");
1604 prog->fieldoffsets.light_lev = PRVM_ED_FindFieldOffset("light_lev");
1605 prog->fieldoffsets.message = PRVM_ED_FindFieldOffset("message");
1606 prog->fieldoffsets.modelflags = PRVM_ED_FindFieldOffset("modelflags");
1607 prog->fieldoffsets.movement = PRVM_ED_FindFieldOffset("movement");
1608 prog->fieldoffsets.movetypesteplandevent = PRVM_ED_FindFieldOffset("movetypesteplandevent");
1609 prog->fieldoffsets.netaddress = PRVM_ED_FindFieldOffset("netaddress");
1610 prog->fieldoffsets.nextthink = PRVM_ED_FindFieldOffset("nextthink");
1611 prog->fieldoffsets.nodrawtoclient = PRVM_ED_FindFieldOffset("nodrawtoclient");
1612 prog->fieldoffsets.pflags = PRVM_ED_FindFieldOffset("pflags");
1613 prog->fieldoffsets.ping = PRVM_ED_FindFieldOffset("ping");
1614 prog->fieldoffsets.pitch_speed = PRVM_ED_FindFieldOffset("pitch_speed");
1615 prog->fieldoffsets.playermodel = PRVM_ED_FindFieldOffset("playermodel");
1616 prog->fieldoffsets.playerskin = PRVM_ED_FindFieldOffset("playerskin");
1617 prog->fieldoffsets.pmodel = PRVM_ED_FindFieldOffset("pmodel");
1618 prog->fieldoffsets.punchvector = PRVM_ED_FindFieldOffset("punchvector");
1619 prog->fieldoffsets.renderamt = PRVM_ED_FindFieldOffset("renderamt"); // HalfLife support
1620 prog->fieldoffsets.renderflags = PRVM_ED_FindFieldOffset("renderflags");
1621 prog->fieldoffsets.rendermode = PRVM_ED_FindFieldOffset("rendermode"); // HalfLife support
1622 prog->fieldoffsets.scale = PRVM_ED_FindFieldOffset("scale");
1623 prog->fieldoffsets.shadertime = PRVM_ED_FindFieldOffset("shadertime");
1624 prog->fieldoffsets.style = PRVM_ED_FindFieldOffset("style");
1625 prog->fieldoffsets.tag_entity = PRVM_ED_FindFieldOffset("tag_entity");
1626 prog->fieldoffsets.tag_index = PRVM_ED_FindFieldOffset("tag_index");
1627 prog->fieldoffsets.think = PRVM_ED_FindFieldOffset("think");
1628 prog->fieldoffsets.viewmodelforclient = PRVM_ED_FindFieldOffset("viewmodelforclient");
1629 prog->fieldoffsets.viewzoom = PRVM_ED_FindFieldOffset("viewzoom");
1630 prog->fieldoffsets.yaw_speed = PRVM_ED_FindFieldOffset("yaw_speed");
1631 prog->fieldoffsets.bouncefactor = PRVM_ED_FindFieldOffset("bouncefactor");
1632 prog->fieldoffsets.bouncestop = PRVM_ED_FindFieldOffset("bouncestop");
1633 prog->funcoffsets.CSQC_ConsoleCommand = PRVM_ED_FindFunctionOffset("CSQC_ConsoleCommand");
1634 prog->funcoffsets.CSQC_Ent_Remove = PRVM_ED_FindFunctionOffset("CSQC_Ent_Remove");
1635 prog->funcoffsets.CSQC_Ent_Spawn = PRVM_ED_FindFunctionOffset("CSQC_Ent_Spawn");
1636 prog->funcoffsets.CSQC_Ent_Update = PRVM_ED_FindFunctionOffset("CSQC_Ent_Update");
1637 prog->funcoffsets.CSQC_Event = PRVM_ED_FindFunctionOffset("CSQC_Event");
1638 prog->funcoffsets.CSQC_Event_Sound = PRVM_ED_FindFunctionOffset("CSQC_Event_Sound");
1639 prog->funcoffsets.CSQC_Init = PRVM_ED_FindFunctionOffset("CSQC_Init");
1640 prog->funcoffsets.CSQC_InputEvent = PRVM_ED_FindFunctionOffset("CSQC_InputEvent");
1641 prog->funcoffsets.CSQC_Parse_CenterPrint = PRVM_ED_FindFunctionOffset("CSQC_Parse_CenterPrint");
1642 prog->funcoffsets.CSQC_Parse_Print = PRVM_ED_FindFunctionOffset("CSQC_Parse_Print");
1643 prog->funcoffsets.CSQC_Parse_StuffCmd = PRVM_ED_FindFunctionOffset("CSQC_Parse_StuffCmd");
1644 prog->funcoffsets.CSQC_Parse_TempEntity = PRVM_ED_FindFunctionOffset("CSQC_Parse_TempEntity");
1645 prog->funcoffsets.CSQC_Shutdown = PRVM_ED_FindFunctionOffset("CSQC_Shutdown");
1646 prog->funcoffsets.CSQC_UpdateView = PRVM_ED_FindFunctionOffset("CSQC_UpdateView");
1647 prog->funcoffsets.EndFrame = PRVM_ED_FindFunctionOffset("EndFrame");
1648 prog->funcoffsets.GameCommand = PRVM_ED_FindFunctionOffset("GameCommand");
1649 prog->funcoffsets.Gecko_Query = PRVM_ED_FindFunctionOffset("Gecko_Query");
1650 prog->funcoffsets.RestoreGame = PRVM_ED_FindFunctionOffset("RestoreGame");
1651 prog->funcoffsets.SV_ChangeTeam = PRVM_ED_FindFunctionOffset("SV_ChangeTeam");
1652 prog->funcoffsets.SV_OnEntityNoSpawnFunction = PRVM_ED_FindFunctionOffset("SV_OnEntityNoSpawnFunction");
1653 prog->funcoffsets.SV_OnEntityPostSpawnFunction = PRVM_ED_FindFunctionOffset("SV_OnEntityPostSpawnFunction");
1654 prog->funcoffsets.SV_OnEntityPreSpawnFunction = PRVM_ED_FindFunctionOffset("SV_OnEntityPreSpawnFunction");
1655 prog->funcoffsets.SV_ParseClientCommand = PRVM_ED_FindFunctionOffset("SV_ParseClientCommand");
1656 prog->funcoffsets.SV_PausedTic = PRVM_ED_FindFunctionOffset("SV_PausedTic");
1657 prog->funcoffsets.SV_PlayerPhysics = PRVM_ED_FindFunctionOffset("SV_PlayerPhysics");
1658 prog->funcoffsets.SV_Shutdown = PRVM_ED_FindFunctionOffset("SV_Shutdown");
1659 prog->funcoffsets.URI_Get_Callback = PRVM_ED_FindFunctionOffset("URI_Get_Callback");
1660 prog->globaloffsets.SV_InitCmd = PRVM_ED_FindGlobalOffset("SV_InitCmd");
1661 prog->globaloffsets.coop = PRVM_ED_FindGlobalOffset("coop");
1662 prog->globaloffsets.deathmatch = PRVM_ED_FindGlobalOffset("deathmatch");
1663 prog->globaloffsets.dmg_origin = PRVM_ED_FindGlobalOffset("dmg_origin");
1664 prog->globaloffsets.dmg_save = PRVM_ED_FindGlobalOffset("dmg_save");
1665 prog->globaloffsets.dmg_take = PRVM_ED_FindGlobalOffset("dmg_take");
1666 prog->globaloffsets.drawfont = PRVM_ED_FindGlobalOffset("drawfont");
1667 prog->globaloffsets.gettaginfo_forward = PRVM_ED_FindGlobalOffset("gettaginfo_forward");
1668 prog->globaloffsets.gettaginfo_name = PRVM_ED_FindGlobalOffset("gettaginfo_name");
1669 prog->globaloffsets.gettaginfo_offset = PRVM_ED_FindGlobalOffset("gettaginfo_offset");
1670 prog->globaloffsets.gettaginfo_parent = PRVM_ED_FindGlobalOffset("gettaginfo_parent");
1671 prog->globaloffsets.gettaginfo_right = PRVM_ED_FindGlobalOffset("gettaginfo_right");
1672 prog->globaloffsets.gettaginfo_up = PRVM_ED_FindGlobalOffset("gettaginfo_up");
1673 prog->globaloffsets.intermission = PRVM_ED_FindGlobalOffset("intermission");
1674 prog->globaloffsets.require_spawnfunc_prefix = PRVM_ED_FindGlobalOffset("require_spawnfunc_prefix");
1675 prog->globaloffsets.sb_showscores = PRVM_ED_FindGlobalOffset("sb_showscores");
1676 prog->globaloffsets.self = PRVM_ED_FindGlobalOffset("self");
1677 prog->globaloffsets.serverdeltatime = PRVM_ED_FindGlobalOffset("serverdeltatime");
1678 prog->globaloffsets.serverprevtime = PRVM_ED_FindGlobalOffset("serverprevtime");
1679 prog->globaloffsets.servertime = PRVM_ED_FindGlobalOffset("servertime");
1680 prog->globaloffsets.time = PRVM_ED_FindGlobalOffset("time");
1681 prog->globaloffsets.trace_allsolid = PRVM_ED_FindGlobalOffset("trace_allsolid");
1682 prog->globaloffsets.trace_dphitcontents = PRVM_ED_FindGlobalOffset("trace_dphitcontents");
1683 prog->globaloffsets.trace_dphitq3surfaceflags = PRVM_ED_FindGlobalOffset("trace_dphitq3surfaceflags");
1684 prog->globaloffsets.trace_dphittexturename = PRVM_ED_FindGlobalOffset("trace_dphittexturename");
1685 prog->globaloffsets.trace_dpstartcontents = PRVM_ED_FindGlobalOffset("trace_dpstartcontents");
1686 prog->globaloffsets.trace_endpos = PRVM_ED_FindGlobalOffset("trace_endpos");
1687 prog->globaloffsets.trace_ent = PRVM_ED_FindGlobalOffset("trace_ent");
1688 prog->globaloffsets.trace_fraction = PRVM_ED_FindGlobalOffset("trace_fraction");
1689 prog->globaloffsets.trace_inopen = PRVM_ED_FindGlobalOffset("trace_inopen");
1690 prog->globaloffsets.trace_inwater = PRVM_ED_FindGlobalOffset("trace_inwater");
1691 prog->globaloffsets.trace_networkentity = PRVM_ED_FindGlobalOffset("trace_networkentity");
1692 prog->globaloffsets.trace_plane_dist = PRVM_ED_FindGlobalOffset("trace_plane_dist");
1693 prog->globaloffsets.trace_plane_normal = PRVM_ED_FindGlobalOffset("trace_plane_normal");
1694 prog->globaloffsets.trace_startsolid = PRVM_ED_FindGlobalOffset("trace_startsolid");
1695 prog->globaloffsets.v_forward = PRVM_ED_FindGlobalOffset("v_forward");
1696 prog->globaloffsets.v_right = PRVM_ED_FindGlobalOffset("v_right");
1697 prog->globaloffsets.v_up = PRVM_ED_FindGlobalOffset("v_up");
1698 prog->globaloffsets.view_angles = PRVM_ED_FindGlobalOffset("view_angles");
1699 prog->globaloffsets.worldstatus = PRVM_ED_FindGlobalOffset("worldstatus");
1701 // menu qc only uses some functions, nothing else
1702 prog->funcoffsets.m_draw = PRVM_ED_FindFunctionOffset("m_draw");
1703 prog->funcoffsets.m_init = PRVM_ED_FindFunctionOffset("m_init");
1704 prog->funcoffsets.m_keydown = PRVM_ED_FindFunctionOffset("m_keydown");
1705 prog->funcoffsets.m_keyup = PRVM_ED_FindFunctionOffset("m_keyup");
1706 prog->funcoffsets.m_shutdown = PRVM_ED_FindFunctionOffset("m_shutdown");
1707 prog->funcoffsets.m_toggle = PRVM_ED_FindFunctionOffset("m_toggle");
1712 typedef struct dpfield_s
1719 #define DPFIELDS (sizeof(dpfields) / sizeof(dpfield_t))
1721 dpfield_t dpfields[] =
1732 void PRVM_LeakTest(void);
1733 void PRVM_ResetProg(void)
1736 PRVM_GCALL(reset_cmd)();
1737 Mem_FreePool(&prog->progs_mempool);
1738 memset(prog,0,sizeof(prvm_prog_t));
1739 prog->starttime = Sys_DoubleTime();
1747 void PRVM_LoadLNO( const char *progname ) {
1748 fs_offset_t filesize;
1750 unsigned int *header;
1753 FS_StripExtension( progname, filename, sizeof( filename ) );
1754 strlcat( filename, ".lno", sizeof( filename ) );
1756 lno = FS_LoadFile( filename, tempmempool, false, &filesize );
1762 <Spike> SafeWrite (h, &lnotype, sizeof(int));
1763 <Spike> SafeWrite (h, &version, sizeof(int));
1764 <Spike> SafeWrite (h, &numglobaldefs, sizeof(int));
1765 <Spike> SafeWrite (h, &numpr_globals, sizeof(int));
1766 <Spike> SafeWrite (h, &numfielddefs, sizeof(int));
1767 <Spike> SafeWrite (h, &numstatements, sizeof(int));
1768 <Spike> SafeWrite (h, statement_linenums, numstatements*sizeof(int));
1770 if( (unsigned) filesize < (6 + prog->progs->numstatements) * sizeof( int ) ) {
1775 header = (unsigned int *) lno;
1776 if( header[ 0 ] == *(unsigned int *) "LNOF" &&
1777 LittleLong( header[ 1 ] ) == 1 &&
1778 (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs->numglobaldefs &&
1779 (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs->numglobals &&
1780 (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs->numfielddefs &&
1781 (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs->numstatements )
1783 prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs->numstatements * sizeof( int ) );
1784 memcpy( prog->statement_linenums, (int *) lno + 6, prog->progs->numstatements * sizeof( int ) );
1794 void PRVM_LoadProgs (const char * filename, int numrequiredfunc, char **required_func, int numrequiredfields, prvm_required_field_t *required_field, int numrequiredglobals, char **required_global)
1798 ddef_t *infielddefs;
1799 dfunction_t *dfunctions;
1800 fs_offset_t filesize;
1802 if( prog->loaded ) {
1803 PRVM_ERROR ("PRVM_LoadProgs: there is already a %s program loaded!", PRVM_NAME );
1806 prog->progs = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
1807 if (prog->progs == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
1808 PRVM_ERROR ("PRVM_LoadProgs: couldn't load %s for %s", filename, PRVM_NAME);
1809 // TODO bounds check header fields (e.g. numstatements), they must never go behind end of file
1811 Con_DPrintf("%s programs occupy %iK.\n", PRVM_NAME, (int)(filesize/1024));
1813 prog->filecrc = CRC_Block((unsigned char *)prog->progs, filesize);
1815 // byte swap the header
1816 for (i = 0;i < (int) sizeof(*prog->progs) / 4;i++)
1817 ((int *)prog->progs)[i] = LittleLong ( ((int *)prog->progs)[i] );
1819 if (prog->progs->version != PROG_VERSION && prog->progs->version != PROG_EXTENDED)
1820 PRVM_ERROR ("%s: %s has wrong version number (%i should be %i or %i)", PRVM_NAME, filename, prog->progs->version, PROG_VERSION, PROG_EXTENDED);
1821 if (prog->progs->crc != prog->headercrc && prog->progs->crc != prog->headercrc2)
1822 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);
1824 //prog->functions = (dfunction_t *)((unsigned char *)progs + progs->ofs_functions);
1825 dfunctions = (dfunction_t *)((unsigned char *)prog->progs + prog->progs->ofs_functions);
1827 if (prog->progs->ofs_strings + prog->progs->numstrings >= (int)filesize)
1828 PRVM_ERROR ("%s: %s strings go past end of file", PRVM_NAME, filename);
1829 prog->strings = (char *)prog->progs + prog->progs->ofs_strings;
1830 prog->stringssize = prog->progs->numstrings;
1832 prog->numknownstrings = 0;
1833 prog->maxknownstrings = 0;
1834 prog->knownstrings = NULL;
1835 prog->knownstrings_freeable = NULL;
1837 Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
1839 prog->globaldefs = (ddef_t *)((unsigned char *)prog->progs + prog->progs->ofs_globaldefs);
1841 // we need to expand the fielddefs list to include all the engine fields,
1842 // so allocate a new place for it
1843 infielddefs = (ddef_t *)((unsigned char *)prog->progs + prog->progs->ofs_fielddefs);
1845 prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs->numfielddefs + numrequiredfields) * sizeof(ddef_t));
1847 prog->statements = (dstatement_t *)((unsigned char *)prog->progs + prog->progs->ofs_statements);
1849 prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs->numstatements * sizeof(*prog->statement_profile));
1851 // moved edict_size calculation down below field adding code
1853 //pr_global_struct = (globalvars_t *)((unsigned char *)progs + progs->ofs_globals);
1854 prog->globals.generic = (float *)((unsigned char *)prog->progs + prog->progs->ofs_globals);
1856 // byte swap the lumps
1857 for (i=0 ; i<prog->progs->numstatements ; i++)
1859 prog->statements[i].op = LittleShort(prog->statements[i].op);
1860 prog->statements[i].a = LittleShort(prog->statements[i].a);
1861 prog->statements[i].b = LittleShort(prog->statements[i].b);
1862 prog->statements[i].c = LittleShort(prog->statements[i].c);
1865 prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs->numfunctions);
1866 for (i = 0;i < prog->progs->numfunctions;i++)
1868 prog->functions[i].first_statement = LittleLong (dfunctions[i].first_statement);
1869 prog->functions[i].parm_start = LittleLong (dfunctions[i].parm_start);
1870 prog->functions[i].s_name = LittleLong (dfunctions[i].s_name);
1871 prog->functions[i].s_file = LittleLong (dfunctions[i].s_file);
1872 prog->functions[i].numparms = LittleLong (dfunctions[i].numparms);
1873 prog->functions[i].locals = LittleLong (dfunctions[i].locals);
1874 memcpy(prog->functions[i].parm_size, dfunctions[i].parm_size, sizeof(dfunctions[i].parm_size));
1875 if(prog->functions[i].first_statement >= prog->progs->numstatements)
1876 PRVM_ERROR("PRVM_LoadProgs: out of bounds function statement (function %d) in %s", i, PRVM_NAME);
1877 // TODO bounds check parm_start, s_name, s_file, numparms, locals, parm_size
1880 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
1882 prog->globaldefs[i].type = LittleShort (prog->globaldefs[i].type);
1883 prog->globaldefs[i].ofs = LittleShort (prog->globaldefs[i].ofs);
1884 prog->globaldefs[i].s_name = LittleLong (prog->globaldefs[i].s_name);
1885 // TODO bounds check ofs, s_name
1888 // copy the progs fields to the new fields list
1889 for (i = 0;i < prog->progs->numfielddefs;i++)
1891 prog->fielddefs[i].type = LittleShort (infielddefs[i].type);
1892 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
1893 PRVM_ERROR ("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", PRVM_NAME);
1894 prog->fielddefs[i].ofs = LittleShort (infielddefs[i].ofs);
1895 prog->fielddefs[i].s_name = LittleLong (infielddefs[i].s_name);
1896 // TODO bounds check ofs, s_name
1899 // append the required fields
1900 for (i = 0;i < (int) numrequiredfields;i++)
1902 prog->fielddefs[prog->progs->numfielddefs].type = required_field[i].type;
1903 prog->fielddefs[prog->progs->numfielddefs].ofs = prog->progs->entityfields;
1904 prog->fielddefs[prog->progs->numfielddefs].s_name = PRVM_SetEngineString(required_field[i].name);
1905 // TODO bounds check ofs, s_name
1906 if (prog->fielddefs[prog->progs->numfielddefs].type == ev_vector)
1907 prog->progs->entityfields += 3;
1909 prog->progs->entityfields++;
1910 prog->progs->numfielddefs++;
1913 // check required functions
1914 for(i=0 ; i < numrequiredfunc ; i++)
1915 if(PRVM_ED_FindFunction(required_func[i]) == 0)
1916 PRVM_ERROR("%s: %s not found in %s",PRVM_NAME, required_func[i], filename);
1918 // check required globals
1919 for(i=0 ; i < numrequiredglobals ; i++)
1920 if(PRVM_ED_FindGlobal(required_global[i]) == 0)
1921 PRVM_ERROR("%s: %s not found in %s",PRVM_NAME, required_global[i], filename);
1923 for (i=0 ; i<prog->progs->numglobals ; i++)
1924 ((int *)prog->globals.generic)[i] = LittleLong (((int *)prog->globals.generic)[i]);
1926 // moved edict_size calculation down here, below field adding code
1927 // LordHavoc: this no longer includes the prvm_edict_t header
1928 prog->edict_size = prog->progs->entityfields * 4;
1929 prog->edictareasize = prog->edict_size * prog->limit_edicts;
1931 // LordHavoc: bounds check anything static
1932 for (i = 0,st = prog->statements;i < prog->progs->numstatements;i++,st++)
1938 if ((unsigned short) st->a >= prog->progs->numglobals || st->b + i < 0 || st->b + i >= prog->progs->numstatements)
1939 PRVM_ERROR("PRVM_LoadProgs: out of bounds IF/IFNOT (int/general) (statement %d) in %s", i, PRVM_NAME);
1943 if ((unsigned short) st->a >= prog->progs->numglobals || st->b + i < 0 || st->b + i >= prog->progs->numstatements)
1944 PRVM_ERROR("PRVM_LoadProgs: out of bounds IF/IFNOT (float) (statement %d) in %s", i, PRVM_NAME);
1948 if ((unsigned short) st->a >= prog->progs->numglobals || st->b + i < 0 || st->b + i >= prog->progs->numstatements)
1949 PRVM_ERROR("PRVM_LoadProgs: out of bounds IF/IFNOT (string not null) (statement %d) in %s", i, PRVM_NAME);
1952 if (st->a + i < 0 || st->a + i >= prog->progs->numstatements)
1953 PRVM_ERROR("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, PRVM_NAME);
1956 if ((unsigned short) st->a >= prog->progs->numglobals || st->b + i < 0 || st->b + i >= prog->progs->numstatements)
1957 PRVM_ERROR("PRVM_LoadProgs: out of bounds CASE (statement %d) in %s", i, PRVM_NAME);
1960 if ((unsigned short) st->a >= prog->progs->numglobals ||
1961 (unsigned short) st->b >= prog->progs->numglobals ||
1962 st->c + i < 0 || st->c + i >= prog->progs->numstatements)
1963 PRVM_ERROR("PRVM_LoadProgs: out of bounds CASERANGE (statement %d) in %s", i, PRVM_NAME);
1965 // global global global
2071 case OP_ADDSTOREP_F:
2072 case OP_ADDSTOREP_V:
2073 case OP_SUBSTOREP_F:
2074 case OP_SUBSTOREP_V:
2075 case OP_MULSTOREP_F:
2076 case OP_MULSTOREP_V:
2077 case OP_DIVSTOREP_F:
2078 case OP_GLOBALADDRESS:
2079 case OP_POINTER_ADD:
2085 case OP_FETCH_GBL_F:
2086 case OP_FETCH_GBL_S:
2087 case OP_FETCH_GBL_E:
2088 case OP_FETCH_GBL_FNC:
2089 case OP_FETCH_GBL_V:
2090 if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->b >= prog->progs->numglobals || (unsigned short) st->c >= prog->progs->numglobals)
2091 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
2093 // global none global
2102 if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->c >= prog->progs->numglobals)
2103 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
2125 // case OP_STOREP_C:
2129 case OP_GSTOREP_ENT:
2130 case OP_GSTOREP_FLD:
2132 case OP_GSTOREP_FNC:
2150 if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->b >= prog->progs->numglobals)
2151 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
2181 if ((unsigned short) st->a >= prog->progs->numglobals)
2182 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
2184 // TODO: add fte instruction support here too
2187 // this writes to OFS_RETURN and has no parameters
2190 Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", st->op, i, PRVM_NAME);
2194 if(prog->progs->numstatements < 1)
2196 PRVM_ERROR("PRVM_LoadProgs: empty program in %s", PRVM_NAME);
2198 else switch(prog->statements[prog->progs->numstatements - 1].op)
2205 PRVM_ERROR("PRVM_LoadProgs: program may fall off the edge (does not end with RETURN, GOTO or DONE) in %s", PRVM_NAME);
2209 PRVM_LoadLNO(filename);
2213 prog->loaded = TRUE;
2215 // set flags & ddef_ts in prog
2221 PRVM_GCALL(init_cmd)();
2228 void PRVM_Fields_f (void)
2230 int i, j, ednum, used, usedamount;
2232 char tempstring[MAX_INPUTLINE], tempstring2[260];
2242 Con_Print("no progs loaded\n");
2249 Con_Print("prvm_fields <program name>\n");
2254 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
2257 counts = (int *)Mem_Alloc(tempmempool, prog->progs->numfielddefs * sizeof(int));
2258 for (ednum = 0;ednum < prog->max_edicts;ednum++)
2260 ed = PRVM_EDICT_NUM(ednum);
2261 if (ed->priv.required->free)
2263 for (i = 1;i < prog->progs->numfielddefs;i++)
2265 d = &prog->fielddefs[i];
2266 name = PRVM_GetString(d->s_name);
2267 if (name[strlen(name)-2] == '_')
2268 continue; // skip _x, _y, _z vars
2269 v = (int *)((char *)ed->fields.vp + d->ofs*4);
2270 // if the value is still all 0, skip the field
2271 for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
2284 for (i = 0;i < prog->progs->numfielddefs;i++)
2286 d = &prog->fielddefs[i];
2287 name = PRVM_GetString(d->s_name);
2288 if (name[strlen(name)-2] == '_')
2289 continue; // skip _x, _y, _z vars
2290 switch(d->type & ~DEF_SAVEGLOBAL)
2293 strlcat(tempstring, "string ", sizeof(tempstring));
2296 strlcat(tempstring, "entity ", sizeof(tempstring));
2299 strlcat(tempstring, "function ", sizeof(tempstring));
2302 strlcat(tempstring, "field ", sizeof(tempstring));
2305 strlcat(tempstring, "void ", sizeof(tempstring));
2308 strlcat(tempstring, "float ", sizeof(tempstring));
2311 strlcat(tempstring, "vector ", sizeof(tempstring));
2314 strlcat(tempstring, "pointer ", sizeof(tempstring));
2317 dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
2318 strlcat(tempstring, tempstring2, sizeof(tempstring));
2321 if (strlen(name) > sizeof(tempstring2)-4)
2323 memcpy (tempstring2, name, sizeof(tempstring2)-4);
2324 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
2325 tempstring2[sizeof(tempstring2)-1] = 0;
2328 strlcat(tempstring, name, sizeof(tempstring));
2329 for (j = (int)strlen(name);j < 25;j++)
2330 strlcat(tempstring, " ", sizeof(tempstring));
2331 dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
2332 strlcat(tempstring, tempstring2, sizeof(tempstring));
2333 strlcat(tempstring, "\n", sizeof(tempstring));
2334 if (strlen(tempstring) >= sizeof(tempstring)/2)
2336 Con_Print(tempstring);
2342 usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
2346 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);
2351 void PRVM_Globals_f (void)
2354 const char *wildcard;
2360 Con_Print("no progs loaded\n");
2363 if(Cmd_Argc () < 2 || Cmd_Argc() > 3)
2365 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
2370 if(!PRVM_SetProgFromString (Cmd_Argv (1)))
2373 if( Cmd_Argc() == 3)
2374 wildcard = Cmd_Argv(2);
2378 Con_Printf("%s :", PRVM_NAME);
2380 for (i = 0;i < prog->progs->numglobaldefs;i++)
2383 if( !matchpattern( PRVM_GetString(prog->globaldefs[i].s_name), wildcard, 1) )
2388 Con_Printf("%s\n", PRVM_GetString(prog->globaldefs[i].s_name));
2390 Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->progs->numglobals, numculled, prog->progs->numglobals * 4);
2400 void PRVM_Global_f(void)
2403 if( Cmd_Argc() != 3 ) {
2404 Con_Printf( "prvm_global <program name> <global name>\n" );
2409 if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
2412 global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
2414 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2416 Con_Printf( "%s: %s\n", Cmd_Argv(2), PRVM_ValueString( (etype_t)global->type, (prvm_eval_t *) &prog->globals.generic[ global->ofs ] ) );
2425 void PRVM_GlobalSet_f(void)
2428 if( Cmd_Argc() != 4 ) {
2429 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2434 if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
2437 global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
2439 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2441 PRVM_ED_ParseEpair( NULL, global, Cmd_Argv(3), true );
2450 void PRVM_Init (void)
2452 Cmd_AddCommand ("prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2453 Cmd_AddCommand ("prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2454 Cmd_AddCommand ("prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2455 Cmd_AddCommand ("prvm_profile", PRVM_Profile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu)");
2456 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");
2457 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)");
2458 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)");
2459 Cmd_AddCommand ("prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2460 Cmd_AddCommand ("prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2461 Cmd_AddCommand ("prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2462 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)");
2463 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");
2464 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");
2465 Cmd_AddCommand ("prvm_printfunction", PRVM_PrintFunction_f, "prints a disassembly (QuakeC instructions) of the specified function in the selected VM (server, client, menu)");
2466 Cmd_AddCommand ("cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2467 Cmd_AddCommand ("menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2468 Cmd_AddCommand ("sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2470 // COMMANDLINEOPTION: PRVM: -noboundscheck disables the bounds checks (security hole if CSQC is in use!)
2471 prvm_boundscheck = !COM_CheckParm("-noboundscheck");
2473 Cvar_RegisterVariable (&prvm_traceqc);
2474 Cvar_RegisterVariable (&prvm_statementprofiling);
2475 Cvar_RegisterVariable (&prvm_backtraceforwarnings);
2476 Cvar_RegisterVariable (&prvm_leaktest);
2477 Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames);
2478 Cvar_RegisterVariable (&prvm_errordump);
2480 // COMMANDLINEOPTION: PRVM: -norunaway disables the runaway loop check (it might be impossible to exit DarkPlaces if used!)
2481 prvm_runawaycheck = !COM_CheckParm("-norunaway");
2491 void PRVM_InitProg(int prognr)
2493 if(prognr < 0 || prognr >= PRVM_MAXPROGS)
2494 Sys_Error("PRVM_InitProg: Invalid program number %i",prognr);
2496 prog = &prog_list[prognr];
2501 memset(prog, 0, sizeof(prvm_prog_t));
2502 prog->starttime = Sys_DoubleTime();
2504 prog->error_cmd = Host_Error;
2505 prog->leaktest_active = prvm_leaktest.integer != 0;
2508 int PRVM_GetProgNr(void)
2510 return prog - prog_list;
2513 void *_PRVM_Alloc(size_t buffersize, const char *filename, int fileline)
2515 return _Mem_Alloc(prog->progs_mempool, buffersize, filename, fileline);
2518 void _PRVM_Free(void *buffer, const char *filename, int fileline)
2520 _Mem_Free(buffer, filename, fileline);
2523 void _PRVM_FreeAll(const char *filename, int fileline)
2526 prog->fielddefs = NULL;
2527 prog->functions = NULL;
2528 _Mem_EmptyPool(prog->progs_mempool, filename, fileline);
2531 // LordHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
2532 unsigned int PRVM_EDICT_NUM_ERROR(unsigned int n, char *filename, int fileline)
2534 PRVM_ERROR ("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", PRVM_NAME, n, filename, fileline);
2539 int NUM_FOR_EDICT_ERROR(prvm_edict_t *e)
2541 PRVM_ERROR ("PRVM_NUM_FOR_EDICT: bad pointer %p (world is %p, entity number would be %i)", e, prog->edicts, e - prog->edicts);
2545 int PRVM_NUM_FOR_EDICT(prvm_edict_t *e)
2548 n = e - prog->edicts;
2549 if ((unsigned int)n >= prog->limit_edicts)
2550 Host_Error ("PRVM_NUM_FOR_EDICT: bad pointer");
2554 //int NoCrash_NUM_FOR_EDICT(prvm_edict_t *e)
2556 // return e - prog->edicts;
2559 //#define PRVM_EDICT_TO_PROG(e) ((unsigned char *)(((prvm_edict_t *)e)->v) - (unsigned char *)(prog->edictsfields))
2560 //#define PRVM_PROG_TO_EDICT(e) (prog->edicts + ((e) / (progs->entityfields * 4)))
2561 int PRVM_EDICT_TO_PROG(prvm_edict_t *e)
2564 n = e - prog->edicts;
2565 if ((unsigned int)n >= (unsigned int)prog->max_edicts)
2566 Host_Error("PRVM_EDICT_TO_PROG: invalid edict %8p (number %i compared to world at %8p)", e, n, prog->edicts);
2567 return n;// EXPERIMENTAL
2568 //return (unsigned char *)e->v - (unsigned char *)prog->edictsfields;
2570 prvm_edict_t *PRVM_PROG_TO_EDICT(int n)
2572 if ((unsigned int)n >= (unsigned int)prog->max_edicts)
2573 Host_Error("PRVM_PROG_TO_EDICT: invalid edict number %i", n);
2574 return prog->edicts + n; // EXPERIMENTAL
2575 //return prog->edicts + ((n) / (progs->entityfields * 4));
2580 sizebuf_t vm_tempstringsbuf;
2582 const char *PRVM_GetString(int num)
2586 if (num < prog->stringssize)
2587 return prog->strings + num;
2590 if (num <= prog->stringssize + vm_tempstringsbuf.maxsize)
2592 num -= prog->stringssize;
2593 if (num < vm_tempstringsbuf.cursize)
2594 return (char *)vm_tempstringsbuf.data + num;
2597 VM_Warning("PRVM_GetString: Invalid temp-string offset (%i >= %i vm_tempstringsbuf.cursize)\n", num, vm_tempstringsbuf.cursize);
2604 VM_Warning("PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
2614 // special range reserved for tempstrings
2616 if (num < vm_tempstringsbuf.cursize)
2617 return (char *)vm_tempstringsbuf.data + num;
2620 VM_Warning("PRVM_GetString: Invalid temp-string offset (%i >= %i vm_tempstringsbuf.cursize)\n", num, vm_tempstringsbuf.cursize);
2626 if (num < prog->numknownstrings)
2628 if (!prog->knownstrings[num])
2630 VM_Warning("PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
2633 return prog->knownstrings[num];
2637 VM_Warning("PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
2643 int PRVM_SetEngineString(const char *s)
2648 if (s >= prog->strings && s <= prog->strings + prog->stringssize)
2649 PRVM_ERROR("PRVM_SetEngineString: s in prog->strings area");
2650 // if it's in the tempstrings area, use a reserved range
2651 // (otherwise we'd get millions of useless string offsets cluttering the database)
2652 if (s >= (char *)vm_tempstringsbuf.data && s < (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.maxsize)
2654 return prog->stringssize + (s - (char *)vm_tempstringsbuf.data);
2656 return -1 - ((1<<30) + (s - (char *)vm_tempstringsbuf.data));
2658 // see if it's a known string address
2659 for (i = 0;i < prog->numknownstrings;i++)
2660 if (prog->knownstrings[i] == s)
2662 // new unknown engine string
2663 if (developer.integer >= 200)
2664 Con_Printf("new engine string %p = \"%s\"\n", s, s);
2665 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2666 if (!prog->knownstrings[i])
2668 if (i >= prog->numknownstrings)
2670 if (i >= prog->maxknownstrings)
2672 const char **oldstrings = prog->knownstrings;
2673 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2674 const char **oldstrings_origin = prog->knownstrings_origin;
2675 prog->maxknownstrings += 128;
2676 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2677 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2678 if(prog->leaktest_active)
2679 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2680 if (prog->numknownstrings)
2682 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2683 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2684 if(prog->leaktest_active)
2685 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
2688 prog->numknownstrings++;
2690 prog->firstfreeknownstring = i + 1;
2691 prog->knownstrings[i] = s;
2692 prog->knownstrings_freeable[i] = false;
2693 if(prog->leaktest_active)
2694 prog->knownstrings_origin[i] = NULL;
2698 // temp string handling
2700 // all tempstrings go into this buffer consecutively, and it is reset
2701 // whenever PRVM_ExecuteProgram returns to the engine
2702 // (technically each PRVM_ExecuteProgram call saves the cursize value and
2703 // restores it on return, so multiple recursive calls can share the same
2705 // the buffer size is automatically grown as needed
2707 int PRVM_SetTempString(const char *s)
2713 size = (int)strlen(s) + 1;
2714 if (developer.integer >= 300)
2715 Con_Printf("PRVM_SetTempString: cursize %i, size %i\n", vm_tempstringsbuf.cursize, size);
2716 if (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
2718 sizebuf_t old = vm_tempstringsbuf;
2719 if (vm_tempstringsbuf.cursize + size >= 1<<28)
2720 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);
2721 vm_tempstringsbuf.maxsize = max(vm_tempstringsbuf.maxsize, 65536);
2722 while (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
2723 vm_tempstringsbuf.maxsize *= 2;
2724 if (vm_tempstringsbuf.maxsize != old.maxsize || vm_tempstringsbuf.data == NULL)
2726 if (developer.integer >= 100)
2727 Con_Printf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, vm_tempstringsbuf.maxsize/1024);
2728 vm_tempstringsbuf.data = (unsigned char *) Mem_Alloc(sv_mempool, vm_tempstringsbuf.maxsize);
2730 memcpy(vm_tempstringsbuf.data, old.data, old.cursize);
2735 t = (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.cursize;
2737 vm_tempstringsbuf.cursize += size;
2738 return PRVM_SetEngineString(t);
2741 int PRVM_AllocString(size_t bufferlength, char **pointer)
2746 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2747 if (!prog->knownstrings[i])
2749 if (i >= prog->numknownstrings)
2751 if (i >= prog->maxknownstrings)
2753 const char **oldstrings = prog->knownstrings;
2754 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2755 const char **oldstrings_origin = prog->knownstrings_origin;
2756 prog->maxknownstrings += 128;
2757 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2758 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2759 if(prog->leaktest_active)
2760 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2761 if (prog->numknownstrings)
2763 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2764 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2765 if(prog->leaktest_active)
2766 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
2768 // TODO why not Mem_Free the old ones?
2770 prog->numknownstrings++;
2772 prog->firstfreeknownstring = i + 1;
2773 prog->knownstrings[i] = (char *)PRVM_Alloc(bufferlength);
2774 prog->knownstrings_freeable[i] = true;
2775 if(prog->leaktest_active)
2776 prog->knownstrings_origin[i] = PRVM_AllocationOrigin();
2778 *pointer = (char *)(prog->knownstrings[i]);
2782 void PRVM_FreeString(int num)
2785 PRVM_ERROR("PRVM_FreeString: attempt to free a NULL string");
2786 else if (num >= 0 && num < prog->stringssize)
2787 PRVM_ERROR("PRVM_FreeString: attempt to free a constant string");
2788 else if (num < 0 && num >= -prog->numknownstrings)
2791 if (!prog->knownstrings[num])
2792 PRVM_ERROR("PRVM_FreeString: attempt to free a non-existent or already freed string");
2793 if (!prog->knownstrings_freeable[num])
2794 PRVM_ERROR("PRVM_FreeString: attempt to free a string owned by the engine");
2795 PRVM_Free((char *)prog->knownstrings[num]);
2796 if(prog->leaktest_active)
2797 if(prog->knownstrings_origin[num])
2798 PRVM_Free((char *)prog->knownstrings_origin[num]);
2799 prog->knownstrings[num] = NULL;
2800 prog->knownstrings_freeable[num] = false;
2801 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
2804 PRVM_ERROR("PRVM_FreeString: invalid string offset %i", num);
2807 static qboolean PRVM_IsStringReferenced(string_t string)
2811 for (i = 0;i < prog->progs->numglobaldefs;i++)
2813 ddef_t *d = &prog->globaldefs[i];
2814 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
2816 if(string == ((prvm_eval_t *) &prog->globals.generic[d->ofs])->string)
2820 for(j = 0; j < prog->num_edicts; ++j)
2822 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2823 if (ed->priv.required->free)
2825 for (i=0; i<prog->progs->numfielddefs; ++i)
2827 ddef_t *d = &prog->fielddefs[i];
2828 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
2830 if(string == ((prvm_eval_t *) &((float*)ed->fields.vp)[d->ofs])->string)
2838 static qboolean PRVM_IsEdictRelevant(prvm_edict_t *edict)
2840 if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
2841 return true; // world or clients
2842 switch(prog - prog_list)
2844 case PRVM_SERVERPROG:
2846 entvars_t *ev = edict->fields.server;
2847 if(ev->solid) // can block other stuff, or is a trigger?
2849 if(ev->modelindex) // visible ent?
2851 if(ev->effects) // particle effect?
2853 if(ev->think) // has a think function?
2854 if(ev->nextthink > 0) // that actually will eventually run?
2858 if(*prvm_leaktest_ignore_classnames.string)
2860 if(strstr(va(" %s ", prvm_leaktest_ignore_classnames.string), va(" %s ", PRVM_GetString(ev->classname))))
2865 case PRVM_CLIENTPROG:
2867 // TODO someone add more stuff here
2868 cl_entvars_t *ev = edict->fields.client;
2869 if(ev->entnum) // csqc networked
2871 if(ev->modelindex) // visible ent?
2873 if(ev->effects) // particle effect?
2875 if(ev->think) // has a think function?
2876 if(ev->nextthink > 0) // that actually will eventually run?
2878 if(*prvm_leaktest_ignore_classnames.string)
2880 if(strstr(va(" %s ", prvm_leaktest_ignore_classnames.string), va(" %s ", PRVM_GetString(ev->classname))))
2886 // menu prog does not have classnames
2892 static qboolean PRVM_IsEdictReferenced(prvm_edict_t *edict, int mark)
2895 int edictnum = PRVM_NUM_FOR_EDICT(edict);
2896 const char *targetname = NULL;
2898 switch(prog - prog_list)
2900 case PRVM_SERVERPROG:
2901 targetname = PRVM_GetString(edict->fields.server->targetname);
2906 if(!*targetname) // ""
2909 for (i = 0;i < prog->progs->numglobaldefs;i++)
2911 ddef_t *d = &prog->globaldefs[i];
2912 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
2914 if(edictnum == ((prvm_eval_t *) &prog->globals.generic[d->ofs])->edict)
2918 for(j = 0; j < prog->num_edicts; ++j)
2920 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2921 if (ed->priv.required->mark < mark)
2927 const char *target = PRVM_GetString(ed->fields.server->target);
2929 if(!strcmp(target, targetname))
2932 for (i=0; i<prog->progs->numfielddefs; ++i)
2934 ddef_t *d = &prog->fielddefs[i];
2935 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
2937 if(edictnum == ((prvm_eval_t *) &((float*)ed->fields.vp)[d->ofs])->edict)
2945 static void PRVM_MarkReferencedEdicts(void)
2951 for(j = 0; j < prog->num_edicts; ++j)
2953 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2954 if(ed->priv.required->free)
2956 ed->priv.required->mark = PRVM_IsEdictRelevant(ed) ? 1 : 0;
2963 for(j = 0; j < prog->num_edicts; ++j)
2965 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2966 if(ed->priv.required->free)
2968 if(ed->priv.required->mark)
2970 if(PRVM_IsEdictReferenced(ed, stage))
2972 ed->priv.required->mark = stage + 1;
2979 Con_DPrintf("leak check used %d stages to find all references\n", stage);
2982 void PRVM_LeakTest(void)
2985 qboolean leaked = false;
2987 if(!prog->leaktest_active)
2991 for (i = 0; i < prog->numknownstrings; ++i)
2993 if(prog->knownstrings[i])
2994 if(prog->knownstrings_freeable[i])
2995 if(prog->knownstrings_origin[i])
2996 if(!PRVM_IsStringReferenced(-1 - i))
2998 Con_Printf("Unreferenced string found!\n Value: %s\n Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
3004 PRVM_MarkReferencedEdicts();
3005 for(j = 0; j < prog->num_edicts; ++j)
3007 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3008 if(ed->priv.required->free)
3010 if(!ed->priv.required->mark)
3011 if(ed->priv.required->allocation_origin)
3013 Con_Printf("Unreferenced edict found!\n Allocated at: %s\n", ed->priv.required->allocation_origin);
3014 PRVM_ED_Print(ed, NULL);
3020 for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
3022 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
3024 if(stringbuffer->origin)
3026 Con_Printf("Open string buffer handle found!\n Allocated at: %s\n", stringbuffer->origin);
3031 for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
3033 if(prog->openfiles[i])
3034 if(prog->openfiles_origin[i])
3036 Con_Printf("Open file handle found!\n Allocated at: %s\n", prog->openfiles_origin[i]);
3041 for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
3043 if(prog->opensearches[i])
3044 if(prog->opensearches_origin[i])
3046 Con_Printf("Open search handle found!\n Allocated at: %s\n", prog->opensearches_origin[i]);
3052 Con_Printf("Congratulations. No leaks found.\n");