2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 static prvm_prog_t prog_list[PRVM_MAXPROGS];
29 int prvm_type_size[8] = {1,sizeof(string_t)/4,1,3,1,1,sizeof(func_t)/4,sizeof(void *)/4};
31 ddef_t *PRVM_ED_FieldAtOfs(int ofs);
32 qboolean PRVM_ED_ParseEpair(prvm_edict_t *ent, ddef_t *key, const char *s, qboolean parsebackslash);
34 // LordHavoc: optional runtime bounds checking (speed drain, but worth it for security, on by default - breaks most QCCX features (used by CRMod and others))
35 #ifdef PRVM_BOUNDSCHECK_CVAR
36 cvar_t prvm_boundscheck = {0, "prvm_boundscheck", "1", "enables detection of out of bounds memory access in the QuakeC code being run (in other words, prevents really exceedingly bad QuakeC code from doing nasty things to your computer)"};
38 // LordHavoc: prints every opcode as it executes - warning: this is significant spew
39 cvar_t prvm_traceqc = {0, "prvm_traceqc", "0", "prints every QuakeC statement as it is executed (only for really thorough debugging!)"};
40 // LordHavoc: counts usage of each QuakeC statement
41 cvar_t prvm_statementprofiling = {0, "prvm_statementprofiling", "0", "counts how many times each QuakeC statement has been executed, these counts are displayed in prvm_printfunction output (if enabled)"};
42 cvar_t prvm_backtraceforwarnings = {0, "prvm_backtraceforwarnings", "0", "print a backtrace for warnings too"};
43 cvar_t prvm_leaktest = {0, "prvm_leaktest", "0", "try to detect memory leaks in strings or entities"};
44 cvar_t prvm_leaktest_ignore_classnames = {0, "prvm_leaktest_ignore_classnames", "", "classnames of entities to NOT leak check because they are found by find(world, classname, ...) but are actually spawned by QC code (NOT map entities)"};
45 cvar_t prvm_errordump = {0, "prvm_errordump", "0", "write a savegame on crash to crash-server.dmp"};
47 extern sizebuf_t vm_tempstringsbuf;
49 //============================================================================
57 void PRVM_MEM_Alloc(void)
61 // reserve space for the null entity aka world
62 // check bound of max_edicts
63 prog->max_edicts = bound(1 + prog->reserved_edicts, prog->max_edicts, prog->limit_edicts);
64 prog->num_edicts = bound(1 + prog->reserved_edicts, prog->num_edicts, prog->max_edicts);
66 // edictprivate_size has to be min as big prvm_edict_private_t
67 prog->edictprivate_size = max(prog->edictprivate_size,(int)sizeof(prvm_edict_private_t));
70 prog->edicts = (prvm_edict_t *)Mem_Alloc(prog->progs_mempool,prog->limit_edicts * sizeof(prvm_edict_t));
72 // alloc edict private space
73 prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size);
76 prog->edictsfields = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edict_size);
79 for(i = 0; i < prog->max_edicts; i++)
81 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
82 prog->edicts[i].fields.vp = (void*)((unsigned char *)prog->edictsfields + i * prog->edict_size);
88 PRVM_MEM_IncreaseEdicts
91 void PRVM_MEM_IncreaseEdicts(void)
94 int oldmaxedicts = prog->max_edicts;
95 void *oldedictsfields = prog->edictsfields;
96 void *oldedictprivate = prog->edictprivate;
98 if(prog->max_edicts >= prog->limit_edicts)
101 PRVM_GCALL(begin_increase_edicts)();
104 prog->max_edicts = min(prog->max_edicts + 256, prog->limit_edicts);
106 prog->edictsfields = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edict_size);
107 prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size);
109 memcpy(prog->edictsfields, oldedictsfields, oldmaxedicts * prog->edict_size);
110 memcpy(prog->edictprivate, oldedictprivate, oldmaxedicts * prog->edictprivate_size);
112 //set e and v pointers
113 for(i = 0; i < prog->max_edicts; i++)
115 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
116 prog->edicts[i].fields.vp = (void*)((unsigned char *)prog->edictsfields + i * prog->edict_size);
119 PRVM_GCALL(end_increase_edicts)();
121 Mem_Free(oldedictsfields);
122 Mem_Free(oldedictprivate);
125 //============================================================================
128 int PRVM_ED_FindFieldOffset(const char *field)
131 d = PRVM_ED_FindField(field);
137 int PRVM_ED_FindGlobalOffset(const char *global)
140 d = PRVM_ED_FindGlobal(global);
146 func_t PRVM_ED_FindFunctionOffset(const char *function)
149 f = PRVM_ED_FindFunction(function);
152 return (func_t)(f - prog->functions);
155 qboolean PRVM_ProgLoaded(int prognr)
157 if(prognr < 0 || prognr >= PRVM_MAXPROGS)
160 return (prog_list[prognr].loaded ? TRUE : FALSE);
165 PRVM_SetProgFromString
168 // perhaps add a return value when the str doesnt exist
169 qboolean PRVM_SetProgFromString(const char *str)
172 for(; i < PRVM_MAXPROGS ; i++)
173 if(prog_list[i].name && !strcmp(prog_list[i].name,str))
175 if(prog_list[i].loaded)
177 prog = &prog_list[i];
182 Con_Printf("%s not loaded !\n",PRVM_NAME);
187 Con_Printf("Invalid program name %s !\n", str);
196 void PRVM_SetProg(int prognr)
198 if(0 <= prognr && prognr < PRVM_MAXPROGS)
200 if(prog_list[prognr].loaded)
201 prog = &prog_list[prognr];
203 PRVM_ERROR("%i not loaded !", prognr);
206 PRVM_ERROR("Invalid program number %i", prognr);
213 Sets everything to NULL
216 void PRVM_ED_ClearEdict (prvm_edict_t *e)
218 memset (e->fields.vp, 0, prog->progs->entityfields * 4);
219 e->priv.required->free = false;
221 // AK: Let the init_edict function determine if something needs to be initialized
222 PRVM_GCALL(init_edict)(e);
225 const char *PRVM_AllocationOrigin()
228 if(prog->leaktest_active)
229 if(prog->depth > 0) // actually in QC code and not just parsing the entities block of a map/savegame
231 buf = (char *)PRVM_Alloc(128);
232 PRVM_ShortStackTrace(buf, 128);
241 Either finds a free edict, or allocates a new one.
242 Try to avoid reusing an entity that was recently freed, because it
243 can cause the client to think the entity morphed into something else
244 instead of being removed and recreated, which can cause interpolated
245 angles and bad trails.
248 prvm_edict_t *PRVM_ED_Alloc (void)
253 // the client qc dont need maxclients
254 // thus it doesnt need to use svs.maxclients
255 // AK: changed i=svs.maxclients+1
256 // AK: changed so the edict 0 wont spawn -> used as reserved/world entity
257 // although the menu/client has no world
258 for (i = prog->reserved_edicts + 1;i < prog->num_edicts;i++)
260 e = PRVM_EDICT_NUM(i);
261 // the first couple seconds of server time can involve a lot of
262 // freeing and allocating, so relax the replacement policy
263 if (e->priv.required->free && ( e->priv.required->freetime < 2 || prog->globaloffsets.time < 0 || (PRVM_GLOBALFIELDVALUE(prog->globaloffsets.time)->_float - e->priv.required->freetime) > 0.5 ) )
265 PRVM_ED_ClearEdict (e);
266 e->priv.required->allocation_origin = PRVM_AllocationOrigin();
271 if (i == prog->limit_edicts)
272 PRVM_ERROR ("%s: PRVM_ED_Alloc: no free edicts",PRVM_NAME);
275 if (prog->num_edicts >= prog->max_edicts)
276 PRVM_MEM_IncreaseEdicts();
278 e = PRVM_EDICT_NUM(i);
279 PRVM_ED_ClearEdict (e);
281 e->priv.required->allocation_origin = PRVM_AllocationOrigin();
290 Marks the edict as free
291 FIXME: walk all entities and NULL out references to this entity
294 void PRVM_ED_Free (prvm_edict_t *ed)
296 // dont delete the null entity (world) or reserved edicts
297 if(PRVM_NUM_FOR_EDICT(ed) <= prog->reserved_edicts )
300 PRVM_GCALL(free_edict)(ed);
302 ed->priv.required->free = true;
303 ed->priv.required->freetime = prog->globaloffsets.time >= 0 ? PRVM_GLOBALFIELDVALUE(prog->globaloffsets.time)->_float : 0;
304 if(ed->priv.required->allocation_origin)
306 PRVM_Free((char *)ed->priv.required->allocation_origin);
307 ed->priv.required->allocation_origin = NULL;
311 //===========================================================================
318 ddef_t *PRVM_ED_GlobalAtOfs (int ofs)
323 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
325 def = &prog->globaldefs[i];
337 ddef_t *PRVM_ED_FieldAtOfs (int ofs)
342 for (i=0 ; i<prog->progs->numfielddefs ; i++)
344 def = &prog->fielddefs[i];
356 ddef_t *PRVM_ED_FindField (const char *name)
361 for (i=0 ; i<prog->progs->numfielddefs ; i++)
363 def = &prog->fielddefs[i];
364 if (!strcmp(PRVM_GetString(def->s_name), name))
375 ddef_t *PRVM_ED_FindGlobal (const char *name)
380 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
382 def = &prog->globaldefs[i];
383 if (!strcmp(PRVM_GetString(def->s_name), name))
395 mfunction_t *PRVM_ED_FindFunction (const char *name)
400 for (i=0 ; i<prog->progs->numfunctions ; i++)
402 func = &prog->functions[i];
403 if (!strcmp(PRVM_GetString(func->s_name), name))
414 Returns a string describing *data in a type specific manner
417 char *PRVM_ValueString (etype_t type, prvm_eval_t *val)
419 static char line[MAX_INPUTLINE];
424 type = (etype_t)((int) type & ~DEF_SAVEGLOBAL);
429 strlcpy (line, PRVM_GetString (val->string), sizeof (line));
433 if (n < 0 || n >= prog->limit_edicts)
434 dpsnprintf (line, sizeof(line), "entity %i (invalid!)", n);
436 dpsnprintf (line, sizeof(line), "entity %i", n);
439 f = prog->functions + val->function;
440 dpsnprintf (line, sizeof(line), "%s()", PRVM_GetString(f->s_name));
443 def = PRVM_ED_FieldAtOfs ( val->_int );
444 dpsnprintf (line, sizeof(line), ".%s", PRVM_GetString(def->s_name));
447 dpsnprintf (line, sizeof(line), "void");
450 // LordHavoc: changed from %5.1f to %10.4f
451 dpsnprintf (line, sizeof(line), "%10.4f", val->_float);
454 // LordHavoc: changed from %5.1f to %10.4f
455 dpsnprintf (line, sizeof(line), "'%10.4f %10.4f %10.4f'", val->vector[0], val->vector[1], val->vector[2]);
458 dpsnprintf (line, sizeof(line), "pointer");
461 dpsnprintf (line, sizeof(line), "bad type %i", (int) type);
472 Returns a string describing *data in a type specific manner
473 Easier to parse than PR_ValueString
476 char *PRVM_UglyValueString (etype_t type, prvm_eval_t *val)
478 static char line[MAX_INPUTLINE];
484 type = (etype_t)((int)type & ~DEF_SAVEGLOBAL);
489 // Parse the string a bit to turn special characters
490 // (like newline, specifically) into escape codes,
491 // this fixes saving games from various mods
492 s = PRVM_GetString (val->string);
493 for (i = 0;i < (int)sizeof(line) - 2 && *s;)
517 dpsnprintf (line, sizeof (line), "%i", PRVM_NUM_FOR_EDICT(PRVM_PROG_TO_EDICT(val->edict)));
520 f = prog->functions + val->function;
521 strlcpy (line, PRVM_GetString (f->s_name), sizeof (line));
524 def = PRVM_ED_FieldAtOfs ( val->_int );
525 dpsnprintf (line, sizeof (line), ".%s", PRVM_GetString(def->s_name));
528 dpsnprintf (line, sizeof (line), "void");
531 dpsnprintf (line, sizeof (line), "%f", val->_float);
534 dpsnprintf (line, sizeof (line), "%f %f %f", val->vector[0], val->vector[1], val->vector[2]);
537 dpsnprintf (line, sizeof (line), "bad type %i", type);
548 Returns a string with a description and the contents of a global,
549 padded to 20 field width
552 char *PRVM_GlobalString (int ofs)
558 static char line[128];
560 val = (void *)&prog->globals.generic[ofs];
561 def = PRVM_ED_GlobalAtOfs(ofs);
563 dpsnprintf (line, sizeof(line), "GLOBAL%i", ofs);
566 s = PRVM_ValueString ((etype_t)def->type, (prvm_eval_t *)val);
567 dpsnprintf (line, sizeof(line), "%s (=%s)", PRVM_GetString(def->s_name), s);
571 //for ( ; i<20 ; i++)
572 // strcat (line," ");
578 char *PRVM_GlobalStringNoContents (int ofs)
582 static char line[128];
584 def = PRVM_ED_GlobalAtOfs(ofs);
586 dpsnprintf (line, sizeof(line), "GLOBAL%i", ofs);
588 dpsnprintf (line, sizeof(line), "%s", PRVM_GetString(def->s_name));
591 //for ( ; i<20 ; i++)
592 // strcat (line," ");
606 // LordHavoc: optimized this to print out much more quickly (tempstring)
607 // LordHavoc: changed to print out every 4096 characters (incase there are a lot of fields to print)
608 void PRVM_ED_Print(prvm_edict_t *ed, const char *wildcard_fieldname)
616 char tempstring[MAX_INPUTLINE], tempstring2[260]; // temporary string buffers
618 if (ed->priv.required->free)
620 Con_Printf("%s: FREE\n",PRVM_NAME);
625 dpsnprintf(tempstring, sizeof(tempstring), "\n%s EDICT %i:\n", PRVM_NAME, PRVM_NUM_FOR_EDICT(ed));
626 for (i=1 ; i<prog->progs->numfielddefs ; i++)
628 d = &prog->fielddefs[i];
629 name = PRVM_GetString(d->s_name);
630 if (name[strlen(name)-2] == '_')
631 continue; // skip _x, _y, _z vars
633 // Check Field Name Wildcard
634 if(wildcard_fieldname)
635 if( !matchpattern(name, wildcard_fieldname, 1) )
636 // Didn't match; skip
639 v = (int *)((char *)ed->fields.vp + d->ofs*4);
641 // if the value is still all 0, skip the field
642 type = d->type & ~DEF_SAVEGLOBAL;
644 for (j=0 ; j<prvm_type_size[type] ; j++)
647 if (j == prvm_type_size[type])
650 if (strlen(name) > sizeof(tempstring2)-4)
652 memcpy (tempstring2, name, sizeof(tempstring2)-4);
653 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
654 tempstring2[sizeof(tempstring2)-1] = 0;
657 strlcat(tempstring, name, sizeof(tempstring));
658 for (l = strlen(name);l < 14;l++)
659 strlcat(tempstring, " ", sizeof(tempstring));
660 strlcat(tempstring, " ", sizeof(tempstring));
662 name = PRVM_ValueString((etype_t)d->type, (prvm_eval_t *)v);
663 if (strlen(name) > sizeof(tempstring2)-4)
665 memcpy (tempstring2, name, sizeof(tempstring2)-4);
666 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
667 tempstring2[sizeof(tempstring2)-1] = 0;
670 strlcat(tempstring, name, sizeof(tempstring));
671 strlcat(tempstring, "\n", sizeof(tempstring));
672 if (strlen(tempstring) >= sizeof(tempstring)/2)
674 Con_Print(tempstring);
679 Con_Print(tempstring);
689 void PRVM_ED_Write (qfile_t *f, prvm_edict_t *ed)
699 if (ed->priv.required->free)
705 for (i=1 ; i<prog->progs->numfielddefs ; i++)
707 d = &prog->fielddefs[i];
708 name = PRVM_GetString(d->s_name);
709 if (name[strlen(name)-2] == '_')
710 continue; // skip _x, _y, _z vars
712 v = (int *)((char *)ed->fields.vp + d->ofs*4);
714 // if the value is still all 0, skip the field
715 type = d->type & ~DEF_SAVEGLOBAL;
716 for (j=0 ; j<prvm_type_size[type] ; j++)
719 if (j == prvm_type_size[type])
722 FS_Printf(f,"\"%s\" ",name);
723 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString((etype_t)d->type, (prvm_eval_t *)v));
729 void PRVM_ED_PrintNum (int ent, const char *wildcard_fieldname)
731 PRVM_ED_Print(PRVM_EDICT_NUM(ent), wildcard_fieldname);
736 PRVM_ED_PrintEdicts_f
738 For debugging, prints all the entities in the current server
741 void PRVM_ED_PrintEdicts_f (void)
744 const char *wildcard_fieldname;
746 if(Cmd_Argc() < 2 || Cmd_Argc() > 3)
748 Con_Print("prvm_edicts <program name> <optional field name wildcard>\n");
753 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
757 wildcard_fieldname = Cmd_Argv(2);
759 wildcard_fieldname = NULL;
761 Con_Printf("%s: %i entities\n", PRVM_NAME, prog->num_edicts);
762 for (i=0 ; i<prog->num_edicts ; i++)
763 PRVM_ED_PrintNum (i, wildcard_fieldname);
772 For debugging, prints a single edict
775 void PRVM_ED_PrintEdict_f (void)
778 const char *wildcard_fieldname;
780 if(Cmd_Argc() < 3 || Cmd_Argc() > 4)
782 Con_Print("prvm_edict <program name> <edict number> <optional field name wildcard>\n");
787 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
790 i = atoi (Cmd_Argv(2));
791 if (i >= prog->num_edicts)
793 Con_Print("Bad edict number\n");
798 // Optional Wildcard Provided
799 wildcard_fieldname = Cmd_Argv(3);
802 wildcard_fieldname = NULL;
803 PRVM_ED_PrintNum (i, wildcard_fieldname);
815 // 2 possibilities : 1. just displaying the active edict count
816 // 2. making a function pointer [x]
817 void PRVM_ED_Count_f (void)
825 Con_Print("prvm_count <program name>\n");
830 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
833 if(prog->count_edicts)
834 prog->count_edicts();
838 for (i=0 ; i<prog->num_edicts ; i++)
840 ent = PRVM_EDICT_NUM(i);
841 if (ent->priv.required->free)
846 Con_Printf("num_edicts:%3i\n", prog->num_edicts);
847 Con_Printf("active :%3i\n", active);
854 ==============================================================================
858 FIXME: need to tag constants, doesn't really work
859 ==============================================================================
867 void PRVM_ED_WriteGlobals (qfile_t *f)
875 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
877 def = &prog->globaldefs[i];
879 if ( !(def->type & DEF_SAVEGLOBAL) )
881 type &= ~DEF_SAVEGLOBAL;
883 if (type != ev_string && type != ev_float && type != ev_entity)
886 name = PRVM_GetString(def->s_name);
887 FS_Printf(f,"\"%s\" ", name);
888 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString((etype_t)type, (prvm_eval_t *)&prog->globals.generic[def->ofs]));
898 void PRVM_ED_ParseGlobals (const char *data)
900 char keyname[MAX_INPUTLINE];
906 if (!COM_ParseToken_Simple(&data, false, false))
907 PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace");
908 if (com_token[0] == '}')
911 strlcpy (keyname, com_token, sizeof(keyname));
914 if (!COM_ParseToken_Simple(&data, false, true))
915 PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace");
917 if (com_token[0] == '}')
918 PRVM_ERROR ("PRVM_ED_ParseGlobals: closing brace without data");
920 key = PRVM_ED_FindGlobal (keyname);
923 Con_DPrintf("'%s' is not a global on %s\n", keyname, PRVM_NAME);
927 if (!PRVM_ED_ParseEpair(NULL, key, com_token, true))
928 PRVM_ERROR ("PRVM_ED_ParseGlobals: parse error");
932 //============================================================================
939 Can parse either fields or globals
940 returns false if error
943 qboolean PRVM_ED_ParseEpair(prvm_edict_t *ent, ddef_t *key, const char *s, qboolean parsebackslash)
952 val = (prvm_eval_t *)((int *)ent->fields.vp + key->ofs);
954 val = (prvm_eval_t *)((int *)prog->globals.generic + key->ofs);
955 switch (key->type & ~DEF_SAVEGLOBAL)
958 l = (int)strlen(s) + 1;
959 val->string = PRVM_AllocString(l, &new_p);
960 for (i = 0;i < l;i++)
962 if (s[i] == '\\' && s[i+1] && parsebackslash)
967 else if (s[i] == 'r')
978 while (*s && *s <= ' ')
980 val->_float = atof(s);
984 for (i = 0;i < 3;i++)
986 while (*s && *s <= ' ')
990 val->vector[i] = atof(s);
999 while (*s && *s <= ' ')
1002 if (i >= prog->limit_edicts)
1003 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);
1004 while (i >= prog->max_edicts)
1005 PRVM_MEM_IncreaseEdicts();
1006 // if IncreaseEdicts was called the base pointer needs to be updated
1008 val = (prvm_eval_t *)((int *)ent->fields.vp + key->ofs);
1009 val->edict = PRVM_EDICT_TO_PROG(PRVM_EDICT_NUM((int)i));
1015 Con_DPrintf("PRVM_ED_ParseEpair: Bogus field name %s in %s\n", s, PRVM_NAME);
1018 def = PRVM_ED_FindField(s + 1);
1021 Con_DPrintf("PRVM_ED_ParseEpair: Can't find field %s in %s\n", s, PRVM_NAME);
1024 val->_int = def->ofs;
1028 func = PRVM_ED_FindFunction(s);
1031 Con_Printf("PRVM_ED_ParseEpair: Can't find function %s in %s\n", s, PRVM_NAME);
1034 val->function = func - prog->functions;
1038 Con_Printf("PRVM_ED_ParseEpair: Unknown key->type %i for key \"%s\" on %s\n", key->type, PRVM_GetString(key->s_name), PRVM_NAME);
1048 Console command to send a string to QC function GameCommand of the
1052 sv_cmd adminmsg 3 "do not teamkill"
1053 cl_cmd someclientcommand
1054 menu_cmd somemenucommand
1056 All progs can support this extension; sg calls it in server QC, cg in client
1060 void PRVM_GameCommand(const char *whichprogs, const char *whichcmd)
1064 Con_Printf("%s text...\n", whichcmd);
1069 if(!PRVM_SetProgFromString(whichprogs))
1070 // note: this is not PRVM_SetProg because that one aborts "hard" using PRVM_Error
1071 // also, it makes printing error messages easier!
1073 Con_Printf("%s program not loaded.\n", whichprogs);
1077 if(!prog->funcoffsets.GameCommand)
1079 Con_Printf("%s program do not support GameCommand!\n", whichprogs);
1083 int restorevm_tempstringsbuf_cursize;
1088 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
1089 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(s ? s : "");
1090 PRVM_ExecuteProgram (prog->funcoffsets.GameCommand, "QC function GameCommand is missing");
1091 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1096 void PRVM_GameCommand_Server_f(void)
1098 PRVM_GameCommand("server", "sv_cmd");
1100 void PRVM_GameCommand_Client_f(void)
1102 PRVM_GameCommand("client", "cl_cmd");
1104 void PRVM_GameCommand_Menu_f(void)
1106 PRVM_GameCommand("menu", "menu_cmd");
1113 Console command to set a field of a specified edict
1116 void PRVM_ED_EdictSet_f(void)
1123 Con_Print("prvm_edictset <program name> <edict number> <field> <value>\n");
1128 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1130 Con_Printf("Wrong program name %s !\n", Cmd_Argv(1));
1134 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1136 if((key = PRVM_ED_FindField(Cmd_Argv(3))) == 0)
1137 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1139 PRVM_ED_ParseEpair(ed, key, Cmd_Argv(4), true);
1145 ====================
1148 Parses an edict out of the given string, returning the new position
1149 ed should be a properly initialized empty edict.
1150 Used for initial level load and for savegames.
1151 ====================
1153 extern cvar_t developer_entityparsing;
1154 const char *PRVM_ED_ParseEdict (const char *data, prvm_edict_t *ent)
1164 // go through all the dictionary pairs
1168 if (!COM_ParseToken_Simple(&data, false, false))
1169 PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1170 if (developer_entityparsing.integer)
1171 Con_Printf("Key: \"%s\"", com_token);
1172 if (com_token[0] == '}')
1175 // anglehack is to allow QuakeEd to write single scalar angles
1176 // and allow them to be turned into vectors. (FIXME...)
1177 if (!strcmp(com_token, "angle"))
1179 strlcpy (com_token, "angles", sizeof(com_token));
1185 // FIXME: change light to _light to get rid of this hack
1186 if (!strcmp(com_token, "light"))
1187 strlcpy (com_token, "light_lev", sizeof(com_token)); // hack for single light def
1189 strlcpy (keyname, com_token, sizeof(keyname));
1191 // another hack to fix keynames with trailing spaces
1192 n = strlen(keyname);
1193 while (n && keyname[n-1] == ' ')
1200 if (!COM_ParseToken_Simple(&data, false, false))
1201 PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1202 if (developer_entityparsing.integer)
1203 Con_Printf(" \"%s\"\n", com_token);
1205 if (com_token[0] == '}')
1206 PRVM_ERROR ("PRVM_ED_ParseEdict: closing brace without data");
1210 // ignore attempts to set key "" (this problem occurs in nehahra neh1m8.bsp)
1214 // keynames with a leading underscore are used for utility comments,
1215 // and are immediately discarded by quake
1216 if (keyname[0] == '_')
1219 key = PRVM_ED_FindField (keyname);
1222 Con_DPrintf("%s: '%s' is not a field\n", PRVM_NAME, keyname);
1229 strlcpy (temp, com_token, sizeof(temp));
1230 dpsnprintf (com_token, sizeof(com_token), "0 %s 0", temp);
1233 if (!PRVM_ED_ParseEpair(ent, key, com_token, strcmp(keyname, "wad") != 0))
1234 PRVM_ERROR ("PRVM_ED_ParseEdict: parse error");
1238 ent->priv.required->free = true;
1246 PRVM_ED_LoadFromFile
1248 The entities are directly placed in the array, rather than allocated with
1249 PRVM_ED_Alloc, because otherwise an error loading the map would have entity
1250 number references out of order.
1252 Creates a server's entity / program execution context by
1253 parsing textual entity definitions out of an ent file.
1255 Used for both fresh maps and savegame loads. A fresh map would also need
1256 to call PRVM_ED_CallSpawnFunctions () to let the objects initialize themselves.
1259 void PRVM_ED_LoadFromFile (const char *data)
1262 int parsed, inhibited, spawned, died;
1263 const char *funcname;
1275 // parse the opening brace
1276 if (!COM_ParseToken_Simple(&data, false, false))
1278 if (com_token[0] != '{')
1279 PRVM_ERROR ("PRVM_ED_LoadFromFile: %s: found %s when expecting {", PRVM_NAME, com_token);
1281 // CHANGED: this is not conform to PR_LoadFromFile
1282 if(prog->loadintoworld)
1284 prog->loadintoworld = false;
1285 ent = PRVM_EDICT_NUM(0);
1288 ent = PRVM_ED_Alloc();
1291 if (ent != prog->edicts) // hack
1292 memset (ent->fields.vp, 0, prog->progs->entityfields * 4);
1294 data = PRVM_ED_ParseEdict (data, ent);
1297 // remove the entity ?
1298 if(prog->load_edict && !prog->load_edict(ent))
1306 // immediately call spawn function, but only if there is a self global and a classname
1308 if(prog->globaloffsets.self >= 0 && prog->fieldoffsets.classname >= 0)
1310 string_t handle = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.classname)->string;
1313 Con_Print("No classname for:\n");
1314 PRVM_ED_Print(ent, NULL);
1319 // look for the spawn function
1320 funcname = PRVM_GetString(handle);
1321 func = PRVM_ED_FindFunction (va("spawnfunc_%s", funcname));
1323 if(prog->globaloffsets.require_spawnfunc_prefix < 0)
1324 func = PRVM_ED_FindFunction (funcname);
1328 // check for OnEntityNoSpawnFunction
1329 if (prog->funcoffsets.SV_OnEntityNoSpawnFunction)
1332 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1333 PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityNoSpawnFunction, "QC function SV_OnEntityNoSpawnFunction is missing");
1337 if (developer.integer) // don't confuse non-developers with errors
1339 Con_Print("No spawn function for:\n");
1340 PRVM_ED_Print(ent, NULL);
1349 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1350 PRVM_ExecuteProgram (func - prog->functions, "");
1355 if (ent->priv.required->free)
1359 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);
1362 void PRVM_FindOffsets(void)
1364 // field and global searches use -1 for NULL
1365 memset(&prog->fieldoffsets, -1, sizeof(prog->fieldoffsets));
1366 memset(&prog->globaloffsets, -1, sizeof(prog->globaloffsets));
1367 // functions use 0 for NULL
1368 memset(&prog->funcoffsets, 0, sizeof(prog->funcoffsets));
1370 // server and client qc use a lot of similar fields, so this is combined
1371 prog->fieldoffsets.SendEntity = PRVM_ED_FindFieldOffset("SendEntity");
1372 prog->fieldoffsets.SendFlags = PRVM_ED_FindFieldOffset("SendFlags");
1373 prog->fieldoffsets.Version = PRVM_ED_FindFieldOffset("Version");
1374 prog->fieldoffsets.alpha = PRVM_ED_FindFieldOffset("alpha");
1375 prog->fieldoffsets.ammo_cells1 = PRVM_ED_FindFieldOffset("ammo_cells1");
1376 prog->fieldoffsets.ammo_lava_nails = PRVM_ED_FindFieldOffset("ammo_lava_nails");
1377 prog->fieldoffsets.ammo_multi_rockets = PRVM_ED_FindFieldOffset("ammo_multi_rockets");
1378 prog->fieldoffsets.ammo_nails1 = PRVM_ED_FindFieldOffset("ammo_nails1");
1379 prog->fieldoffsets.ammo_plasma = PRVM_ED_FindFieldOffset("ammo_plasma");
1380 prog->fieldoffsets.ammo_rockets1 = PRVM_ED_FindFieldOffset("ammo_rockets1");
1381 prog->fieldoffsets.ammo_shells1 = PRVM_ED_FindFieldOffset("ammo_shells1");
1382 prog->fieldoffsets.angles = PRVM_ED_FindFieldOffset("angles");
1383 prog->fieldoffsets.button3 = PRVM_ED_FindFieldOffset("button3");
1384 prog->fieldoffsets.button4 = PRVM_ED_FindFieldOffset("button4");
1385 prog->fieldoffsets.button5 = PRVM_ED_FindFieldOffset("button5");
1386 prog->fieldoffsets.button6 = PRVM_ED_FindFieldOffset("button6");
1387 prog->fieldoffsets.button7 = PRVM_ED_FindFieldOffset("button7");
1388 prog->fieldoffsets.button8 = PRVM_ED_FindFieldOffset("button8");
1389 prog->fieldoffsets.button9 = PRVM_ED_FindFieldOffset("button9");
1390 prog->fieldoffsets.button10 = PRVM_ED_FindFieldOffset("button10");
1391 prog->fieldoffsets.button11 = PRVM_ED_FindFieldOffset("button11");
1392 prog->fieldoffsets.button12 = PRVM_ED_FindFieldOffset("button12");
1393 prog->fieldoffsets.button13 = PRVM_ED_FindFieldOffset("button13");
1394 prog->fieldoffsets.button14 = PRVM_ED_FindFieldOffset("button14");
1395 prog->fieldoffsets.button15 = PRVM_ED_FindFieldOffset("button15");
1396 prog->fieldoffsets.button16 = PRVM_ED_FindFieldOffset("button16");
1397 prog->fieldoffsets.buttonchat = PRVM_ED_FindFieldOffset("buttonchat");
1398 prog->fieldoffsets.buttonuse = PRVM_ED_FindFieldOffset("buttonuse");
1399 prog->fieldoffsets.chain = PRVM_ED_FindFieldOffset("chain");
1400 prog->fieldoffsets.classname = PRVM_ED_FindFieldOffset("classname");
1401 prog->fieldoffsets.clientcolors = PRVM_ED_FindFieldOffset("clientcolors");
1402 prog->fieldoffsets.color = PRVM_ED_FindFieldOffset("color");
1403 prog->fieldoffsets.colormod = PRVM_ED_FindFieldOffset("colormod");
1404 prog->fieldoffsets.contentstransition = PRVM_ED_FindFieldOffset("contentstransition");
1405 prog->fieldoffsets.cursor_active = PRVM_ED_FindFieldOffset("cursor_active");
1406 prog->fieldoffsets.cursor_screen = PRVM_ED_FindFieldOffset("cursor_screen");
1407 prog->fieldoffsets.cursor_trace_endpos = PRVM_ED_FindFieldOffset("cursor_trace_endpos");
1408 prog->fieldoffsets.cursor_trace_ent = PRVM_ED_FindFieldOffset("cursor_trace_ent");
1409 prog->fieldoffsets.cursor_trace_start = PRVM_ED_FindFieldOffset("cursor_trace_start");
1410 prog->fieldoffsets.customizeentityforclient = PRVM_ED_FindFieldOffset("customizeentityforclient");
1411 prog->fieldoffsets.dimension_hit = PRVM_ED_FindFieldOffset("dimension_hit");
1412 prog->fieldoffsets.dimension_solid = PRVM_ED_FindFieldOffset("dimension_solid");
1413 prog->fieldoffsets.disableclientprediction = PRVM_ED_FindFieldOffset("disableclientprediction");
1414 prog->fieldoffsets.dphitcontentsmask = PRVM_ED_FindFieldOffset("dphitcontentsmask");
1415 prog->fieldoffsets.drawonlytoclient = PRVM_ED_FindFieldOffset("drawonlytoclient");
1416 prog->fieldoffsets.exteriormodeltoclient = PRVM_ED_FindFieldOffset("exteriormodeltoclient");
1417 prog->fieldoffsets.fatness = PRVM_ED_FindFieldOffset("fatness");
1418 prog->fieldoffsets.forceshader = PRVM_ED_FindFieldOffset("forceshader");
1419 prog->fieldoffsets.frame = PRVM_ED_FindFieldOffset("frame");
1420 prog->fieldoffsets.frame1time = PRVM_ED_FindFieldOffset("frame1time");
1421 prog->fieldoffsets.frame2 = PRVM_ED_FindFieldOffset("frame2");
1422 prog->fieldoffsets.frame2time = PRVM_ED_FindFieldOffset("frame2time");
1423 prog->fieldoffsets.fullbright = PRVM_ED_FindFieldOffset("fullbright");
1424 prog->fieldoffsets.glow_color = PRVM_ED_FindFieldOffset("glow_color");
1425 prog->fieldoffsets.glow_size = PRVM_ED_FindFieldOffset("glow_size");
1426 prog->fieldoffsets.glow_trail = PRVM_ED_FindFieldOffset("glow_trail");
1427 prog->fieldoffsets.gravity = PRVM_ED_FindFieldOffset("gravity");
1428 prog->fieldoffsets.groundentity = PRVM_ED_FindFieldOffset("groundentity");
1429 prog->fieldoffsets.hull = PRVM_ED_FindFieldOffset("hull");
1430 prog->fieldoffsets.ideal_yaw = PRVM_ED_FindFieldOffset("ideal_yaw");
1431 prog->fieldoffsets.idealpitch = PRVM_ED_FindFieldOffset("idealpitch");
1432 prog->fieldoffsets.items2 = PRVM_ED_FindFieldOffset("items2");
1433 prog->fieldoffsets.lerpfrac = PRVM_ED_FindFieldOffset("lerpfrac");
1434 prog->fieldoffsets.light_lev = PRVM_ED_FindFieldOffset("light_lev");
1435 prog->fieldoffsets.message = PRVM_ED_FindFieldOffset("message");
1436 prog->fieldoffsets.modelflags = PRVM_ED_FindFieldOffset("modelflags");
1437 prog->fieldoffsets.movement = PRVM_ED_FindFieldOffset("movement");
1438 prog->fieldoffsets.movetypesteplandevent = PRVM_ED_FindFieldOffset("movetypesteplandevent");
1439 prog->fieldoffsets.netaddress = PRVM_ED_FindFieldOffset("netaddress");
1440 prog->fieldoffsets.nextthink = PRVM_ED_FindFieldOffset("nextthink");
1441 prog->fieldoffsets.nodrawtoclient = PRVM_ED_FindFieldOffset("nodrawtoclient");
1442 prog->fieldoffsets.pflags = PRVM_ED_FindFieldOffset("pflags");
1443 prog->fieldoffsets.ping = PRVM_ED_FindFieldOffset("ping");
1444 prog->fieldoffsets.pitch_speed = PRVM_ED_FindFieldOffset("pitch_speed");
1445 prog->fieldoffsets.playermodel = PRVM_ED_FindFieldOffset("playermodel");
1446 prog->fieldoffsets.playerskin = PRVM_ED_FindFieldOffset("playerskin");
1447 prog->fieldoffsets.pmodel = PRVM_ED_FindFieldOffset("pmodel");
1448 prog->fieldoffsets.punchvector = PRVM_ED_FindFieldOffset("punchvector");
1449 prog->fieldoffsets.renderamt = PRVM_ED_FindFieldOffset("renderamt"); // HalfLife support
1450 prog->fieldoffsets.renderflags = PRVM_ED_FindFieldOffset("renderflags");
1451 prog->fieldoffsets.rendermode = PRVM_ED_FindFieldOffset("rendermode"); // HalfLife support
1452 prog->fieldoffsets.scale = PRVM_ED_FindFieldOffset("scale");
1453 prog->fieldoffsets.style = PRVM_ED_FindFieldOffset("style");
1454 prog->fieldoffsets.tag_entity = PRVM_ED_FindFieldOffset("tag_entity");
1455 prog->fieldoffsets.tag_index = PRVM_ED_FindFieldOffset("tag_index");
1456 prog->fieldoffsets.think = PRVM_ED_FindFieldOffset("think");
1457 prog->fieldoffsets.viewmodelforclient = PRVM_ED_FindFieldOffset("viewmodelforclient");
1458 prog->fieldoffsets.viewzoom = PRVM_ED_FindFieldOffset("viewzoom");
1459 prog->fieldoffsets.yaw_speed = PRVM_ED_FindFieldOffset("yaw_speed");
1460 prog->fieldoffsets.clientcamera = PRVM_ED_FindFieldOffset("clientcamera");
1461 prog->fieldoffsets.clientstatus = PRVM_ED_FindFieldOffset("clientstatus");
1462 prog->funcoffsets.CSQC_ConsoleCommand = PRVM_ED_FindFunctionOffset("CSQC_ConsoleCommand");
1463 prog->funcoffsets.CSQC_Ent_Remove = PRVM_ED_FindFunctionOffset("CSQC_Ent_Remove");
1464 prog->funcoffsets.CSQC_Ent_Update = PRVM_ED_FindFunctionOffset("CSQC_Ent_Update");
1465 prog->funcoffsets.CSQC_Ent_Spawn = PRVM_ED_FindFunctionOffset("CSQC_Ent_Spawn");
1466 prog->funcoffsets.CSQC_Event = PRVM_ED_FindFunctionOffset("CSQC_Event");
1467 prog->funcoffsets.CSQC_Event_Sound = PRVM_ED_FindFunctionOffset("CSQC_Event_Sound");
1468 prog->funcoffsets.CSQC_Init = PRVM_ED_FindFunctionOffset("CSQC_Init");
1469 prog->funcoffsets.CSQC_InputEvent = PRVM_ED_FindFunctionOffset("CSQC_InputEvent");
1470 prog->funcoffsets.CSQC_Parse_CenterPrint = PRVM_ED_FindFunctionOffset("CSQC_Parse_CenterPrint");
1471 prog->funcoffsets.CSQC_Parse_Print = PRVM_ED_FindFunctionOffset("CSQC_Parse_Print");
1472 prog->funcoffsets.CSQC_Parse_StuffCmd = PRVM_ED_FindFunctionOffset("CSQC_Parse_StuffCmd");
1473 prog->funcoffsets.CSQC_Parse_TempEntity = PRVM_ED_FindFunctionOffset("CSQC_Parse_TempEntity");
1474 prog->funcoffsets.CSQC_Shutdown = PRVM_ED_FindFunctionOffset("CSQC_Shutdown");
1475 prog->funcoffsets.CSQC_UpdateView = PRVM_ED_FindFunctionOffset("CSQC_UpdateView");
1476 prog->funcoffsets.Gecko_Query = PRVM_ED_FindFunctionOffset("Gecko_Query");
1477 prog->funcoffsets.EndFrame = PRVM_ED_FindFunctionOffset("EndFrame");
1478 prog->funcoffsets.RestoreGame = PRVM_ED_FindFunctionOffset("RestoreGame");
1479 prog->funcoffsets.SV_ChangeTeam = PRVM_ED_FindFunctionOffset("SV_ChangeTeam");
1480 prog->funcoffsets.SV_ParseClientCommand = PRVM_ED_FindFunctionOffset("SV_ParseClientCommand");
1481 prog->funcoffsets.SV_PlayerPhysics = PRVM_ED_FindFunctionOffset("SV_PlayerPhysics");
1482 prog->funcoffsets.SV_OnEntityNoSpawnFunction = PRVM_ED_FindFunctionOffset("SV_OnEntityNoSpawnFunction");
1483 prog->funcoffsets.GameCommand = PRVM_ED_FindFunctionOffset("GameCommand");
1484 prog->funcoffsets.SV_Shutdown = PRVM_ED_FindFunctionOffset("SV_Shutdown");
1485 prog->funcoffsets.URI_Get_Callback = PRVM_ED_FindFunctionOffset("URI_Get_Callback");
1486 prog->globaloffsets.SV_InitCmd = PRVM_ED_FindGlobalOffset("SV_InitCmd");
1487 prog->globaloffsets.self = PRVM_ED_FindGlobalOffset("self");
1488 prog->globaloffsets.time = PRVM_ED_FindGlobalOffset("time");
1489 prog->globaloffsets.v_forward = PRVM_ED_FindGlobalOffset("v_forward");
1490 prog->globaloffsets.v_right = PRVM_ED_FindGlobalOffset("v_right");
1491 prog->globaloffsets.v_up = PRVM_ED_FindGlobalOffset("v_up");
1492 prog->globaloffsets.view_angles = PRVM_ED_FindGlobalOffset("view_angles");
1493 prog->globaloffsets.trace_allsolid = PRVM_ED_FindGlobalOffset("trace_allsolid");
1494 prog->globaloffsets.trace_startsolid = PRVM_ED_FindGlobalOffset("trace_startsolid");
1495 prog->globaloffsets.trace_fraction = PRVM_ED_FindGlobalOffset("trace_fraction");
1496 prog->globaloffsets.trace_inwater = PRVM_ED_FindGlobalOffset("trace_inwater");
1497 prog->globaloffsets.trace_inopen = PRVM_ED_FindGlobalOffset("trace_inopen");
1498 prog->globaloffsets.trace_endpos = PRVM_ED_FindGlobalOffset("trace_endpos");
1499 prog->globaloffsets.trace_plane_normal = PRVM_ED_FindGlobalOffset("trace_plane_normal");
1500 prog->globaloffsets.trace_plane_dist = PRVM_ED_FindGlobalOffset("trace_plane_dist");
1501 prog->globaloffsets.trace_ent = PRVM_ED_FindGlobalOffset("trace_ent");
1502 prog->globaloffsets.trace_dphitcontents = PRVM_ED_FindGlobalOffset("trace_dphitcontents");
1503 prog->globaloffsets.trace_dphitq3surfaceflags = PRVM_ED_FindGlobalOffset("trace_dphitq3surfaceflags");
1504 prog->globaloffsets.trace_dphittexturename = PRVM_ED_FindGlobalOffset("trace_dphittexturename");
1505 prog->globaloffsets.trace_dpstartcontents = PRVM_ED_FindGlobalOffset("trace_dpstartcontents");
1506 prog->globaloffsets.intermission = PRVM_ED_FindGlobalOffset("intermission");
1507 prog->globaloffsets.coop = PRVM_ED_FindGlobalOffset("coop");
1508 prog->globaloffsets.deathmatch = PRVM_ED_FindGlobalOffset("deathmatch");
1509 prog->globaloffsets.dmg_take = PRVM_ED_FindGlobalOffset("dmg_take");
1510 prog->globaloffsets.dmg_save = PRVM_ED_FindGlobalOffset("dmg_save");
1511 prog->globaloffsets.dmg_origin = PRVM_ED_FindGlobalOffset("dmg_origin");
1512 prog->globaloffsets.sb_showscores = PRVM_ED_FindGlobalOffset("sb_showscores");
1513 prog->globaloffsets.drawfont = PRVM_ED_FindGlobalOffset("drawfont");
1514 prog->globaloffsets.require_spawnfunc_prefix = PRVM_ED_FindGlobalOffset("require_spawnfunc_prefix");
1515 prog->globaloffsets.worldstatus = PRVM_ED_FindGlobalOffset("worldstatus");
1517 // menu qc only uses some functions, nothing else
1518 prog->funcoffsets.m_draw = PRVM_ED_FindFunctionOffset("m_draw");
1519 prog->funcoffsets.m_init = PRVM_ED_FindFunctionOffset("m_init");
1520 prog->funcoffsets.m_keydown = PRVM_ED_FindFunctionOffset("m_keydown");
1521 prog->funcoffsets.m_keyup = PRVM_ED_FindFunctionOffset("m_keyup");
1522 prog->funcoffsets.m_shutdown = PRVM_ED_FindFunctionOffset("m_shutdown");
1523 prog->funcoffsets.m_toggle = PRVM_ED_FindFunctionOffset("m_toggle");
1528 typedef struct dpfield_s
1535 #define DPFIELDS (sizeof(dpfields) / sizeof(dpfield_t))
1537 dpfield_t dpfields[] =
1548 void PRVM_LeakTest();
1549 void PRVM_ResetProg()
1552 PRVM_GCALL(reset_cmd)();
1553 Mem_FreePool(&prog->progs_mempool);
1554 memset(prog,0,sizeof(prvm_prog_t));
1555 prog->starttime = Sys_DoubleTime();
1563 void PRVM_LoadLNO( const char *progname ) {
1564 fs_offset_t filesize;
1566 unsigned int *header;
1569 FS_StripExtension( progname, filename, sizeof( filename ) );
1570 strlcat( filename, ".lno", sizeof( filename ) );
1572 lno = FS_LoadFile( filename, tempmempool, false, &filesize );
1578 <Spike> SafeWrite (h, &lnotype, sizeof(int));
1579 <Spike> SafeWrite (h, &version, sizeof(int));
1580 <Spike> SafeWrite (h, &numglobaldefs, sizeof(int));
1581 <Spike> SafeWrite (h, &numpr_globals, sizeof(int));
1582 <Spike> SafeWrite (h, &numfielddefs, sizeof(int));
1583 <Spike> SafeWrite (h, &numstatements, sizeof(int));
1584 <Spike> SafeWrite (h, statement_linenums, numstatements*sizeof(int));
1586 if( (unsigned) filesize < (6 + prog->progs->numstatements) * sizeof( int ) ) {
1591 header = (unsigned int *) lno;
1592 if( header[ 0 ] == *(unsigned int *) "LNOF" &&
1593 LittleLong( header[ 1 ] ) == 1 &&
1594 (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs->numglobaldefs &&
1595 (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs->numglobals &&
1596 (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs->numfielddefs &&
1597 (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs->numstatements )
1599 prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs->numstatements * sizeof( int ) );
1600 memcpy( prog->statement_linenums, (int *) lno + 6, prog->progs->numstatements * sizeof( int ) );
1610 void PRVM_LoadProgs (const char * filename, int numrequiredfunc, char **required_func, int numrequiredfields, prvm_required_field_t *required_field, int numrequiredglobals, char **required_global)
1614 ddef_t *infielddefs;
1615 dfunction_t *dfunctions;
1616 fs_offset_t filesize;
1618 if( prog->loaded ) {
1619 PRVM_ERROR ("PRVM_LoadProgs: there is already a %s program loaded!", PRVM_NAME );
1622 prog->progs = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
1623 if (prog->progs == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
1624 PRVM_ERROR ("PRVM_LoadProgs: couldn't load %s for %s", filename, PRVM_NAME);
1626 Con_DPrintf("%s programs occupy %iK.\n", PRVM_NAME, (int)(filesize/1024));
1628 prog->filecrc = CRC_Block((unsigned char *)prog->progs, filesize);
1630 // byte swap the header
1631 for (i = 0;i < (int) sizeof(*prog->progs) / 4;i++)
1632 ((int *)prog->progs)[i] = LittleLong ( ((int *)prog->progs)[i] );
1634 if (prog->progs->version != PROG_VERSION)
1635 PRVM_ERROR ("%s: %s has wrong version number (%i should be %i)", PRVM_NAME, filename, prog->progs->version, PROG_VERSION);
1636 if (prog->progs->crc != prog->headercrc && prog->progs->crc != prog->headercrc2)
1637 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);
1639 //prog->functions = (dfunction_t *)((unsigned char *)progs + progs->ofs_functions);
1640 dfunctions = (dfunction_t *)((unsigned char *)prog->progs + prog->progs->ofs_functions);
1642 if (prog->progs->ofs_strings + prog->progs->numstrings >= (int)filesize)
1643 PRVM_ERROR ("%s: %s strings go past end of file", PRVM_NAME, filename);
1644 prog->strings = (char *)prog->progs + prog->progs->ofs_strings;
1645 prog->stringssize = prog->progs->numstrings;
1647 prog->numknownstrings = 0;
1648 prog->maxknownstrings = 0;
1649 prog->knownstrings = NULL;
1650 prog->knownstrings_freeable = NULL;
1652 Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
1654 prog->globaldefs = (ddef_t *)((unsigned char *)prog->progs + prog->progs->ofs_globaldefs);
1656 // we need to expand the fielddefs list to include all the engine fields,
1657 // so allocate a new place for it
1658 infielddefs = (ddef_t *)((unsigned char *)prog->progs + prog->progs->ofs_fielddefs);
1660 prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs->numfielddefs + numrequiredfields) * sizeof(ddef_t));
1662 prog->statements = (dstatement_t *)((unsigned char *)prog->progs + prog->progs->ofs_statements);
1664 prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs->numstatements * sizeof(*prog->statement_profile));
1666 // moved edict_size calculation down below field adding code
1668 //pr_global_struct = (globalvars_t *)((unsigned char *)progs + progs->ofs_globals);
1669 prog->globals.generic = (float *)((unsigned char *)prog->progs + prog->progs->ofs_globals);
1671 // byte swap the lumps
1672 for (i=0 ; i<prog->progs->numstatements ; i++)
1674 prog->statements[i].op = LittleShort(prog->statements[i].op);
1675 prog->statements[i].a = LittleShort(prog->statements[i].a);
1676 prog->statements[i].b = LittleShort(prog->statements[i].b);
1677 prog->statements[i].c = LittleShort(prog->statements[i].c);
1680 prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs->numfunctions);
1681 for (i = 0;i < prog->progs->numfunctions;i++)
1683 prog->functions[i].first_statement = LittleLong (dfunctions[i].first_statement);
1684 prog->functions[i].parm_start = LittleLong (dfunctions[i].parm_start);
1685 prog->functions[i].s_name = LittleLong (dfunctions[i].s_name);
1686 prog->functions[i].s_file = LittleLong (dfunctions[i].s_file);
1687 prog->functions[i].numparms = LittleLong (dfunctions[i].numparms);
1688 prog->functions[i].locals = LittleLong (dfunctions[i].locals);
1689 memcpy(prog->functions[i].parm_size, dfunctions[i].parm_size, sizeof(dfunctions[i].parm_size));
1692 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
1694 prog->globaldefs[i].type = LittleShort (prog->globaldefs[i].type);
1695 prog->globaldefs[i].ofs = LittleShort (prog->globaldefs[i].ofs);
1696 prog->globaldefs[i].s_name = LittleLong (prog->globaldefs[i].s_name);
1699 // copy the progs fields to the new fields list
1700 for (i = 0;i < prog->progs->numfielddefs;i++)
1702 prog->fielddefs[i].type = LittleShort (infielddefs[i].type);
1703 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
1704 PRVM_ERROR ("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", PRVM_NAME);
1705 prog->fielddefs[i].ofs = LittleShort (infielddefs[i].ofs);
1706 prog->fielddefs[i].s_name = LittleLong (infielddefs[i].s_name);
1709 // append the required fields
1710 for (i = 0;i < (int) numrequiredfields;i++)
1712 prog->fielddefs[prog->progs->numfielddefs].type = required_field[i].type;
1713 prog->fielddefs[prog->progs->numfielddefs].ofs = prog->progs->entityfields;
1714 prog->fielddefs[prog->progs->numfielddefs].s_name = PRVM_SetEngineString(required_field[i].name);
1715 if (prog->fielddefs[prog->progs->numfielddefs].type == ev_vector)
1716 prog->progs->entityfields += 3;
1718 prog->progs->entityfields++;
1719 prog->progs->numfielddefs++;
1722 // check required functions
1723 for(i=0 ; i < numrequiredfunc ; i++)
1724 if(PRVM_ED_FindFunction(required_func[i]) == 0)
1725 PRVM_ERROR("%s: %s not found in %s",PRVM_NAME, required_func[i], filename);
1727 // check required globals
1728 for(i=0 ; i < numrequiredglobals ; i++)
1729 if(PRVM_ED_FindGlobal(required_global[i]) == 0)
1730 PRVM_ERROR("%s: %s not found in %s",PRVM_NAME, required_global[i], filename);
1732 for (i=0 ; i<prog->progs->numglobals ; i++)
1733 ((int *)prog->globals.generic)[i] = LittleLong (((int *)prog->globals.generic)[i]);
1735 // moved edict_size calculation down here, below field adding code
1736 // LordHavoc: this no longer includes the prvm_edict_t header
1737 prog->edict_size = prog->progs->entityfields * 4;
1738 prog->edictareasize = prog->edict_size * prog->limit_edicts;
1740 // LordHavoc: bounds check anything static
1741 for (i = 0,st = prog->statements;i < prog->progs->numstatements;i++,st++)
1747 if ((unsigned short) st->a >= prog->progs->numglobals || st->b + i < 0 || st->b + i >= prog->progs->numstatements)
1748 PRVM_ERROR("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", i, PRVM_NAME);
1751 if (st->a + i < 0 || st->a + i >= prog->progs->numstatements)
1752 PRVM_ERROR("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, PRVM_NAME);
1754 // global global global
1789 if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->b >= prog->progs->numglobals || (unsigned short) st->c >= prog->progs->numglobals)
1790 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
1792 // global none global
1798 if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->c >= prog->progs->numglobals)
1799 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
1815 if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->b >= prog->progs->numglobals)
1816 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
1830 if ((unsigned short) st->a >= prog->progs->numglobals)
1831 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
1834 Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", st->op, i, PRVM_NAME);
1839 PRVM_LoadLNO(filename);
1843 prog->loaded = TRUE;
1845 // set flags & ddef_ts in prog
1851 PRVM_GCALL(init_cmd)();
1858 void PRVM_Fields_f (void)
1860 int i, j, ednum, used, usedamount;
1862 char tempstring[MAX_INPUTLINE], tempstring2[260];
1872 Con_Print("no progs loaded\n");
1879 Con_Print("prvm_fields <program name>\n");
1884 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1887 counts = (int *)Mem_Alloc(tempmempool, prog->progs->numfielddefs * sizeof(int));
1888 for (ednum = 0;ednum < prog->max_edicts;ednum++)
1890 ed = PRVM_EDICT_NUM(ednum);
1891 if (ed->priv.required->free)
1893 for (i = 1;i < prog->progs->numfielddefs;i++)
1895 d = &prog->fielddefs[i];
1896 name = PRVM_GetString(d->s_name);
1897 if (name[strlen(name)-2] == '_')
1898 continue; // skip _x, _y, _z vars
1899 v = (int *)((char *)ed->fields.vp + d->ofs*4);
1900 // if the value is still all 0, skip the field
1901 for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
1914 for (i = 0;i < prog->progs->numfielddefs;i++)
1916 d = &prog->fielddefs[i];
1917 name = PRVM_GetString(d->s_name);
1918 if (name[strlen(name)-2] == '_')
1919 continue; // skip _x, _y, _z vars
1920 switch(d->type & ~DEF_SAVEGLOBAL)
1923 strlcat(tempstring, "string ", sizeof(tempstring));
1926 strlcat(tempstring, "entity ", sizeof(tempstring));
1929 strlcat(tempstring, "function ", sizeof(tempstring));
1932 strlcat(tempstring, "field ", sizeof(tempstring));
1935 strlcat(tempstring, "void ", sizeof(tempstring));
1938 strlcat(tempstring, "float ", sizeof(tempstring));
1941 strlcat(tempstring, "vector ", sizeof(tempstring));
1944 strlcat(tempstring, "pointer ", sizeof(tempstring));
1947 dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
1948 strlcat(tempstring, tempstring2, sizeof(tempstring));
1951 if (strlen(name) > sizeof(tempstring2)-4)
1953 memcpy (tempstring2, name, sizeof(tempstring2)-4);
1954 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
1955 tempstring2[sizeof(tempstring2)-1] = 0;
1958 strlcat(tempstring, name, sizeof(tempstring));
1959 for (j = (int)strlen(name);j < 25;j++)
1960 strlcat(tempstring, " ", sizeof(tempstring));
1961 dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
1962 strlcat(tempstring, tempstring2, sizeof(tempstring));
1963 strlcat(tempstring, "\n", sizeof(tempstring));
1964 if (strlen(tempstring) >= sizeof(tempstring)/2)
1966 Con_Print(tempstring);
1972 usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
1976 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);
1981 void PRVM_Globals_f (void)
1984 const char *wildcard;
1990 Con_Print("no progs loaded\n");
1993 if(Cmd_Argc () < 2 || Cmd_Argc() > 3)
1995 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
2000 if(!PRVM_SetProgFromString (Cmd_Argv (1)))
2003 if( Cmd_Argc() == 3)
2004 wildcard = Cmd_Argv(2);
2008 Con_Printf("%s :", PRVM_NAME);
2010 for (i = 0;i < prog->progs->numglobaldefs;i++)
2013 if( !matchpattern( PRVM_GetString(prog->globaldefs[i].s_name), wildcard, 1) )
2018 Con_Printf("%s\n", PRVM_GetString(prog->globaldefs[i].s_name));
2020 Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->progs->numglobals, numculled, prog->progs->numglobals * 4);
2030 void PRVM_Global_f(void)
2033 if( Cmd_Argc() != 3 ) {
2034 Con_Printf( "prvm_global <program name> <global name>\n" );
2039 if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
2042 global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
2044 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2046 Con_Printf( "%s: %s\n", Cmd_Argv(2), PRVM_ValueString( (etype_t)global->type, (prvm_eval_t *) &prog->globals.generic[ global->ofs ] ) );
2055 void PRVM_GlobalSet_f(void)
2058 if( Cmd_Argc() != 4 ) {
2059 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2064 if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
2067 global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
2069 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2071 PRVM_ED_ParseEpair( NULL, global, Cmd_Argv(3), true );
2080 void PRVM_Init (void)
2082 Cmd_AddCommand ("prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2083 Cmd_AddCommand ("prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2084 Cmd_AddCommand ("prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2085 Cmd_AddCommand ("prvm_profile", PRVM_Profile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu)");
2086 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)");
2087 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)");
2088 Cmd_AddCommand ("prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2089 Cmd_AddCommand ("prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2090 Cmd_AddCommand ("prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2091 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)");
2092 Cmd_AddCommand ("prvm_printfunction", PRVM_PrintFunction_f, "prints a disassembly (QuakeC instructions) of the specified function in the selected VM (server, client, menu)");
2093 Cmd_AddCommand ("cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2094 Cmd_AddCommand ("menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2095 Cmd_AddCommand ("sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2096 // LordHavoc: optional runtime bounds checking (speed drain, but worth it for security, on by default - breaks most QCCX features (used by CRMod and others))
2097 #ifdef PRVM_BOUNDSCHECK_CVAR
2098 Cvar_RegisterVariable (&prvm_boundscheck);
2100 Cvar_RegisterVariable (&prvm_traceqc);
2101 Cvar_RegisterVariable (&prvm_statementprofiling);
2102 Cvar_RegisterVariable (&prvm_backtraceforwarnings);
2103 Cvar_RegisterVariable (&prvm_leaktest);
2104 Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames);
2105 Cvar_RegisterVariable (&prvm_errordump);
2115 void PRVM_InitProg(int prognr)
2117 if(prognr < 0 || prognr >= PRVM_MAXPROGS)
2118 Sys_Error("PRVM_InitProg: Invalid program number %i",prognr);
2120 prog = &prog_list[prognr];
2125 memset(prog, 0, sizeof(prvm_prog_t));
2126 prog->starttime = Sys_DoubleTime();
2128 prog->error_cmd = Host_Error;
2129 prog->leaktest_active = prvm_leaktest.integer;
2132 int PRVM_GetProgNr()
2134 return prog - prog_list;
2137 void *_PRVM_Alloc(size_t buffersize, const char *filename, int fileline)
2139 return _Mem_Alloc(prog->progs_mempool, buffersize, filename, fileline);
2142 void _PRVM_Free(void *buffer, const char *filename, int fileline)
2144 _Mem_Free(buffer, filename, fileline);
2147 void _PRVM_FreeAll(const char *filename, int fileline)
2150 prog->fielddefs = NULL;
2151 prog->functions = NULL;
2152 _Mem_EmptyPool(prog->progs_mempool, filename, fileline);
2155 // LordHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
2156 unsigned int PRVM_EDICT_NUM_ERROR(unsigned int n, char *filename, int fileline)
2158 PRVM_ERROR ("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", PRVM_NAME, n, filename, fileline);
2163 int NUM_FOR_EDICT_ERROR(prvm_edict_t *e)
2165 PRVM_ERROR ("PRVM_NUM_FOR_EDICT: bad pointer %p (world is %p, entity number would be %i)", e, prog->edicts, e - prog->edicts);
2169 int PRVM_NUM_FOR_EDICT(prvm_edict_t *e)
2172 n = e - prog->edicts;
2173 if ((unsigned int)n >= prog->limit_edicts)
2174 Host_Error ("PRVM_NUM_FOR_EDICT: bad pointer");
2178 //int NoCrash_NUM_FOR_EDICT(prvm_edict_t *e)
2180 // return e - prog->edicts;
2183 //#define PRVM_EDICT_TO_PROG(e) ((unsigned char *)(((prvm_edict_t *)e)->v) - (unsigned char *)(prog->edictsfields))
2184 //#define PRVM_PROG_TO_EDICT(e) (prog->edicts + ((e) / (progs->entityfields * 4)))
2185 int PRVM_EDICT_TO_PROG(prvm_edict_t *e)
2188 n = e - prog->edicts;
2189 if ((unsigned int)n >= (unsigned int)prog->max_edicts)
2190 Host_Error("PRVM_EDICT_TO_PROG: invalid edict %8p (number %i compared to world at %8p)", e, n, prog->edicts);
2191 return n;// EXPERIMENTAL
2192 //return (unsigned char *)e->v - (unsigned char *)prog->edictsfields;
2194 prvm_edict_t *PRVM_PROG_TO_EDICT(int n)
2196 if ((unsigned int)n >= (unsigned int)prog->max_edicts)
2197 Host_Error("PRVM_PROG_TO_EDICT: invalid edict number %i", n);
2198 return prog->edicts + n; // EXPERIMENTAL
2199 //return prog->edicts + ((n) / (progs->entityfields * 4));
2204 sizebuf_t vm_tempstringsbuf;
2206 const char *PRVM_GetString(int num)
2210 if (num < prog->stringssize)
2211 return prog->strings + num;
2214 if (num <= prog->stringssize + vm_tempstringsbuf.maxsize)
2216 num -= prog->stringssize;
2217 if (num < vm_tempstringsbuf.cursize)
2218 return (char *)vm_tempstringsbuf.data + num;
2221 VM_Warning("PRVM_GetString: Invalid temp-string offset (%i >= %i vm_tempstringsbuf.cursize)\n", num, vm_tempstringsbuf.cursize);
2228 VM_Warning("PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
2238 // special range reserved for tempstrings
2240 if (num < vm_tempstringsbuf.cursize)
2241 return (char *)vm_tempstringsbuf.data + num;
2244 VM_Warning("PRVM_GetString: Invalid temp-string offset (%i >= %i vm_tempstringsbuf.cursize)\n", num, vm_tempstringsbuf.cursize);
2250 if (num < prog->numknownstrings)
2252 if (!prog->knownstrings[num])
2253 VM_Warning("PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
2254 return prog->knownstrings[num];
2258 VM_Warning("PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
2264 int PRVM_SetEngineString(const char *s)
2269 if (s >= prog->strings && s <= prog->strings + prog->stringssize)
2270 PRVM_ERROR("PRVM_SetEngineString: s in prog->strings area");
2271 // if it's in the tempstrings area, use a reserved range
2272 // (otherwise we'd get millions of useless string offsets cluttering the database)
2273 if (s >= (char *)vm_tempstringsbuf.data && s < (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.maxsize)
2275 return prog->stringssize + (s - (char *)vm_tempstringsbuf.data);
2277 return -1 - ((1<<30) + (s - (char *)vm_tempstringsbuf.data));
2279 // see if it's a known string address
2280 for (i = 0;i < prog->numknownstrings;i++)
2281 if (prog->knownstrings[i] == s)
2283 // new unknown engine string
2284 if (developer.integer >= 200)
2285 Con_Printf("new engine string %p = \"%s\"\n", s, s);
2286 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2287 if (!prog->knownstrings[i])
2289 if (i >= prog->numknownstrings)
2291 if (i >= prog->maxknownstrings)
2293 const char **oldstrings = prog->knownstrings;
2294 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2295 const char **oldstrings_origin = prog->knownstrings_origin;
2296 prog->maxknownstrings += 128;
2297 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2298 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2299 if(prog->leaktest_active)
2300 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2301 if (prog->numknownstrings)
2303 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2304 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2305 if(prog->leaktest_active)
2306 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
2309 prog->numknownstrings++;
2311 prog->firstfreeknownstring = i + 1;
2312 prog->knownstrings[i] = s;
2313 prog->knownstrings_freeable[i] = false;
2314 if(prog->leaktest_active)
2315 prog->knownstrings_origin[i] = NULL;
2319 // temp string handling
2321 // all tempstrings go into this buffer consecutively, and it is reset
2322 // whenever PRVM_ExecuteProgram returns to the engine
2323 // (technically each PRVM_ExecuteProgram call saves the cursize value and
2324 // restores it on return, so multiple recursive calls can share the same
2326 // the buffer size is automatically grown as needed
2328 int PRVM_SetTempString(const char *s)
2334 size = (int)strlen(s) + 1;
2335 if (developer.integer >= 300)
2336 Con_Printf("PRVM_SetTempString: cursize %i, size %i\n", vm_tempstringsbuf.cursize, size);
2337 if (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
2339 sizebuf_t old = vm_tempstringsbuf;
2340 if (vm_tempstringsbuf.cursize + size >= 1<<28)
2341 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);
2342 vm_tempstringsbuf.maxsize = max(vm_tempstringsbuf.maxsize, 65536);
2343 while (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
2344 vm_tempstringsbuf.maxsize *= 2;
2345 if (vm_tempstringsbuf.maxsize != old.maxsize || vm_tempstringsbuf.data == NULL)
2347 if (developer.integer >= 100)
2348 Con_Printf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, vm_tempstringsbuf.maxsize/1024);
2349 vm_tempstringsbuf.data = Mem_Alloc(sv_mempool, vm_tempstringsbuf.maxsize);
2351 memcpy(vm_tempstringsbuf.data, old.data, old.cursize);
2356 t = (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.cursize;
2358 vm_tempstringsbuf.cursize += size;
2359 return PRVM_SetEngineString(t);
2362 int PRVM_AllocString(size_t bufferlength, char **pointer)
2367 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2368 if (!prog->knownstrings[i])
2370 if (i >= prog->numknownstrings)
2372 if (i >= prog->maxknownstrings)
2374 const char **oldstrings = prog->knownstrings;
2375 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2376 const char **oldstrings_origin = prog->knownstrings_origin;
2377 prog->maxknownstrings += 128;
2378 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2379 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2380 if(prog->leaktest_active)
2381 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2382 if (prog->numknownstrings)
2384 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2385 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2386 if(prog->leaktest_active)
2387 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
2389 // TODO why not Mem_Free the old ones?
2391 prog->numknownstrings++;
2393 prog->firstfreeknownstring = i + 1;
2394 prog->knownstrings[i] = (char *)PRVM_Alloc(bufferlength);
2395 prog->knownstrings_freeable[i] = true;
2396 if(prog->leaktest_active)
2397 prog->knownstrings_origin[i] = PRVM_AllocationOrigin();
2399 *pointer = (char *)(prog->knownstrings[i]);
2403 void PRVM_FreeString(int num)
2406 PRVM_ERROR("PRVM_FreeString: attempt to free a NULL string");
2407 else if (num >= 0 && num < prog->stringssize)
2408 PRVM_ERROR("PRVM_FreeString: attempt to free a constant string");
2409 else if (num < 0 && num >= -prog->numknownstrings)
2412 if (!prog->knownstrings[num])
2413 PRVM_ERROR("PRVM_FreeString: attempt to free a non-existent or already freed string");
2414 if (!prog->knownstrings_freeable[num])
2415 PRVM_ERROR("PRVM_FreeString: attempt to free a string owned by the engine");
2416 PRVM_Free((char *)prog->knownstrings[num]);
2417 if(prog->leaktest_active)
2418 if(prog->knownstrings_origin[num])
2419 PRVM_Free((char *)prog->knownstrings_origin[num]);
2420 prog->knownstrings[num] = NULL;
2421 prog->knownstrings_freeable[num] = false;
2422 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
2425 PRVM_ERROR("PRVM_FreeString: invalid string offset %i", num);
2428 static qboolean PRVM_IsStringReferenced(string_t string)
2432 for (i = 0;i < prog->progs->numglobaldefs;i++)
2434 ddef_t *d = &prog->globaldefs[i];
2435 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
2437 if(string == ((prvm_eval_t *) &prog->globals.generic[d->ofs])->string)
2441 for(j = 0; j < prog->num_edicts; ++j)
2443 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2444 if (ed->priv.required->free)
2446 for (i=0; i<prog->progs->numfielddefs; ++i)
2448 ddef_t *d = &prog->fielddefs[i];
2449 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
2451 if(string == ((prvm_eval_t *) &((float*)ed->fields.vp)[d->ofs])->string)
2459 static qboolean PRVM_IsEdictRelevant(prvm_edict_t *edict)
2461 if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
2462 return true; // world or clients
2463 switch(prog - prog_list)
2465 case PRVM_SERVERPROG:
2467 entvars_t *ev = edict->fields.server;
2468 if(ev->solid) // can block other stuff, or is a trigger?
2470 if(ev->modelindex) // visible ent?
2472 if(ev->effects) // particle effect?
2474 if(ev->think) // has a think function?
2475 if(ev->nextthink > 0) // that actually will eventually run?
2479 if(*prvm_leaktest_ignore_classnames.string)
2481 if(strstr(va(" %s ", prvm_leaktest_ignore_classnames.string), va(" %s ", PRVM_GetString(ev->classname))))
2486 case PRVM_CLIENTPROG:
2488 // TODO someone add more stuff here
2489 cl_entvars_t *ev = edict->fields.client;
2490 if(ev->entnum) // csqc networked
2492 if(ev->modelindex) // visible ent?
2494 if(ev->effects) // particle effect?
2496 if(ev->think) // has a think function?
2497 if(ev->nextthink > 0) // that actually will eventually run?
2499 if(*prvm_leaktest_ignore_classnames.string)
2501 if(strstr(va(" %s ", prvm_leaktest_ignore_classnames.string), va(" %s ", PRVM_GetString(ev->classname))))
2507 // menu prog does not have classnames
2513 static qboolean PRVM_IsEdictReferenced(prvm_edict_t *edict, int mark)
2516 int edictnum = PRVM_NUM_FOR_EDICT(edict);
2517 const char *targetname = NULL;
2519 switch(prog - prog_list)
2521 case PRVM_SERVERPROG:
2522 targetname = PRVM_GetString(edict->fields.server->targetname);
2527 if(!*targetname) // ""
2530 for (i = 0;i < prog->progs->numglobaldefs;i++)
2532 ddef_t *d = &prog->globaldefs[i];
2533 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
2535 if(edictnum == ((prvm_eval_t *) &prog->globals.generic[d->ofs])->edict)
2539 for(j = 0; j < prog->num_edicts; ++j)
2541 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2542 if (ed->priv.required->mark < mark)
2548 const char *target = PRVM_GetString(ed->fields.server->target);
2550 if(!strcmp(target, targetname))
2553 for (i=0; i<prog->progs->numfielddefs; ++i)
2555 ddef_t *d = &prog->fielddefs[i];
2556 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
2558 if(edictnum == ((prvm_eval_t *) &((float*)ed->fields.vp)[d->ofs])->edict)
2566 static void PRVM_MarkReferencedEdicts()
2572 for(j = 0; j < prog->num_edicts; ++j)
2574 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2575 if(ed->priv.required->free)
2577 ed->priv.required->mark = PRVM_IsEdictRelevant(ed) ? 1 : 0;
2584 for(j = 0; j < prog->num_edicts; ++j)
2586 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2587 if(ed->priv.required->free)
2589 if(ed->priv.required->mark)
2591 if(PRVM_IsEdictReferenced(ed, stage))
2593 ed->priv.required->mark = stage + 1;
2600 Con_DPrintf("leak check used %d stages to find all references\n", stage);
2603 void PRVM_LeakTest()
2606 qboolean leaked = false;
2608 if(!prog->leaktest_active)
2612 for (i = 0; i < prog->numknownstrings; ++i)
2614 if(prog->knownstrings[i])
2615 if(prog->knownstrings_freeable[i])
2616 if(prog->knownstrings_origin[i])
2617 if(!PRVM_IsStringReferenced(-1 - i))
2619 Con_Printf("Unreferenced string found!\n Value: %s\n Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
2625 PRVM_MarkReferencedEdicts();
2626 for(j = 0; j < prog->num_edicts; ++j)
2628 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2629 if(ed->priv.required->free)
2631 if(!ed->priv.required->mark)
2632 if(ed->priv.required->allocation_origin)
2634 Con_Printf("Unreferenced edict found!\n Allocated at: %s\n", ed->priv.required->allocation_origin);
2635 PRVM_ED_Print(ed, NULL);
2641 for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
2643 prvm_stringbuffer_t *stringbuffer = Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
2645 if(stringbuffer->origin)
2647 Con_Printf("Open string buffer handle found!\n Allocated at: %s\n", stringbuffer->origin);
2652 for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
2654 if(prog->openfiles[i])
2655 if(prog->openfiles_origin[i])
2657 Con_Printf("Open file handle found!\n Allocated at: %s\n", prog->openfiles_origin[i]);
2662 for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
2664 if(prog->opensearches[i])
2665 if(prog->opensearches_origin[i])
2667 Con_Printf("Open search handle found!\n Allocated at: %s\n", prog->opensearches_origin[i]);
2673 Con_Printf("Congratulations. No leaks found.\n");