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 of entities to NOT leak check because they are found by find(world, classname, ...) but are actually spawned by QC code (NOT map entities)"};
46 extern sizebuf_t vm_tempstringsbuf;
48 //============================================================================
56 void PRVM_MEM_Alloc(void)
60 // reserve space for the null entity aka world
61 // check bound of max_edicts
62 prog->max_edicts = bound(1 + prog->reserved_edicts, prog->max_edicts, prog->limit_edicts);
63 prog->num_edicts = bound(1 + prog->reserved_edicts, prog->num_edicts, prog->max_edicts);
65 // edictprivate_size has to be min as big prvm_edict_private_t
66 prog->edictprivate_size = max(prog->edictprivate_size,(int)sizeof(prvm_edict_private_t));
69 prog->edicts = (prvm_edict_t *)Mem_Alloc(prog->progs_mempool,prog->limit_edicts * sizeof(prvm_edict_t));
71 // alloc edict private space
72 prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size);
75 prog->edictsfields = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edict_size);
78 for(i = 0; i < prog->max_edicts; i++)
80 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
81 prog->edicts[i].fields.vp = (void*)((unsigned char *)prog->edictsfields + i * prog->edict_size);
87 PRVM_MEM_IncreaseEdicts
90 void PRVM_MEM_IncreaseEdicts(void)
93 int oldmaxedicts = prog->max_edicts;
94 void *oldedictsfields = prog->edictsfields;
95 void *oldedictprivate = prog->edictprivate;
97 if(prog->max_edicts >= prog->limit_edicts)
100 PRVM_GCALL(begin_increase_edicts)();
103 prog->max_edicts = min(prog->max_edicts + 256, prog->limit_edicts);
105 prog->edictsfields = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edict_size);
106 prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size);
108 memcpy(prog->edictsfields, oldedictsfields, oldmaxedicts * prog->edict_size);
109 memcpy(prog->edictprivate, oldedictprivate, oldmaxedicts * prog->edictprivate_size);
111 //set e and v pointers
112 for(i = 0; i < prog->max_edicts; i++)
114 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
115 prog->edicts[i].fields.vp = (void*)((unsigned char *)prog->edictsfields + i * prog->edict_size);
118 PRVM_GCALL(end_increase_edicts)();
120 Mem_Free(oldedictsfields);
121 Mem_Free(oldedictprivate);
124 //============================================================================
127 int PRVM_ED_FindFieldOffset(const char *field)
130 d = PRVM_ED_FindField(field);
136 int PRVM_ED_FindGlobalOffset(const char *global)
139 d = PRVM_ED_FindGlobal(global);
145 func_t PRVM_ED_FindFunctionOffset(const char *function)
148 f = PRVM_ED_FindFunction(function);
151 return (func_t)(f - prog->functions);
154 qboolean PRVM_ProgLoaded(int prognr)
156 if(prognr < 0 || prognr >= PRVM_MAXPROGS)
159 return (prog_list[prognr].loaded ? TRUE : FALSE);
164 PRVM_SetProgFromString
167 // perhaps add a return value when the str doesnt exist
168 qboolean PRVM_SetProgFromString(const char *str)
171 for(; i < PRVM_MAXPROGS ; i++)
172 if(prog_list[i].name && !strcmp(prog_list[i].name,str))
174 if(prog_list[i].loaded)
176 prog = &prog_list[i];
181 Con_Printf("%s not loaded !\n",PRVM_NAME);
186 Con_Printf("Invalid program name %s !\n", str);
195 void PRVM_SetProg(int prognr)
197 if(0 <= prognr && prognr < PRVM_MAXPROGS)
199 if(prog_list[prognr].loaded)
200 prog = &prog_list[prognr];
202 PRVM_ERROR("%i not loaded !", prognr);
205 PRVM_ERROR("Invalid program number %i", prognr);
212 Sets everything to NULL
215 void PRVM_ED_ClearEdict (prvm_edict_t *e)
217 memset (e->fields.vp, 0, prog->progs->entityfields * 4);
218 e->priv.required->free = false;
220 // AK: Let the init_edict function determine if something needs to be initialized
221 PRVM_GCALL(init_edict)(e);
224 const char *PRVM_AllocationOrigin()
227 if(prog->leaktest_active)
228 if(prog->depth > 0) // actually in QC code and not just parsing the entities block of a map/savegame
230 buf = (char *)PRVM_Alloc(128);
231 PRVM_ShortStackTrace(buf, 128);
240 Either finds a free edict, or allocates a new one.
241 Try to avoid reusing an entity that was recently freed, because it
242 can cause the client to think the entity morphed into something else
243 instead of being removed and recreated, which can cause interpolated
244 angles and bad trails.
247 prvm_edict_t *PRVM_ED_Alloc (void)
252 // the client qc dont need maxclients
253 // thus it doesnt need to use svs.maxclients
254 // AK: changed i=svs.maxclients+1
255 // AK: changed so the edict 0 wont spawn -> used as reserved/world entity
256 // although the menu/client has no world
257 for (i = prog->reserved_edicts + 1;i < prog->num_edicts;i++)
259 e = PRVM_EDICT_NUM(i);
260 // the first couple seconds of server time can involve a lot of
261 // freeing and allocating, so relax the replacement policy
262 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 ) )
264 PRVM_ED_ClearEdict (e);
265 e->priv.required->allocation_origin = PRVM_AllocationOrigin();
270 if (i == prog->limit_edicts)
271 PRVM_ERROR ("%s: PRVM_ED_Alloc: no free edicts",PRVM_NAME);
274 if (prog->num_edicts >= prog->max_edicts)
275 PRVM_MEM_IncreaseEdicts();
277 e = PRVM_EDICT_NUM(i);
278 PRVM_ED_ClearEdict (e);
280 e->priv.required->allocation_origin = PRVM_AllocationOrigin();
289 Marks the edict as free
290 FIXME: walk all entities and NULL out references to this entity
293 void PRVM_ED_Free (prvm_edict_t *ed)
295 // dont delete the null entity (world) or reserved edicts
296 if(PRVM_NUM_FOR_EDICT(ed) <= prog->reserved_edicts )
299 PRVM_GCALL(free_edict)(ed);
301 ed->priv.required->free = true;
302 ed->priv.required->freetime = prog->globaloffsets.time >= 0 ? PRVM_GLOBALFIELDVALUE(prog->globaloffsets.time)->_float : 0;
303 if(ed->priv.required->allocation_origin)
305 PRVM_Free((char *)ed->priv.required->allocation_origin);
306 ed->priv.required->allocation_origin = NULL;
310 //===========================================================================
317 ddef_t *PRVM_ED_GlobalAtOfs (int ofs)
322 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
324 def = &prog->globaldefs[i];
336 ddef_t *PRVM_ED_FieldAtOfs (int ofs)
341 for (i=0 ; i<prog->progs->numfielddefs ; i++)
343 def = &prog->fielddefs[i];
355 ddef_t *PRVM_ED_FindField (const char *name)
360 for (i=0 ; i<prog->progs->numfielddefs ; i++)
362 def = &prog->fielddefs[i];
363 if (!strcmp(PRVM_GetString(def->s_name), name))
374 ddef_t *PRVM_ED_FindGlobal (const char *name)
379 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
381 def = &prog->globaldefs[i];
382 if (!strcmp(PRVM_GetString(def->s_name), name))
394 mfunction_t *PRVM_ED_FindFunction (const char *name)
399 for (i=0 ; i<prog->progs->numfunctions ; i++)
401 func = &prog->functions[i];
402 if (!strcmp(PRVM_GetString(func->s_name), name))
413 Returns a string describing *data in a type specific manner
416 char *PRVM_ValueString (etype_t type, prvm_eval_t *val)
418 static char line[MAX_INPUTLINE];
423 type = (etype_t)((int) type & ~DEF_SAVEGLOBAL);
428 strlcpy (line, PRVM_GetString (val->string), sizeof (line));
432 if (n < 0 || n >= prog->limit_edicts)
433 dpsnprintf (line, sizeof(line), "entity %i (invalid!)", n);
435 dpsnprintf (line, sizeof(line), "entity %i", n);
438 f = prog->functions + val->function;
439 dpsnprintf (line, sizeof(line), "%s()", PRVM_GetString(f->s_name));
442 def = PRVM_ED_FieldAtOfs ( val->_int );
443 dpsnprintf (line, sizeof(line), ".%s", PRVM_GetString(def->s_name));
446 dpsnprintf (line, sizeof(line), "void");
449 // LordHavoc: changed from %5.1f to %10.4f
450 dpsnprintf (line, sizeof(line), "%10.4f", val->_float);
453 // LordHavoc: changed from %5.1f to %10.4f
454 dpsnprintf (line, sizeof(line), "'%10.4f %10.4f %10.4f'", val->vector[0], val->vector[1], val->vector[2]);
457 dpsnprintf (line, sizeof(line), "pointer");
460 dpsnprintf (line, sizeof(line), "bad type %i", (int) type);
471 Returns a string describing *data in a type specific manner
472 Easier to parse than PR_ValueString
475 char *PRVM_UglyValueString (etype_t type, prvm_eval_t *val)
477 static char line[MAX_INPUTLINE];
483 type = (etype_t)((int)type & ~DEF_SAVEGLOBAL);
488 // Parse the string a bit to turn special characters
489 // (like newline, specifically) into escape codes,
490 // this fixes saving games from various mods
491 s = PRVM_GetString (val->string);
492 for (i = 0;i < (int)sizeof(line) - 2 && *s;)
516 dpsnprintf (line, sizeof (line), "%i", PRVM_NUM_FOR_EDICT(PRVM_PROG_TO_EDICT(val->edict)));
519 f = prog->functions + val->function;
520 strlcpy (line, PRVM_GetString (f->s_name), sizeof (line));
523 def = PRVM_ED_FieldAtOfs ( val->_int );
524 dpsnprintf (line, sizeof (line), ".%s", PRVM_GetString(def->s_name));
527 dpsnprintf (line, sizeof (line), "void");
530 dpsnprintf (line, sizeof (line), "%f", val->_float);
533 dpsnprintf (line, sizeof (line), "%f %f %f", val->vector[0], val->vector[1], val->vector[2]);
536 dpsnprintf (line, sizeof (line), "bad type %i", type);
547 Returns a string with a description and the contents of a global,
548 padded to 20 field width
551 char *PRVM_GlobalString (int ofs)
557 static char line[128];
559 val = (void *)&prog->globals.generic[ofs];
560 def = PRVM_ED_GlobalAtOfs(ofs);
562 dpsnprintf (line, sizeof(line), "GLOBAL%i", ofs);
565 s = PRVM_ValueString ((etype_t)def->type, (prvm_eval_t *)val);
566 dpsnprintf (line, sizeof(line), "%s (=%s)", PRVM_GetString(def->s_name), s);
570 //for ( ; i<20 ; i++)
571 // strcat (line," ");
577 char *PRVM_GlobalStringNoContents (int ofs)
581 static char line[128];
583 def = PRVM_ED_GlobalAtOfs(ofs);
585 dpsnprintf (line, sizeof(line), "GLOBAL%i", ofs);
587 dpsnprintf (line, sizeof(line), "%s", PRVM_GetString(def->s_name));
590 //for ( ; i<20 ; i++)
591 // strcat (line," ");
605 // LordHavoc: optimized this to print out much more quickly (tempstring)
606 // LordHavoc: changed to print out every 4096 characters (incase there are a lot of fields to print)
607 void PRVM_ED_Print(prvm_edict_t *ed, const char *wildcard_fieldname)
615 char tempstring[MAX_INPUTLINE], tempstring2[260]; // temporary string buffers
617 if (ed->priv.required->free)
619 Con_Printf("%s: FREE\n",PRVM_NAME);
624 dpsnprintf(tempstring, sizeof(tempstring), "\n%s EDICT %i:\n", PRVM_NAME, PRVM_NUM_FOR_EDICT(ed));
625 for (i=1 ; i<prog->progs->numfielddefs ; i++)
627 d = &prog->fielddefs[i];
628 name = PRVM_GetString(d->s_name);
629 if (name[strlen(name)-2] == '_')
630 continue; // skip _x, _y, _z vars
632 // Check Field Name Wildcard
633 if(wildcard_fieldname)
634 if( !matchpattern(name, wildcard_fieldname, 1) )
635 // Didn't match; skip
638 v = (int *)((char *)ed->fields.vp + d->ofs*4);
640 // if the value is still all 0, skip the field
641 type = d->type & ~DEF_SAVEGLOBAL;
643 for (j=0 ; j<prvm_type_size[type] ; j++)
646 if (j == prvm_type_size[type])
649 if (strlen(name) > sizeof(tempstring2)-4)
651 memcpy (tempstring2, name, sizeof(tempstring2)-4);
652 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
653 tempstring2[sizeof(tempstring2)-1] = 0;
656 strlcat(tempstring, name, sizeof(tempstring));
657 for (l = strlen(name);l < 14;l++)
658 strlcat(tempstring, " ", sizeof(tempstring));
659 strlcat(tempstring, " ", sizeof(tempstring));
661 name = PRVM_ValueString((etype_t)d->type, (prvm_eval_t *)v);
662 if (strlen(name) > sizeof(tempstring2)-4)
664 memcpy (tempstring2, name, sizeof(tempstring2)-4);
665 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
666 tempstring2[sizeof(tempstring2)-1] = 0;
669 strlcat(tempstring, name, sizeof(tempstring));
670 strlcat(tempstring, "\n", sizeof(tempstring));
671 if (strlen(tempstring) >= sizeof(tempstring)/2)
673 Con_Print(tempstring);
678 Con_Print(tempstring);
688 void PRVM_ED_Write (qfile_t *f, prvm_edict_t *ed)
698 if (ed->priv.required->free)
704 for (i=1 ; i<prog->progs->numfielddefs ; i++)
706 d = &prog->fielddefs[i];
707 name = PRVM_GetString(d->s_name);
708 if (name[strlen(name)-2] == '_')
709 continue; // skip _x, _y, _z vars
711 v = (int *)((char *)ed->fields.vp + d->ofs*4);
713 // if the value is still all 0, skip the field
714 type = d->type & ~DEF_SAVEGLOBAL;
715 for (j=0 ; j<prvm_type_size[type] ; j++)
718 if (j == prvm_type_size[type])
721 FS_Printf(f,"\"%s\" ",name);
722 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString((etype_t)d->type, (prvm_eval_t *)v));
728 void PRVM_ED_PrintNum (int ent, const char *wildcard_fieldname)
730 PRVM_ED_Print(PRVM_EDICT_NUM(ent), wildcard_fieldname);
735 PRVM_ED_PrintEdicts_f
737 For debugging, prints all the entities in the current server
740 void PRVM_ED_PrintEdicts_f (void)
743 const char *wildcard_fieldname;
745 if(Cmd_Argc() < 2 || Cmd_Argc() > 3)
747 Con_Print("prvm_edicts <program name> <optional field name wildcard>\n");
752 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
756 wildcard_fieldname = Cmd_Argv(2);
758 wildcard_fieldname = NULL;
760 Con_Printf("%s: %i entities\n", PRVM_NAME, prog->num_edicts);
761 for (i=0 ; i<prog->num_edicts ; i++)
762 PRVM_ED_PrintNum (i, wildcard_fieldname);
771 For debugging, prints a single edict
774 void PRVM_ED_PrintEdict_f (void)
777 const char *wildcard_fieldname;
779 if(Cmd_Argc() < 3 || Cmd_Argc() > 4)
781 Con_Print("prvm_edict <program name> <edict number> <optional field name wildcard>\n");
786 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
789 i = atoi (Cmd_Argv(2));
790 if (i >= prog->num_edicts)
792 Con_Print("Bad edict number\n");
797 // Optional Wildcard Provided
798 wildcard_fieldname = Cmd_Argv(3);
801 wildcard_fieldname = NULL;
802 PRVM_ED_PrintNum (i, wildcard_fieldname);
814 // 2 possibilities : 1. just displaying the active edict count
815 // 2. making a function pointer [x]
816 void PRVM_ED_Count_f (void)
824 Con_Print("prvm_count <program name>\n");
829 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
832 if(prog->count_edicts)
833 prog->count_edicts();
837 for (i=0 ; i<prog->num_edicts ; i++)
839 ent = PRVM_EDICT_NUM(i);
840 if (ent->priv.required->free)
845 Con_Printf("num_edicts:%3i\n", prog->num_edicts);
846 Con_Printf("active :%3i\n", active);
853 ==============================================================================
857 FIXME: need to tag constants, doesn't really work
858 ==============================================================================
866 void PRVM_ED_WriteGlobals (qfile_t *f)
874 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
876 def = &prog->globaldefs[i];
878 if ( !(def->type & DEF_SAVEGLOBAL) )
880 type &= ~DEF_SAVEGLOBAL;
882 if (type != ev_string && type != ev_float && type != ev_entity)
885 name = PRVM_GetString(def->s_name);
886 FS_Printf(f,"\"%s\" ", name);
887 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString((etype_t)type, (prvm_eval_t *)&prog->globals.generic[def->ofs]));
897 void PRVM_ED_ParseGlobals (const char *data)
899 char keyname[MAX_INPUTLINE];
905 if (!COM_ParseToken_Simple(&data, false, false))
906 PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace");
907 if (com_token[0] == '}')
910 strlcpy (keyname, com_token, sizeof(keyname));
913 if (!COM_ParseToken_Simple(&data, false, true))
914 PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace");
916 if (com_token[0] == '}')
917 PRVM_ERROR ("PRVM_ED_ParseGlobals: closing brace without data");
919 key = PRVM_ED_FindGlobal (keyname);
922 Con_DPrintf("'%s' is not a global on %s\n", keyname, PRVM_NAME);
926 if (!PRVM_ED_ParseEpair(NULL, key, com_token, true))
927 PRVM_ERROR ("PRVM_ED_ParseGlobals: parse error");
931 //============================================================================
938 Can parse either fields or globals
939 returns false if error
942 qboolean PRVM_ED_ParseEpair(prvm_edict_t *ent, ddef_t *key, const char *s, qboolean parsebackslash)
951 val = (prvm_eval_t *)((int *)ent->fields.vp + key->ofs);
953 val = (prvm_eval_t *)((int *)prog->globals.generic + key->ofs);
954 switch (key->type & ~DEF_SAVEGLOBAL)
957 l = (int)strlen(s) + 1;
958 val->string = PRVM_AllocString(l, &new_p);
959 for (i = 0;i < l;i++)
961 if (s[i] == '\\' && s[i+1] && parsebackslash)
966 else if (s[i] == 'r')
977 while (*s && *s <= ' ')
979 val->_float = atof(s);
983 for (i = 0;i < 3;i++)
985 while (*s && *s <= ' ')
989 val->vector[i] = atof(s);
998 while (*s && *s <= ' ')
1001 if (i >= prog->limit_edicts)
1002 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);
1003 while (i >= prog->max_edicts)
1004 PRVM_MEM_IncreaseEdicts();
1005 // if IncreaseEdicts was called the base pointer needs to be updated
1007 val = (prvm_eval_t *)((int *)ent->fields.vp + key->ofs);
1008 val->edict = PRVM_EDICT_TO_PROG(PRVM_EDICT_NUM((int)i));
1014 Con_DPrintf("PRVM_ED_ParseEpair: Bogus field name %s in %s\n", s, PRVM_NAME);
1017 def = PRVM_ED_FindField(s + 1);
1020 Con_DPrintf("PRVM_ED_ParseEpair: Can't find field %s in %s\n", s, PRVM_NAME);
1023 val->_int = def->ofs;
1027 func = PRVM_ED_FindFunction(s);
1030 Con_Printf("PRVM_ED_ParseEpair: Can't find function %s in %s\n", s, PRVM_NAME);
1033 val->function = func - prog->functions;
1037 Con_Printf("PRVM_ED_ParseEpair: Unknown key->type %i for key \"%s\" on %s\n", key->type, PRVM_GetString(key->s_name), PRVM_NAME);
1047 Console command to send a string to QC function GameCommand of the
1051 sv_cmd adminmsg 3 "do not teamkill"
1052 cl_cmd someclientcommand
1053 menu_cmd somemenucommand
1055 All progs can support this extension; sg calls it in server QC, cg in client
1059 void PRVM_GameCommand(const char *whichprogs, const char *whichcmd)
1063 Con_Printf("%s text...\n", whichcmd);
1068 if(!PRVM_SetProgFromString(whichprogs))
1069 // note: this is not PRVM_SetProg because that one aborts "hard" using PRVM_Error
1070 // also, it makes printing error messages easier!
1072 Con_Printf("%s program not loaded.\n", whichprogs);
1076 if(!prog->funcoffsets.GameCommand)
1078 Con_Printf("%s program do not support GameCommand!\n", whichprogs);
1082 int restorevm_tempstringsbuf_cursize;
1087 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
1088 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(s ? s : "");
1089 PRVM_ExecuteProgram (prog->funcoffsets.GameCommand, "QC function GameCommand is missing");
1090 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1095 void PRVM_GameCommand_Server_f(void)
1097 PRVM_GameCommand("server", "sv_cmd");
1099 void PRVM_GameCommand_Client_f(void)
1101 PRVM_GameCommand("client", "cl_cmd");
1103 void PRVM_GameCommand_Menu_f(void)
1105 PRVM_GameCommand("menu", "menu_cmd");
1112 Console command to set a field of a specified edict
1115 void PRVM_ED_EdictSet_f(void)
1122 Con_Print("prvm_edictset <program name> <edict number> <field> <value>\n");
1127 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1129 Con_Printf("Wrong program name %s !\n", Cmd_Argv(1));
1133 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1135 if((key = PRVM_ED_FindField(Cmd_Argv(3))) == 0)
1136 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1138 PRVM_ED_ParseEpair(ed, key, Cmd_Argv(4), true);
1144 ====================
1147 Parses an edict out of the given string, returning the new position
1148 ed should be a properly initialized empty edict.
1149 Used for initial level load and for savegames.
1150 ====================
1152 extern cvar_t developer_entityparsing;
1153 const char *PRVM_ED_ParseEdict (const char *data, prvm_edict_t *ent)
1163 // go through all the dictionary pairs
1167 if (!COM_ParseToken_Simple(&data, false, false))
1168 PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1169 if (developer_entityparsing.integer)
1170 Con_Printf("Key: \"%s\"", com_token);
1171 if (com_token[0] == '}')
1174 // anglehack is to allow QuakeEd to write single scalar angles
1175 // and allow them to be turned into vectors. (FIXME...)
1176 if (!strcmp(com_token, "angle"))
1178 strlcpy (com_token, "angles", sizeof(com_token));
1184 // FIXME: change light to _light to get rid of this hack
1185 if (!strcmp(com_token, "light"))
1186 strlcpy (com_token, "light_lev", sizeof(com_token)); // hack for single light def
1188 strlcpy (keyname, com_token, sizeof(keyname));
1190 // another hack to fix keynames with trailing spaces
1191 n = strlen(keyname);
1192 while (n && keyname[n-1] == ' ')
1199 if (!COM_ParseToken_Simple(&data, false, false))
1200 PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1201 if (developer_entityparsing.integer)
1202 Con_Printf(" \"%s\"\n", com_token);
1204 if (com_token[0] == '}')
1205 PRVM_ERROR ("PRVM_ED_ParseEdict: closing brace without data");
1209 // ignore attempts to set key "" (this problem occurs in nehahra neh1m8.bsp)
1213 // keynames with a leading underscore are used for utility comments,
1214 // and are immediately discarded by quake
1215 if (keyname[0] == '_')
1218 key = PRVM_ED_FindField (keyname);
1221 Con_DPrintf("%s: '%s' is not a field\n", PRVM_NAME, keyname);
1228 strlcpy (temp, com_token, sizeof(temp));
1229 dpsnprintf (com_token, sizeof(com_token), "0 %s 0", temp);
1232 if (!PRVM_ED_ParseEpair(ent, key, com_token, strcmp(keyname, "wad") != 0))
1233 PRVM_ERROR ("PRVM_ED_ParseEdict: parse error");
1237 ent->priv.required->free = true;
1245 PRVM_ED_LoadFromFile
1247 The entities are directly placed in the array, rather than allocated with
1248 PRVM_ED_Alloc, because otherwise an error loading the map would have entity
1249 number references out of order.
1251 Creates a server's entity / program execution context by
1252 parsing textual entity definitions out of an ent file.
1254 Used for both fresh maps and savegame loads. A fresh map would also need
1255 to call PRVM_ED_CallSpawnFunctions () to let the objects initialize themselves.
1258 void PRVM_ED_LoadFromFile (const char *data)
1261 int parsed, inhibited, spawned, died;
1262 const char *funcname;
1274 // parse the opening brace
1275 if (!COM_ParseToken_Simple(&data, false, false))
1277 if (com_token[0] != '{')
1278 PRVM_ERROR ("PRVM_ED_LoadFromFile: %s: found %s when expecting {", PRVM_NAME, com_token);
1280 // CHANGED: this is not conform to PR_LoadFromFile
1281 if(prog->loadintoworld)
1283 prog->loadintoworld = false;
1284 ent = PRVM_EDICT_NUM(0);
1287 ent = PRVM_ED_Alloc();
1290 if (ent != prog->edicts) // hack
1291 memset (ent->fields.vp, 0, prog->progs->entityfields * 4);
1293 data = PRVM_ED_ParseEdict (data, ent);
1296 // remove the entity ?
1297 if(prog->load_edict && !prog->load_edict(ent))
1305 // immediately call spawn function, but only if there is a self global and a classname
1307 if(prog->globaloffsets.self >= 0 && prog->fieldoffsets.classname >= 0)
1309 string_t handle = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.classname)->string;
1312 Con_Print("No classname for:\n");
1313 PRVM_ED_Print(ent, NULL);
1318 // look for the spawn function
1319 funcname = PRVM_GetString(handle);
1320 func = PRVM_ED_FindFunction (va("spawnfunc_%s", funcname));
1322 if(prog->globaloffsets.require_spawnfunc_prefix < 0)
1323 func = PRVM_ED_FindFunction (funcname);
1327 // check for OnEntityNoSpawnFunction
1328 if (prog->funcoffsets.SV_OnEntityNoSpawnFunction)
1331 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1332 PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityNoSpawnFunction, "QC function SV_OnEntityNoSpawnFunction is missing");
1336 if (developer.integer) // don't confuse non-developers with errors
1338 Con_Print("No spawn function for:\n");
1339 PRVM_ED_Print(ent, NULL);
1348 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1349 PRVM_ExecuteProgram (func - prog->functions, "");
1354 if (ent->priv.required->free)
1358 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);
1361 void PRVM_FindOffsets(void)
1363 // field and global searches use -1 for NULL
1364 memset(&prog->fieldoffsets, -1, sizeof(prog->fieldoffsets));
1365 memset(&prog->globaloffsets, -1, sizeof(prog->globaloffsets));
1366 // functions use 0 for NULL
1367 memset(&prog->funcoffsets, 0, sizeof(prog->funcoffsets));
1369 // server and client qc use a lot of similar fields, so this is combined
1370 prog->fieldoffsets.SendEntity = PRVM_ED_FindFieldOffset("SendEntity");
1371 prog->fieldoffsets.Version = PRVM_ED_FindFieldOffset("Version");
1372 prog->fieldoffsets.alpha = PRVM_ED_FindFieldOffset("alpha");
1373 prog->fieldoffsets.ammo_cells1 = PRVM_ED_FindFieldOffset("ammo_cells1");
1374 prog->fieldoffsets.ammo_lava_nails = PRVM_ED_FindFieldOffset("ammo_lava_nails");
1375 prog->fieldoffsets.ammo_multi_rockets = PRVM_ED_FindFieldOffset("ammo_multi_rockets");
1376 prog->fieldoffsets.ammo_nails1 = PRVM_ED_FindFieldOffset("ammo_nails1");
1377 prog->fieldoffsets.ammo_plasma = PRVM_ED_FindFieldOffset("ammo_plasma");
1378 prog->fieldoffsets.ammo_rockets1 = PRVM_ED_FindFieldOffset("ammo_rockets1");
1379 prog->fieldoffsets.ammo_shells1 = PRVM_ED_FindFieldOffset("ammo_shells1");
1380 prog->fieldoffsets.angles = PRVM_ED_FindFieldOffset("angles");
1381 prog->fieldoffsets.button3 = PRVM_ED_FindFieldOffset("button3");
1382 prog->fieldoffsets.button4 = PRVM_ED_FindFieldOffset("button4");
1383 prog->fieldoffsets.button5 = PRVM_ED_FindFieldOffset("button5");
1384 prog->fieldoffsets.button6 = PRVM_ED_FindFieldOffset("button6");
1385 prog->fieldoffsets.button7 = PRVM_ED_FindFieldOffset("button7");
1386 prog->fieldoffsets.button8 = PRVM_ED_FindFieldOffset("button8");
1387 prog->fieldoffsets.button9 = PRVM_ED_FindFieldOffset("button9");
1388 prog->fieldoffsets.button10 = PRVM_ED_FindFieldOffset("button10");
1389 prog->fieldoffsets.button11 = PRVM_ED_FindFieldOffset("button11");
1390 prog->fieldoffsets.button12 = PRVM_ED_FindFieldOffset("button12");
1391 prog->fieldoffsets.button13 = PRVM_ED_FindFieldOffset("button13");
1392 prog->fieldoffsets.button14 = PRVM_ED_FindFieldOffset("button14");
1393 prog->fieldoffsets.button15 = PRVM_ED_FindFieldOffset("button15");
1394 prog->fieldoffsets.button16 = PRVM_ED_FindFieldOffset("button16");
1395 prog->fieldoffsets.buttonchat = PRVM_ED_FindFieldOffset("buttonchat");
1396 prog->fieldoffsets.buttonuse = PRVM_ED_FindFieldOffset("buttonuse");
1397 prog->fieldoffsets.chain = PRVM_ED_FindFieldOffset("chain");
1398 prog->fieldoffsets.classname = PRVM_ED_FindFieldOffset("classname");
1399 prog->fieldoffsets.clientcolors = PRVM_ED_FindFieldOffset("clientcolors");
1400 prog->fieldoffsets.color = PRVM_ED_FindFieldOffset("color");
1401 prog->fieldoffsets.colormod = PRVM_ED_FindFieldOffset("colormod");
1402 prog->fieldoffsets.contentstransition = PRVM_ED_FindFieldOffset("contentstransition");
1403 prog->fieldoffsets.cursor_active = PRVM_ED_FindFieldOffset("cursor_active");
1404 prog->fieldoffsets.cursor_screen = PRVM_ED_FindFieldOffset("cursor_screen");
1405 prog->fieldoffsets.cursor_trace_endpos = PRVM_ED_FindFieldOffset("cursor_trace_endpos");
1406 prog->fieldoffsets.cursor_trace_ent = PRVM_ED_FindFieldOffset("cursor_trace_ent");
1407 prog->fieldoffsets.cursor_trace_start = PRVM_ED_FindFieldOffset("cursor_trace_start");
1408 prog->fieldoffsets.customizeentityforclient = PRVM_ED_FindFieldOffset("customizeentityforclient");
1409 prog->fieldoffsets.dimension_hit = PRVM_ED_FindFieldOffset("dimension_hit");
1410 prog->fieldoffsets.dimension_solid = PRVM_ED_FindFieldOffset("dimension_solid");
1411 prog->fieldoffsets.disableclientprediction = PRVM_ED_FindFieldOffset("disableclientprediction");
1412 prog->fieldoffsets.dphitcontentsmask = PRVM_ED_FindFieldOffset("dphitcontentsmask");
1413 prog->fieldoffsets.drawonlytoclient = PRVM_ED_FindFieldOffset("drawonlytoclient");
1414 prog->fieldoffsets.exteriormodeltoclient = PRVM_ED_FindFieldOffset("exteriormodeltoclient");
1415 prog->fieldoffsets.fatness = PRVM_ED_FindFieldOffset("fatness");
1416 prog->fieldoffsets.forceshader = PRVM_ED_FindFieldOffset("forceshader");
1417 prog->fieldoffsets.frame = PRVM_ED_FindFieldOffset("frame");
1418 prog->fieldoffsets.frame1time = PRVM_ED_FindFieldOffset("frame1time");
1419 prog->fieldoffsets.frame2 = PRVM_ED_FindFieldOffset("frame2");
1420 prog->fieldoffsets.frame2time = PRVM_ED_FindFieldOffset("frame2time");
1421 prog->fieldoffsets.fullbright = PRVM_ED_FindFieldOffset("fullbright");
1422 prog->fieldoffsets.glow_color = PRVM_ED_FindFieldOffset("glow_color");
1423 prog->fieldoffsets.glow_size = PRVM_ED_FindFieldOffset("glow_size");
1424 prog->fieldoffsets.glow_trail = PRVM_ED_FindFieldOffset("glow_trail");
1425 prog->fieldoffsets.gravity = PRVM_ED_FindFieldOffset("gravity");
1426 prog->fieldoffsets.groundentity = PRVM_ED_FindFieldOffset("groundentity");
1427 prog->fieldoffsets.hull = PRVM_ED_FindFieldOffset("hull");
1428 prog->fieldoffsets.ideal_yaw = PRVM_ED_FindFieldOffset("ideal_yaw");
1429 prog->fieldoffsets.idealpitch = PRVM_ED_FindFieldOffset("idealpitch");
1430 prog->fieldoffsets.items2 = PRVM_ED_FindFieldOffset("items2");
1431 prog->fieldoffsets.lerpfrac = PRVM_ED_FindFieldOffset("lerpfrac");
1432 prog->fieldoffsets.light_lev = PRVM_ED_FindFieldOffset("light_lev");
1433 prog->fieldoffsets.message = PRVM_ED_FindFieldOffset("message");
1434 prog->fieldoffsets.modelflags = PRVM_ED_FindFieldOffset("modelflags");
1435 prog->fieldoffsets.movement = PRVM_ED_FindFieldOffset("movement");
1436 prog->fieldoffsets.movetypesteplandevent = PRVM_ED_FindFieldOffset("movetypesteplandevent");
1437 prog->fieldoffsets.netaddress = PRVM_ED_FindFieldOffset("netaddress");
1438 prog->fieldoffsets.nextthink = PRVM_ED_FindFieldOffset("nextthink");
1439 prog->fieldoffsets.nodrawtoclient = PRVM_ED_FindFieldOffset("nodrawtoclient");
1440 prog->fieldoffsets.pflags = PRVM_ED_FindFieldOffset("pflags");
1441 prog->fieldoffsets.ping = PRVM_ED_FindFieldOffset("ping");
1442 prog->fieldoffsets.pitch_speed = PRVM_ED_FindFieldOffset("pitch_speed");
1443 prog->fieldoffsets.playermodel = PRVM_ED_FindFieldOffset("playermodel");
1444 prog->fieldoffsets.playerskin = PRVM_ED_FindFieldOffset("playerskin");
1445 prog->fieldoffsets.pmodel = PRVM_ED_FindFieldOffset("pmodel");
1446 prog->fieldoffsets.punchvector = PRVM_ED_FindFieldOffset("punchvector");
1447 prog->fieldoffsets.renderamt = PRVM_ED_FindFieldOffset("renderamt"); // HalfLife support
1448 prog->fieldoffsets.renderflags = PRVM_ED_FindFieldOffset("renderflags");
1449 prog->fieldoffsets.rendermode = PRVM_ED_FindFieldOffset("rendermode"); // HalfLife support
1450 prog->fieldoffsets.scale = PRVM_ED_FindFieldOffset("scale");
1451 prog->fieldoffsets.style = PRVM_ED_FindFieldOffset("style");
1452 prog->fieldoffsets.tag_entity = PRVM_ED_FindFieldOffset("tag_entity");
1453 prog->fieldoffsets.tag_index = PRVM_ED_FindFieldOffset("tag_index");
1454 prog->fieldoffsets.think = PRVM_ED_FindFieldOffset("think");
1455 prog->fieldoffsets.viewmodelforclient = PRVM_ED_FindFieldOffset("viewmodelforclient");
1456 prog->fieldoffsets.viewzoom = PRVM_ED_FindFieldOffset("viewzoom");
1457 prog->fieldoffsets.yaw_speed = PRVM_ED_FindFieldOffset("yaw_speed");
1458 prog->fieldoffsets.clientcamera = PRVM_ED_FindFieldOffset("clientcamera");
1459 prog->funcoffsets.CSQC_ConsoleCommand = PRVM_ED_FindFunctionOffset("CSQC_ConsoleCommand");
1460 prog->funcoffsets.CSQC_Ent_Remove = PRVM_ED_FindFunctionOffset("CSQC_Ent_Remove");
1461 prog->funcoffsets.CSQC_Ent_Update = PRVM_ED_FindFunctionOffset("CSQC_Ent_Update");
1462 prog->funcoffsets.CSQC_Ent_Spawn = PRVM_ED_FindFunctionOffset("CSQC_Ent_Spawn");
1463 prog->funcoffsets.CSQC_Event = PRVM_ED_FindFunctionOffset("CSQC_Event");
1464 prog->funcoffsets.CSQC_Event_Sound = PRVM_ED_FindFunctionOffset("CSQC_Event_Sound");
1465 prog->funcoffsets.CSQC_Init = PRVM_ED_FindFunctionOffset("CSQC_Init");
1466 prog->funcoffsets.CSQC_InputEvent = PRVM_ED_FindFunctionOffset("CSQC_InputEvent");
1467 prog->funcoffsets.CSQC_Parse_CenterPrint = PRVM_ED_FindFunctionOffset("CSQC_Parse_CenterPrint");
1468 prog->funcoffsets.CSQC_Parse_Print = PRVM_ED_FindFunctionOffset("CSQC_Parse_Print");
1469 prog->funcoffsets.CSQC_Parse_StuffCmd = PRVM_ED_FindFunctionOffset("CSQC_Parse_StuffCmd");
1470 prog->funcoffsets.CSQC_Parse_TempEntity = PRVM_ED_FindFunctionOffset("CSQC_Parse_TempEntity");
1471 prog->funcoffsets.CSQC_Shutdown = PRVM_ED_FindFunctionOffset("CSQC_Shutdown");
1472 prog->funcoffsets.CSQC_UpdateView = PRVM_ED_FindFunctionOffset("CSQC_UpdateView");
1473 prog->funcoffsets.Gecko_Query = PRVM_ED_FindFunctionOffset("Gecko_Query");
1474 prog->funcoffsets.EndFrame = PRVM_ED_FindFunctionOffset("EndFrame");
1475 prog->funcoffsets.RestoreGame = PRVM_ED_FindFunctionOffset("RestoreGame");
1476 prog->funcoffsets.SV_ChangeTeam = PRVM_ED_FindFunctionOffset("SV_ChangeTeam");
1477 prog->funcoffsets.SV_ParseClientCommand = PRVM_ED_FindFunctionOffset("SV_ParseClientCommand");
1478 prog->funcoffsets.SV_PlayerPhysics = PRVM_ED_FindFunctionOffset("SV_PlayerPhysics");
1479 prog->funcoffsets.SV_OnEntityNoSpawnFunction = PRVM_ED_FindFunctionOffset("SV_OnEntityNoSpawnFunction");
1480 prog->funcoffsets.GameCommand = PRVM_ED_FindFunctionOffset("GameCommand");
1481 prog->funcoffsets.SV_Shutdown = PRVM_ED_FindFunctionOffset("SV_Shutdown");
1482 prog->globaloffsets.SV_InitCmd = PRVM_ED_FindGlobalOffset("SV_InitCmd");
1483 prog->globaloffsets.self = PRVM_ED_FindGlobalOffset("self");
1484 prog->globaloffsets.time = PRVM_ED_FindGlobalOffset("time");
1485 prog->globaloffsets.v_forward = PRVM_ED_FindGlobalOffset("v_forward");
1486 prog->globaloffsets.v_right = PRVM_ED_FindGlobalOffset("v_right");
1487 prog->globaloffsets.v_up = PRVM_ED_FindGlobalOffset("v_up");
1488 prog->globaloffsets.view_angles = PRVM_ED_FindGlobalOffset("view_angles");
1489 prog->globaloffsets.trace_allsolid = PRVM_ED_FindGlobalOffset("trace_allsolid");
1490 prog->globaloffsets.trace_startsolid = PRVM_ED_FindGlobalOffset("trace_startsolid");
1491 prog->globaloffsets.trace_fraction = PRVM_ED_FindGlobalOffset("trace_fraction");
1492 prog->globaloffsets.trace_inwater = PRVM_ED_FindGlobalOffset("trace_inwater");
1493 prog->globaloffsets.trace_inopen = PRVM_ED_FindGlobalOffset("trace_inopen");
1494 prog->globaloffsets.trace_endpos = PRVM_ED_FindGlobalOffset("trace_endpos");
1495 prog->globaloffsets.trace_plane_normal = PRVM_ED_FindGlobalOffset("trace_plane_normal");
1496 prog->globaloffsets.trace_plane_dist = PRVM_ED_FindGlobalOffset("trace_plane_dist");
1497 prog->globaloffsets.trace_ent = PRVM_ED_FindGlobalOffset("trace_ent");
1498 prog->globaloffsets.trace_dphitcontents = PRVM_ED_FindGlobalOffset("trace_dphitcontents");
1499 prog->globaloffsets.trace_dphitq3surfaceflags = PRVM_ED_FindGlobalOffset("trace_dphitq3surfaceflags");
1500 prog->globaloffsets.trace_dphittexturename = PRVM_ED_FindGlobalOffset("trace_dphittexturename");
1501 prog->globaloffsets.trace_dpstartcontents = PRVM_ED_FindGlobalOffset("trace_dpstartcontents");
1502 prog->globaloffsets.intermission = PRVM_ED_FindGlobalOffset("intermission");
1503 prog->globaloffsets.coop = PRVM_ED_FindGlobalOffset("coop");
1504 prog->globaloffsets.deathmatch = PRVM_ED_FindGlobalOffset("deathmatch");
1505 prog->globaloffsets.dmg_take = PRVM_ED_FindGlobalOffset("dmg_take");
1506 prog->globaloffsets.dmg_save = PRVM_ED_FindGlobalOffset("dmg_save");
1507 prog->globaloffsets.dmg_origin = PRVM_ED_FindGlobalOffset("dmg_origin");
1508 prog->globaloffsets.sb_showscores = PRVM_ED_FindGlobalOffset("sb_showscores");
1509 prog->globaloffsets.drawfont = PRVM_ED_FindGlobalOffset("drawfont");
1510 prog->globaloffsets.require_spawnfunc_prefix = PRVM_ED_FindGlobalOffset("require_spawnfunc_prefix");
1512 // menu qc only uses some functions, nothing else
1513 prog->funcoffsets.m_draw = PRVM_ED_FindFunctionOffset("m_draw");
1514 prog->funcoffsets.m_init = PRVM_ED_FindFunctionOffset("m_init");
1515 prog->funcoffsets.m_keydown = PRVM_ED_FindFunctionOffset("m_keydown");
1516 prog->funcoffsets.m_keyup = PRVM_ED_FindFunctionOffset("m_keyup");
1517 prog->funcoffsets.m_shutdown = PRVM_ED_FindFunctionOffset("m_shutdown");
1518 prog->funcoffsets.m_toggle = PRVM_ED_FindFunctionOffset("m_toggle");
1523 typedef struct dpfield_s
1530 #define DPFIELDS (sizeof(dpfields) / sizeof(dpfield_t))
1532 dpfield_t dpfields[] =
1543 void PRVM_LeakTest();
1544 void PRVM_ResetProg()
1547 PRVM_GCALL(reset_cmd)();
1548 Mem_FreePool(&prog->progs_mempool);
1549 memset(prog,0,sizeof(prvm_prog_t));
1550 prog->starttime = Sys_DoubleTime();
1558 void PRVM_LoadLNO( const char *progname ) {
1559 fs_offset_t filesize;
1561 unsigned int *header;
1564 FS_StripExtension( progname, filename, sizeof( filename ) );
1565 strlcat( filename, ".lno", sizeof( filename ) );
1567 lno = FS_LoadFile( filename, tempmempool, false, &filesize );
1573 <Spike> SafeWrite (h, &lnotype, sizeof(int));
1574 <Spike> SafeWrite (h, &version, sizeof(int));
1575 <Spike> SafeWrite (h, &numglobaldefs, sizeof(int));
1576 <Spike> SafeWrite (h, &numpr_globals, sizeof(int));
1577 <Spike> SafeWrite (h, &numfielddefs, sizeof(int));
1578 <Spike> SafeWrite (h, &numstatements, sizeof(int));
1579 <Spike> SafeWrite (h, statement_linenums, numstatements*sizeof(int));
1581 if( (unsigned) filesize < (6 + prog->progs->numstatements) * sizeof( int ) ) {
1586 header = (unsigned int *) lno;
1587 if( header[ 0 ] == *(unsigned int *) "LNOF" &&
1588 LittleLong( header[ 1 ] ) == 1 &&
1589 (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs->numglobaldefs &&
1590 (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs->numglobals &&
1591 (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs->numfielddefs &&
1592 (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs->numstatements )
1594 prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs->numstatements * sizeof( int ) );
1595 memcpy( prog->statement_linenums, (int *) lno + 6, prog->progs->numstatements * sizeof( int ) );
1605 void PRVM_LoadProgs (const char * filename, int numrequiredfunc, char **required_func, int numrequiredfields, prvm_required_field_t *required_field, int numrequiredglobals, char **required_global)
1609 ddef_t *infielddefs;
1610 dfunction_t *dfunctions;
1611 fs_offset_t filesize;
1613 if( prog->loaded ) {
1614 PRVM_ERROR ("PRVM_LoadProgs: there is already a %s program loaded!", PRVM_NAME );
1617 prog->progs = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
1618 if (prog->progs == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
1619 PRVM_ERROR ("PRVM_LoadProgs: couldn't load %s for %s", filename, PRVM_NAME);
1621 Con_DPrintf("%s programs occupy %iK.\n", PRVM_NAME, (int)(filesize/1024));
1623 prog->filecrc = CRC_Block((unsigned char *)prog->progs, filesize);
1625 // byte swap the header
1626 for (i = 0;i < (int) sizeof(*prog->progs) / 4;i++)
1627 ((int *)prog->progs)[i] = LittleLong ( ((int *)prog->progs)[i] );
1629 if (prog->progs->version != PROG_VERSION)
1630 PRVM_ERROR ("%s: %s has wrong version number (%i should be %i)", PRVM_NAME, filename, prog->progs->version, PROG_VERSION);
1631 if (prog->progs->crc != prog->headercrc && prog->progs->crc != prog->headercrc2)
1632 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);
1634 //prog->functions = (dfunction_t *)((unsigned char *)progs + progs->ofs_functions);
1635 dfunctions = (dfunction_t *)((unsigned char *)prog->progs + prog->progs->ofs_functions);
1637 if (prog->progs->ofs_strings + prog->progs->numstrings >= (int)filesize)
1638 PRVM_ERROR ("%s: %s strings go past end of file", PRVM_NAME, filename);
1639 prog->strings = (char *)prog->progs + prog->progs->ofs_strings;
1640 prog->stringssize = prog->progs->numstrings;
1642 prog->numknownstrings = 0;
1643 prog->maxknownstrings = 0;
1644 prog->knownstrings = NULL;
1645 prog->knownstrings_freeable = NULL;
1647 Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
1649 prog->globaldefs = (ddef_t *)((unsigned char *)prog->progs + prog->progs->ofs_globaldefs);
1651 // we need to expand the fielddefs list to include all the engine fields,
1652 // so allocate a new place for it
1653 infielddefs = (ddef_t *)((unsigned char *)prog->progs + prog->progs->ofs_fielddefs);
1655 prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs->numfielddefs + numrequiredfields) * sizeof(ddef_t));
1657 prog->statements = (dstatement_t *)((unsigned char *)prog->progs + prog->progs->ofs_statements);
1659 prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs->numstatements * sizeof(*prog->statement_profile));
1661 // moved edict_size calculation down below field adding code
1663 //pr_global_struct = (globalvars_t *)((unsigned char *)progs + progs->ofs_globals);
1664 prog->globals.generic = (float *)((unsigned char *)prog->progs + prog->progs->ofs_globals);
1666 // byte swap the lumps
1667 for (i=0 ; i<prog->progs->numstatements ; i++)
1669 prog->statements[i].op = LittleShort(prog->statements[i].op);
1670 prog->statements[i].a = LittleShort(prog->statements[i].a);
1671 prog->statements[i].b = LittleShort(prog->statements[i].b);
1672 prog->statements[i].c = LittleShort(prog->statements[i].c);
1675 prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs->numfunctions);
1676 for (i = 0;i < prog->progs->numfunctions;i++)
1678 prog->functions[i].first_statement = LittleLong (dfunctions[i].first_statement);
1679 prog->functions[i].parm_start = LittleLong (dfunctions[i].parm_start);
1680 prog->functions[i].s_name = LittleLong (dfunctions[i].s_name);
1681 prog->functions[i].s_file = LittleLong (dfunctions[i].s_file);
1682 prog->functions[i].numparms = LittleLong (dfunctions[i].numparms);
1683 prog->functions[i].locals = LittleLong (dfunctions[i].locals);
1684 memcpy(prog->functions[i].parm_size, dfunctions[i].parm_size, sizeof(dfunctions[i].parm_size));
1687 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
1689 prog->globaldefs[i].type = LittleShort (prog->globaldefs[i].type);
1690 prog->globaldefs[i].ofs = LittleShort (prog->globaldefs[i].ofs);
1691 prog->globaldefs[i].s_name = LittleLong (prog->globaldefs[i].s_name);
1694 // copy the progs fields to the new fields list
1695 for (i = 0;i < prog->progs->numfielddefs;i++)
1697 prog->fielddefs[i].type = LittleShort (infielddefs[i].type);
1698 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
1699 PRVM_ERROR ("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", PRVM_NAME);
1700 prog->fielddefs[i].ofs = LittleShort (infielddefs[i].ofs);
1701 prog->fielddefs[i].s_name = LittleLong (infielddefs[i].s_name);
1704 // append the required fields
1705 for (i = 0;i < (int) numrequiredfields;i++)
1707 prog->fielddefs[prog->progs->numfielddefs].type = required_field[i].type;
1708 prog->fielddefs[prog->progs->numfielddefs].ofs = prog->progs->entityfields;
1709 prog->fielddefs[prog->progs->numfielddefs].s_name = PRVM_SetEngineString(required_field[i].name);
1710 if (prog->fielddefs[prog->progs->numfielddefs].type == ev_vector)
1711 prog->progs->entityfields += 3;
1713 prog->progs->entityfields++;
1714 prog->progs->numfielddefs++;
1717 // check required functions
1718 for(i=0 ; i < numrequiredfunc ; i++)
1719 if(PRVM_ED_FindFunction(required_func[i]) == 0)
1720 PRVM_ERROR("%s: %s not found in %s",PRVM_NAME, required_func[i], filename);
1722 // check required globals
1723 for(i=0 ; i < numrequiredglobals ; i++)
1724 if(PRVM_ED_FindGlobal(required_global[i]) == 0)
1725 PRVM_ERROR("%s: %s not found in %s",PRVM_NAME, required_global[i], filename);
1727 for (i=0 ; i<prog->progs->numglobals ; i++)
1728 ((int *)prog->globals.generic)[i] = LittleLong (((int *)prog->globals.generic)[i]);
1730 // moved edict_size calculation down here, below field adding code
1731 // LordHavoc: this no longer includes the prvm_edict_t header
1732 prog->edict_size = prog->progs->entityfields * 4;
1733 prog->edictareasize = prog->edict_size * prog->limit_edicts;
1735 // LordHavoc: bounds check anything static
1736 for (i = 0,st = prog->statements;i < prog->progs->numstatements;i++,st++)
1742 if ((unsigned short) st->a >= prog->progs->numglobals || st->b + i < 0 || st->b + i >= prog->progs->numstatements)
1743 PRVM_ERROR("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", i, PRVM_NAME);
1746 if (st->a + i < 0 || st->a + i >= prog->progs->numstatements)
1747 PRVM_ERROR("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, PRVM_NAME);
1749 // global global global
1784 if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->b >= prog->progs->numglobals || (unsigned short) st->c >= prog->progs->numglobals)
1785 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
1787 // global none global
1793 if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->c >= prog->progs->numglobals)
1794 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
1810 if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->b >= prog->progs->numglobals)
1811 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
1825 if ((unsigned short) st->a >= prog->progs->numglobals)
1826 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
1829 Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", st->op, i, PRVM_NAME);
1834 PRVM_LoadLNO(filename);
1838 prog->loaded = TRUE;
1840 // set flags & ddef_ts in prog
1846 PRVM_GCALL(init_cmd)();
1853 void PRVM_Fields_f (void)
1855 int i, j, ednum, used, usedamount;
1857 char tempstring[MAX_INPUTLINE], tempstring2[260];
1867 Con_Print("no progs loaded\n");
1874 Con_Print("prvm_fields <program name>\n");
1879 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1882 counts = (int *)Mem_Alloc(tempmempool, prog->progs->numfielddefs * sizeof(int));
1883 for (ednum = 0;ednum < prog->max_edicts;ednum++)
1885 ed = PRVM_EDICT_NUM(ednum);
1886 if (ed->priv.required->free)
1888 for (i = 1;i < prog->progs->numfielddefs;i++)
1890 d = &prog->fielddefs[i];
1891 name = PRVM_GetString(d->s_name);
1892 if (name[strlen(name)-2] == '_')
1893 continue; // skip _x, _y, _z vars
1894 v = (int *)((char *)ed->fields.vp + d->ofs*4);
1895 // if the value is still all 0, skip the field
1896 for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
1909 for (i = 0;i < prog->progs->numfielddefs;i++)
1911 d = &prog->fielddefs[i];
1912 name = PRVM_GetString(d->s_name);
1913 if (name[strlen(name)-2] == '_')
1914 continue; // skip _x, _y, _z vars
1915 switch(d->type & ~DEF_SAVEGLOBAL)
1918 strlcat(tempstring, "string ", sizeof(tempstring));
1921 strlcat(tempstring, "entity ", sizeof(tempstring));
1924 strlcat(tempstring, "function ", sizeof(tempstring));
1927 strlcat(tempstring, "field ", sizeof(tempstring));
1930 strlcat(tempstring, "void ", sizeof(tempstring));
1933 strlcat(tempstring, "float ", sizeof(tempstring));
1936 strlcat(tempstring, "vector ", sizeof(tempstring));
1939 strlcat(tempstring, "pointer ", sizeof(tempstring));
1942 dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
1943 strlcat(tempstring, tempstring2, sizeof(tempstring));
1946 if (strlen(name) > sizeof(tempstring2)-4)
1948 memcpy (tempstring2, name, sizeof(tempstring2)-4);
1949 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
1950 tempstring2[sizeof(tempstring2)-1] = 0;
1953 strlcat(tempstring, name, sizeof(tempstring));
1954 for (j = (int)strlen(name);j < 25;j++)
1955 strlcat(tempstring, " ", sizeof(tempstring));
1956 dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
1957 strlcat(tempstring, tempstring2, sizeof(tempstring));
1958 strlcat(tempstring, "\n", sizeof(tempstring));
1959 if (strlen(tempstring) >= sizeof(tempstring)/2)
1961 Con_Print(tempstring);
1967 usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
1971 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);
1976 void PRVM_Globals_f (void)
1979 const char *wildcard;
1985 Con_Print("no progs loaded\n");
1988 if(Cmd_Argc () < 2 || Cmd_Argc() > 3)
1990 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
1995 if(!PRVM_SetProgFromString (Cmd_Argv (1)))
1998 if( Cmd_Argc() == 3)
1999 wildcard = Cmd_Argv(2);
2003 Con_Printf("%s :", PRVM_NAME);
2005 for (i = 0;i < prog->progs->numglobaldefs;i++)
2008 if( !matchpattern( PRVM_GetString(prog->globaldefs[i].s_name), wildcard, 1) )
2013 Con_Printf("%s\n", PRVM_GetString(prog->globaldefs[i].s_name));
2015 Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->progs->numglobals, numculled, prog->progs->numglobals * 4);
2025 void PRVM_Global_f(void)
2028 if( Cmd_Argc() != 3 ) {
2029 Con_Printf( "prvm_global <program name> <global name>\n" );
2034 if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
2037 global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
2039 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2041 Con_Printf( "%s: %s\n", Cmd_Argv(2), PRVM_ValueString( (etype_t)global->type, (prvm_eval_t *) &prog->globals.generic[ global->ofs ] ) );
2050 void PRVM_GlobalSet_f(void)
2053 if( Cmd_Argc() != 4 ) {
2054 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2059 if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
2062 global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
2064 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2066 PRVM_ED_ParseEpair( NULL, global, Cmd_Argv(3), true );
2075 void PRVM_Init (void)
2077 Cmd_AddCommand ("prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2078 Cmd_AddCommand ("prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2079 Cmd_AddCommand ("prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2080 Cmd_AddCommand ("prvm_profile", PRVM_Profile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu)");
2081 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)");
2082 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)");
2083 Cmd_AddCommand ("prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2084 Cmd_AddCommand ("prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2085 Cmd_AddCommand ("prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2086 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)");
2087 Cmd_AddCommand ("prvm_printfunction", PRVM_PrintFunction_f, "prints a disassembly (QuakeC instructions) of the specified function in the selected VM (server, client, menu)");
2088 Cmd_AddCommand ("cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2089 Cmd_AddCommand ("menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2090 Cmd_AddCommand ("sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2091 // LordHavoc: optional runtime bounds checking (speed drain, but worth it for security, on by default - breaks most QCCX features (used by CRMod and others))
2092 #ifdef PRVM_BOUNDSCHECK_CVAR
2093 Cvar_RegisterVariable (&prvm_boundscheck);
2095 Cvar_RegisterVariable (&prvm_traceqc);
2096 Cvar_RegisterVariable (&prvm_statementprofiling);
2097 Cvar_RegisterVariable (&prvm_backtraceforwarnings);
2098 Cvar_RegisterVariable (&prvm_leaktest);
2099 Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames);
2109 void PRVM_InitProg(int prognr)
2111 if(prognr < 0 || prognr >= PRVM_MAXPROGS)
2112 Sys_Error("PRVM_InitProg: Invalid program number %i",prognr);
2114 prog = &prog_list[prognr];
2119 memset(prog, 0, sizeof(prvm_prog_t));
2120 prog->starttime = Sys_DoubleTime();
2122 prog->error_cmd = Host_Error;
2123 prog->leaktest_active = prvm_leaktest.integer;
2126 int PRVM_GetProgNr()
2128 return prog - prog_list;
2131 void *_PRVM_Alloc(size_t buffersize, const char *filename, int fileline)
2133 return _Mem_Alloc(prog->progs_mempool, buffersize, filename, fileline);
2136 void _PRVM_Free(void *buffer, const char *filename, int fileline)
2138 _Mem_Free(buffer, filename, fileline);
2141 void _PRVM_FreeAll(const char *filename, int fileline)
2144 prog->fielddefs = NULL;
2145 prog->functions = NULL;
2146 _Mem_EmptyPool(prog->progs_mempool, filename, fileline);
2149 // LordHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
2150 unsigned int PRVM_EDICT_NUM_ERROR(unsigned int n, char *filename, int fileline)
2152 PRVM_ERROR ("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", PRVM_NAME, n, filename, fileline);
2157 int NUM_FOR_EDICT_ERROR(prvm_edict_t *e)
2159 PRVM_ERROR ("PRVM_NUM_FOR_EDICT: bad pointer %p (world is %p, entity number would be %i)", e, prog->edicts, e - prog->edicts);
2163 int PRVM_NUM_FOR_EDICT(prvm_edict_t *e)
2166 n = e - prog->edicts;
2167 if ((unsigned int)n >= prog->limit_edicts)
2168 Host_Error ("PRVM_NUM_FOR_EDICT: bad pointer");
2172 //int NoCrash_NUM_FOR_EDICT(prvm_edict_t *e)
2174 // return e - prog->edicts;
2177 //#define PRVM_EDICT_TO_PROG(e) ((unsigned char *)(((prvm_edict_t *)e)->v) - (unsigned char *)(prog->edictsfields))
2178 //#define PRVM_PROG_TO_EDICT(e) (prog->edicts + ((e) / (progs->entityfields * 4)))
2179 int PRVM_EDICT_TO_PROG(prvm_edict_t *e)
2182 n = e - prog->edicts;
2183 if ((unsigned int)n >= (unsigned int)prog->max_edicts)
2184 Host_Error("PRVM_EDICT_TO_PROG: invalid edict %8p (number %i compared to world at %8p)", e, n, prog->edicts);
2185 return n;// EXPERIMENTAL
2186 //return (unsigned char *)e->v - (unsigned char *)prog->edictsfields;
2188 prvm_edict_t *PRVM_PROG_TO_EDICT(int n)
2190 if ((unsigned int)n >= (unsigned int)prog->max_edicts)
2191 Host_Error("PRVM_PROG_TO_EDICT: invalid edict number %i", n);
2192 return prog->edicts + n; // EXPERIMENTAL
2193 //return prog->edicts + ((n) / (progs->entityfields * 4));
2198 sizebuf_t vm_tempstringsbuf;
2200 const char *PRVM_GetString(int num)
2204 if (num < prog->stringssize)
2205 return prog->strings + num;
2208 if (num <= prog->stringssize + vm_tempstringsbuf.maxsize)
2210 num -= prog->stringssize;
2211 if (num < vm_tempstringsbuf.cursize)
2212 return (char *)vm_tempstringsbuf.data + num;
2215 VM_Warning("PRVM_GetString: Invalid temp-string offset (%i >= %i vm_tempstringsbuf.cursize)\n", num, vm_tempstringsbuf.cursize);
2222 VM_Warning("PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
2232 // special range reserved for tempstrings
2234 if (num < vm_tempstringsbuf.cursize)
2235 return (char *)vm_tempstringsbuf.data + num;
2238 VM_Warning("PRVM_GetString: Invalid temp-string offset (%i >= %i vm_tempstringsbuf.cursize)\n", num, vm_tempstringsbuf.cursize);
2244 if (num < prog->numknownstrings)
2246 if (!prog->knownstrings[num])
2247 VM_Warning("PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
2248 return prog->knownstrings[num];
2252 VM_Warning("PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
2258 int PRVM_SetEngineString(const char *s)
2263 if (s >= prog->strings && s <= prog->strings + prog->stringssize)
2264 PRVM_ERROR("PRVM_SetEngineString: s in prog->strings area");
2265 // if it's in the tempstrings area, use a reserved range
2266 // (otherwise we'd get millions of useless string offsets cluttering the database)
2267 if (s >= (char *)vm_tempstringsbuf.data && s < (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.maxsize)
2269 return prog->stringssize + (s - (char *)vm_tempstringsbuf.data);
2271 return -1 - ((1<<30) + (s - (char *)vm_tempstringsbuf.data));
2273 // see if it's a known string address
2274 for (i = 0;i < prog->numknownstrings;i++)
2275 if (prog->knownstrings[i] == s)
2277 // new unknown engine string
2278 if (developer.integer >= 200)
2279 Con_Printf("new engine string %p = \"%s\"\n", s, s);
2280 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2281 if (!prog->knownstrings[i])
2283 if (i >= prog->numknownstrings)
2285 if (i >= prog->maxknownstrings)
2287 const char **oldstrings = prog->knownstrings;
2288 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2289 const char **oldstrings_origin = prog->knownstrings_origin;
2290 prog->maxknownstrings += 128;
2291 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2292 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2293 if(prog->leaktest_active)
2294 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2295 if (prog->numknownstrings)
2297 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2298 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2299 if(prog->leaktest_active)
2300 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
2303 prog->numknownstrings++;
2305 prog->firstfreeknownstring = i + 1;
2306 prog->knownstrings[i] = s;
2307 prog->knownstrings_freeable[i] = false;
2308 if(prog->leaktest_active)
2309 prog->knownstrings_origin[i] = NULL;
2313 // temp string handling
2315 // all tempstrings go into this buffer consecutively, and it is reset
2316 // whenever PRVM_ExecuteProgram returns to the engine
2317 // (technically each PRVM_ExecuteProgram call saves the cursize value and
2318 // restores it on return, so multiple recursive calls can share the same
2320 // the buffer size is automatically grown as needed
2322 int PRVM_SetTempString(const char *s)
2328 size = (int)strlen(s) + 1;
2329 if (developer.integer >= 300)
2330 Con_Printf("PRVM_SetTempString: cursize %i, size %i\n", vm_tempstringsbuf.cursize, size);
2331 if (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
2333 sizebuf_t old = vm_tempstringsbuf;
2334 if (vm_tempstringsbuf.cursize + size >= 1<<28)
2335 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);
2336 vm_tempstringsbuf.maxsize = max(vm_tempstringsbuf.maxsize, 65536);
2337 while (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
2338 vm_tempstringsbuf.maxsize *= 2;
2339 if (vm_tempstringsbuf.maxsize != old.maxsize || vm_tempstringsbuf.data == NULL)
2341 if (developer.integer >= 100)
2342 Con_Printf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, vm_tempstringsbuf.maxsize/1024);
2343 vm_tempstringsbuf.data = Mem_Alloc(sv_mempool, vm_tempstringsbuf.maxsize);
2345 memcpy(vm_tempstringsbuf.data, old.data, old.cursize);
2350 t = (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.cursize;
2352 vm_tempstringsbuf.cursize += size;
2353 return PRVM_SetEngineString(t);
2356 int PRVM_AllocString(size_t bufferlength, char **pointer)
2361 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2362 if (!prog->knownstrings[i])
2364 if (i >= prog->numknownstrings)
2366 if (i >= prog->maxknownstrings)
2368 const char **oldstrings = prog->knownstrings;
2369 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2370 const char **oldstrings_origin = prog->knownstrings_origin;
2371 prog->maxknownstrings += 128;
2372 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2373 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2374 if(prog->leaktest_active)
2375 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2376 if (prog->numknownstrings)
2378 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2379 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2380 if(prog->leaktest_active)
2381 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
2383 // TODO why not Mem_Free the old ones?
2385 prog->numknownstrings++;
2387 prog->firstfreeknownstring = i + 1;
2388 prog->knownstrings[i] = (char *)PRVM_Alloc(bufferlength);
2389 prog->knownstrings_freeable[i] = true;
2390 if(prog->leaktest_active)
2391 prog->knownstrings_origin[i] = PRVM_AllocationOrigin();
2393 *pointer = (char *)(prog->knownstrings[i]);
2397 void PRVM_FreeString(int num)
2400 PRVM_ERROR("PRVM_FreeString: attempt to free a NULL string");
2401 else if (num >= 0 && num < prog->stringssize)
2402 PRVM_ERROR("PRVM_FreeString: attempt to free a constant string");
2403 else if (num < 0 && num >= -prog->numknownstrings)
2406 if (!prog->knownstrings[num])
2407 PRVM_ERROR("PRVM_FreeString: attempt to free a non-existent or already freed string");
2408 if (!prog->knownstrings_freeable[num])
2409 PRVM_ERROR("PRVM_FreeString: attempt to free a string owned by the engine");
2410 PRVM_Free((char *)prog->knownstrings[num]);
2411 if(prog->leaktest_active)
2412 if(prog->knownstrings_origin[num])
2413 PRVM_Free((char *)prog->knownstrings_origin[num]);
2414 prog->knownstrings[num] = NULL;
2415 prog->knownstrings_freeable[num] = false;
2416 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
2419 PRVM_ERROR("PRVM_FreeString: invalid string offset %i", num);
2422 static qboolean PRVM_IsStringReferenced(string_t string)
2426 for (i = 0;i < prog->progs->numglobaldefs;i++)
2428 ddef_t *d = &prog->globaldefs[i];
2429 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
2431 if(string == ((prvm_eval_t *) &prog->globals.generic[d->ofs])->string)
2435 for(j = 0; j < prog->num_edicts; ++j)
2437 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2438 if (ed->priv.required->free)
2440 for (i=0; i<prog->progs->numfielddefs; ++i)
2442 ddef_t *d = &prog->fielddefs[i];
2443 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
2445 if(string == ((prvm_eval_t *) &((float*)ed->fields.vp)[d->ofs])->string)
2453 static qboolean PRVM_IsEdictRelevant(prvm_edict_t *edict)
2455 if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
2456 return true; // world or clients
2457 switch(prog - prog_list)
2459 case PRVM_SERVERPROG:
2461 entvars_t *ev = edict->fields.server;
2462 if(ev->solid) // can block other stuff, or is a trigger?
2464 if(ev->modelindex) // visible ent?
2466 if(ev->effects) // particle effect?
2468 if(ev->think) // has a think function?
2469 if(ev->nextthink > 0) // that actually will eventually run?
2473 if(*prvm_leaktest_ignore_classnames.string)
2475 if(strstr(va(" %s ", prvm_leaktest_ignore_classnames.string), va(" %s ", PRVM_GetString(ev->classname))))
2480 case PRVM_CLIENTPROG:
2482 // TODO someone add more stuff here
2483 cl_entvars_t *ev = edict->fields.client;
2484 if(ev->modelindex) // visible ent?
2486 if(ev->effects) // particle effect?
2488 if(ev->think) // has a think function?
2489 if(ev->nextthink > 0) // that actually will eventually run?
2491 if(*prvm_leaktest_ignore_classnames.string)
2493 if(strstr(va(" %s ", prvm_leaktest_ignore_classnames.string), va(" %s ", PRVM_GetString(ev->classname))))
2499 // menu prog does not have classnames
2505 static qboolean PRVM_IsEdictReferenced(prvm_edict_t *edict, int mark)
2508 int edictnum = PRVM_NUM_FOR_EDICT(edict);
2509 const char *targetname = NULL;
2511 switch(prog - prog_list)
2513 case PRVM_SERVERPROG:
2514 targetname = PRVM_GetString(edict->fields.server->targetname);
2519 if(!*targetname) // ""
2522 for (i = 0;i < prog->progs->numglobaldefs;i++)
2524 ddef_t *d = &prog->globaldefs[i];
2525 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
2527 if(edictnum == ((prvm_eval_t *) &prog->globals.generic[d->ofs])->edict)
2531 for(j = 0; j < prog->num_edicts; ++j)
2533 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2534 if (ed->priv.required->mark < mark)
2540 const char *target = PRVM_GetString(ed->fields.server->target);
2542 if(!strcmp(target, targetname))
2545 for (i=0; i<prog->progs->numfielddefs; ++i)
2547 ddef_t *d = &prog->fielddefs[i];
2548 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
2550 if(edictnum == ((prvm_eval_t *) &((float*)ed->fields.vp)[d->ofs])->edict)
2558 static void PRVM_MarkReferencedEdicts()
2564 for(j = 0; j < prog->num_edicts; ++j)
2566 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2567 if(ed->priv.required->free)
2569 ed->priv.required->mark = PRVM_IsEdictRelevant(ed) ? 1 : 0;
2576 for(j = 0; j < prog->num_edicts; ++j)
2578 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2579 if(ed->priv.required->free)
2581 if(ed->priv.required->mark)
2583 if(PRVM_IsEdictReferenced(ed, stage))
2585 ed->priv.required->mark = stage + 1;
2592 Con_DPrintf("leak check used %d stages to find all references\n", stage);
2595 void PRVM_LeakTest()
2598 qboolean leaked = false;
2600 if(!prog->leaktest_active)
2604 for (i = 0; i < prog->numknownstrings; ++i)
2606 if(prog->knownstrings[i])
2607 if(prog->knownstrings_freeable[i])
2608 if(prog->knownstrings_origin[i])
2609 if(!PRVM_IsStringReferenced(-1 - i))
2611 Con_Printf("Unreferenced string found!\n Value: %s\n Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
2617 PRVM_MarkReferencedEdicts();
2618 for(j = 0; j < prog->num_edicts; ++j)
2620 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2621 if(ed->priv.required->free)
2623 if(!ed->priv.required->mark)
2624 if(ed->priv.required->allocation_origin)
2626 Con_Printf("Unreferenced edict found!\n Allocated at: %s\n", ed->priv.required->allocation_origin);
2627 PRVM_ED_Print(ed, NULL);
2633 for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
2635 prvm_stringbuffer_t *stringbuffer = Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
2637 if(stringbuffer->origin)
2639 Con_Printf("Open string buffer handle found!\n Allocated at: %s\n", stringbuffer->origin);
2644 for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
2646 if(prog->openfiles[i])
2647 if(prog->openfiles_origin[i])
2649 Con_Printf("Open file handle found!\n Allocated at: %s\n", prog->openfiles_origin[i]);
2654 for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
2656 if(prog->opensearches[i])
2657 if(prog->opensearches_origin[i])
2659 Con_Printf("Open search handle found!\n Allocated at: %s\n", prog->opensearches_origin[i]);
2665 Con_Printf("Congratulations. No leaks found.\n");