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 cvar_t prvm_language = {0, "prvm_language", "", "when set, loads progs.dat.LANGUAGENAME.po for string translations; when set to dump, progs.dat.dump.po is written from the strings in the progs"};
35 // LordHavoc: prints every opcode as it executes - warning: this is significant spew
36 cvar_t prvm_traceqc = {0, "prvm_traceqc", "0", "prints every QuakeC statement as it is executed (only for really thorough debugging!)"};
37 // LordHavoc: counts usage of each QuakeC statement
38 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)"};
39 cvar_t prvm_backtraceforwarnings = {0, "prvm_backtraceforwarnings", "0", "print a backtrace for warnings too"};
40 cvar_t prvm_leaktest = {0, "prvm_leaktest", "0", "try to detect memory leaks in strings or entities"};
41 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)"};
42 cvar_t prvm_errordump = {0, "prvm_errordump", "0", "write a savegame on crash to crash-server.dmp"};
43 cvar_t prvm_reuseedicts_startuptime = {0, "prvm_reuseedicts_startuptime", "2", "allows immediate re-use of freed entity slots during start of new level (value in seconds)"};
44 cvar_t prvm_reuseedicts_neverinsameframe = {0, "prvm_reuseedicts_neverinsameframe", "1", "never allows re-use of freed entity slots during same frame"};
46 static double prvm_reuseedicts_always_allow = 0;
47 qboolean prvm_runawaycheck = true;
49 // LordHavoc: optional runtime bounds checking (speed drain, but worth it for security, on by default - breaks most QCCX features (used by CRMod and others))
50 // 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)
51 qboolean prvm_boundscheck = true;
53 extern sizebuf_t vm_tempstringsbuf;
55 //============================================================================
63 void PRVM_MEM_Alloc(void)
67 // reserve space for the null entity aka world
68 // check bound of max_edicts
69 prog->max_edicts = bound(1 + prog->reserved_edicts, prog->max_edicts, prog->limit_edicts);
70 prog->num_edicts = bound(1 + prog->reserved_edicts, prog->num_edicts, prog->max_edicts);
72 // edictprivate_size has to be min as big prvm_edict_private_t
73 prog->edictprivate_size = max(prog->edictprivate_size,(int)sizeof(prvm_edict_private_t));
76 prog->edicts = (prvm_edict_t *)Mem_Alloc(prog->progs_mempool,prog->limit_edicts * sizeof(prvm_edict_t));
78 // alloc edict private space
79 prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size);
82 prog->entityfieldsarea = prog->entityfields * prog->max_edicts;
83 prog->edictsfields = (vec_t *)Mem_Alloc(prog->progs_mempool, prog->entityfieldsarea * sizeof(vec_t));
86 for(i = 0; i < prog->max_edicts; i++)
88 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
89 prog->edicts[i].fields.vp = prog->edictsfields + i * prog->entityfields;
95 PRVM_MEM_IncreaseEdicts
98 void PRVM_MEM_IncreaseEdicts(void)
102 if(prog->max_edicts >= prog->limit_edicts)
105 PRVM_GCALL(begin_increase_edicts)();
108 prog->max_edicts = min(prog->max_edicts + 256, prog->limit_edicts);
110 prog->entityfieldsarea = prog->entityfields * prog->max_edicts;
111 prog->edictsfields = (vec_t*)Mem_Realloc(prog->progs_mempool, (void *)prog->edictsfields, prog->entityfieldsarea * sizeof(vec_t));
112 prog->edictprivate = (void *)Mem_Realloc(prog->progs_mempool, (void *)prog->edictprivate, prog->max_edicts * prog->edictprivate_size);
114 //set e and v pointers
115 for(i = 0; i < prog->max_edicts; i++)
117 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
118 prog->edicts[i].fields.vp = prog->edictsfields + i * prog->entityfields;
121 PRVM_GCALL(end_increase_edicts)();
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(void)
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 Returns if this particular edict could get allocated by PRVM_ED_Alloc
243 qboolean PRVM_ED_CanAlloc(prvm_edict_t *e)
245 if(!e->priv.required->free)
247 if(prvm_reuseedicts_always_allow == realtime)
249 if(realtime <= e->priv.required->freetime && prvm_reuseedicts_neverinsameframe.integer)
250 return false; // never allow reuse in same frame (causes networking trouble)
251 if(e->priv.required->freetime < prog->starttime + prvm_reuseedicts_startuptime.value)
253 if(realtime > e->priv.required->freetime + 1)
255 return false; // entity slot still blocked because the entity was freed less than one second ago
262 Either finds a free edict, or allocates a new one.
263 Try to avoid reusing an entity that was recently freed, because it
264 can cause the client to think the entity morphed into something else
265 instead of being removed and recreated, which can cause interpolated
266 angles and bad trails.
269 prvm_edict_t *PRVM_ED_Alloc (void)
274 // the client qc dont need maxclients
275 // thus it doesnt need to use svs.maxclients
276 // AK: changed i=svs.maxclients+1
277 // AK: changed so the edict 0 wont spawn -> used as reserved/world entity
278 // although the menu/client has no world
279 for (i = prog->reserved_edicts + 1;i < prog->num_edicts;i++)
281 e = PRVM_EDICT_NUM(i);
282 if(PRVM_ED_CanAlloc(e))
284 PRVM_ED_ClearEdict (e);
285 e->priv.required->allocation_origin = PRVM_AllocationOrigin();
290 if (i == prog->limit_edicts)
291 PRVM_ERROR ("%s: PRVM_ED_Alloc: no free edicts",PRVM_NAME);
294 if (prog->num_edicts >= prog->max_edicts)
295 PRVM_MEM_IncreaseEdicts();
297 e = PRVM_EDICT_NUM(i);
298 PRVM_ED_ClearEdict (e);
300 e->priv.required->allocation_origin = PRVM_AllocationOrigin();
309 Marks the edict as free
310 FIXME: walk all entities and NULL out references to this entity
313 void PRVM_ED_Free (prvm_edict_t *ed)
315 // dont delete the null entity (world) or reserved edicts
316 if(PRVM_NUM_FOR_EDICT(ed) <= prog->reserved_edicts )
319 PRVM_GCALL(free_edict)(ed);
321 ed->priv.required->free = true;
322 ed->priv.required->freetime = realtime;
323 if(ed->priv.required->allocation_origin)
325 PRVM_Free((char *)ed->priv.required->allocation_origin);
326 ed->priv.required->allocation_origin = NULL;
330 //===========================================================================
337 ddef_t *PRVM_ED_GlobalAtOfs (int ofs)
342 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
344 def = &prog->globaldefs[i];
356 ddef_t *PRVM_ED_FieldAtOfs (int ofs)
361 for (i=0 ; i<prog->progs->numfielddefs ; i++)
363 def = &prog->fielddefs[i];
375 ddef_t *PRVM_ED_FindField (const char *name)
380 for (i=0 ; i<prog->progs->numfielddefs ; i++)
382 def = &prog->fielddefs[i];
383 if (!strcmp(PRVM_GetString(def->s_name), name))
394 ddef_t *PRVM_ED_FindGlobal (const char *name)
399 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
401 def = &prog->globaldefs[i];
402 if (!strcmp(PRVM_GetString(def->s_name), name))
414 mfunction_t *PRVM_ED_FindFunction (const char *name)
419 for (i=0 ; i<prog->progs->numfunctions ; i++)
421 func = &prog->functions[i];
422 if (!strcmp(PRVM_GetString(func->s_name), name))
433 Returns a string describing *data in a type specific manner
436 char *PRVM_ValueString (etype_t type, prvm_eval_t *val)
438 static char line[MAX_INPUTLINE];
443 type = (etype_t)((int) type & ~DEF_SAVEGLOBAL);
448 strlcpy (line, PRVM_GetString (val->string), sizeof (line));
452 if (n < 0 || n >= prog->max_edicts)
453 dpsnprintf (line, sizeof(line), "entity %i (invalid!)", n);
455 dpsnprintf (line, sizeof(line), "entity %i", n);
458 f = prog->functions + val->function;
459 dpsnprintf (line, sizeof(line), "%s()", PRVM_GetString(f->s_name));
462 def = PRVM_ED_FieldAtOfs ( val->_int );
463 dpsnprintf (line, sizeof(line), ".%s", PRVM_GetString(def->s_name));
466 dpsnprintf (line, sizeof(line), "void");
469 // LordHavoc: changed from %5.1f to %10.4f
470 dpsnprintf (line, sizeof(line), "%10.4f", val->_float);
473 // LordHavoc: changed from %5.1f to %10.4f
474 dpsnprintf (line, sizeof(line), "'%10.4f %10.4f %10.4f'", val->vector[0], val->vector[1], val->vector[2]);
477 dpsnprintf (line, sizeof(line), "pointer");
480 dpsnprintf (line, sizeof(line), "bad type %i", (int) type);
491 Returns a string describing *data in a type specific manner
492 Easier to parse than PR_ValueString
495 char *PRVM_UglyValueString (etype_t type, prvm_eval_t *val)
497 static char line[MAX_INPUTLINE];
503 type = (etype_t)((int)type & ~DEF_SAVEGLOBAL);
508 // Parse the string a bit to turn special characters
509 // (like newline, specifically) into escape codes,
510 // this fixes saving games from various mods
511 s = PRVM_GetString (val->string);
512 for (i = 0;i < (int)sizeof(line) - 2 && *s;)
541 dpsnprintf (line, sizeof (line), "%i", PRVM_NUM_FOR_EDICT(PRVM_PROG_TO_EDICT(val->edict)));
544 f = prog->functions + val->function;
545 strlcpy (line, PRVM_GetString (f->s_name), sizeof (line));
548 def = PRVM_ED_FieldAtOfs ( val->_int );
549 dpsnprintf (line, sizeof (line), ".%s", PRVM_GetString(def->s_name));
552 dpsnprintf (line, sizeof (line), "void");
555 dpsnprintf (line, sizeof (line), "%f", val->_float);
558 dpsnprintf (line, sizeof (line), "%f %f %f", val->vector[0], val->vector[1], val->vector[2]);
561 dpsnprintf (line, sizeof (line), "bad type %i", type);
572 Returns a string with a description and the contents of a global,
573 padded to 20 field width
576 char *PRVM_GlobalString (int ofs)
582 static char line[128];
584 val = (void *)&prog->globals.generic[ofs];
585 def = PRVM_ED_GlobalAtOfs(ofs);
587 dpsnprintf (line, sizeof(line), "GLOBAL%i", ofs);
590 s = PRVM_ValueString ((etype_t)def->type, (prvm_eval_t *)val);
591 dpsnprintf (line, sizeof(line), "%s (=%s)", PRVM_GetString(def->s_name), s);
595 //for ( ; i<20 ; i++)
596 // strcat (line," ");
602 char *PRVM_GlobalStringNoContents (int ofs)
606 static char line[128];
608 def = PRVM_ED_GlobalAtOfs(ofs);
610 dpsnprintf (line, sizeof(line), "GLOBAL%i", ofs);
612 dpsnprintf (line, sizeof(line), "%s", PRVM_GetString(def->s_name));
615 //for ( ; i<20 ; i++)
616 // strcat (line," ");
630 // LordHavoc: optimized this to print out much more quickly (tempstring)
631 // LordHavoc: changed to print out every 4096 characters (incase there are a lot of fields to print)
632 void PRVM_ED_Print(prvm_edict_t *ed, const char *wildcard_fieldname)
640 char tempstring[MAX_INPUTLINE], tempstring2[260]; // temporary string buffers
642 if (ed->priv.required->free)
644 Con_Printf("%s: FREE\n",PRVM_NAME);
649 dpsnprintf(tempstring, sizeof(tempstring), "\n%s EDICT %i:\n", PRVM_NAME, PRVM_NUM_FOR_EDICT(ed));
650 for (i=1 ; i<prog->progs->numfielddefs ; i++)
652 d = &prog->fielddefs[i];
653 name = PRVM_GetString(d->s_name);
654 if (name[strlen(name)-2] == '_')
655 continue; // skip _x, _y, _z vars
657 // Check Field Name Wildcard
658 if(wildcard_fieldname)
659 if( !matchpattern(name, wildcard_fieldname, 1) )
660 // Didn't match; skip
663 v = (int *)(ed->fields.vp + d->ofs);
665 // if the value is still all 0, skip the field
666 type = d->type & ~DEF_SAVEGLOBAL;
668 for (j=0 ; j<prvm_type_size[type] ; j++)
671 if (j == prvm_type_size[type])
674 if (strlen(name) > sizeof(tempstring2)-4)
676 memcpy (tempstring2, name, sizeof(tempstring2)-4);
677 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
678 tempstring2[sizeof(tempstring2)-1] = 0;
681 strlcat(tempstring, name, sizeof(tempstring));
682 for (l = strlen(name);l < 14;l++)
683 strlcat(tempstring, " ", sizeof(tempstring));
684 strlcat(tempstring, " ", sizeof(tempstring));
686 name = PRVM_ValueString((etype_t)d->type, (prvm_eval_t *)v);
687 if (strlen(name) > sizeof(tempstring2)-4)
689 memcpy (tempstring2, name, sizeof(tempstring2)-4);
690 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
691 tempstring2[sizeof(tempstring2)-1] = 0;
694 strlcat(tempstring, name, sizeof(tempstring));
695 strlcat(tempstring, "\n", sizeof(tempstring));
696 if (strlen(tempstring) >= sizeof(tempstring)/2)
698 Con_Print(tempstring);
703 Con_Print(tempstring);
713 extern cvar_t developer_entityparsing;
714 void PRVM_ED_Write (qfile_t *f, prvm_edict_t *ed)
724 if (ed->priv.required->free)
730 for (i=1 ; i<prog->progs->numfielddefs ; i++)
732 d = &prog->fielddefs[i];
733 name = PRVM_GetString(d->s_name);
735 if(developer_entityparsing.integer)
736 Con_Printf("PRVM_ED_Write: at entity %d field %s\n", PRVM_NUM_FOR_EDICT(ed), name);
738 if (name[strlen(name)-2] == '_')
739 continue; // skip _x, _y, _z vars
741 v = (int *)(ed->fields.vp + d->ofs);
743 // if the value is still all 0, skip the field
744 type = d->type & ~DEF_SAVEGLOBAL;
745 for (j=0 ; j<prvm_type_size[type] ; j++)
748 if (j == prvm_type_size[type])
751 FS_Printf(f,"\"%s\" ",name);
752 prog->statestring = va("PRVM_ED_Write, ent=%d, name=%s", i, name);
753 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString((etype_t)d->type, (prvm_eval_t *)v));
754 prog->statestring = NULL;
760 void PRVM_ED_PrintNum (int ent, const char *wildcard_fieldname)
762 PRVM_ED_Print(PRVM_EDICT_NUM(ent), wildcard_fieldname);
767 PRVM_ED_PrintEdicts_f
769 For debugging, prints all the entities in the current server
772 void PRVM_ED_PrintEdicts_f (void)
775 const char *wildcard_fieldname;
777 if(Cmd_Argc() < 2 || Cmd_Argc() > 3)
779 Con_Print("prvm_edicts <program name> <optional field name wildcard>\n");
784 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
788 wildcard_fieldname = Cmd_Argv(2);
790 wildcard_fieldname = NULL;
792 Con_Printf("%s: %i entities\n", PRVM_NAME, prog->num_edicts);
793 for (i=0 ; i<prog->num_edicts ; i++)
794 PRVM_ED_PrintNum (i, wildcard_fieldname);
803 For debugging, prints a single edict
806 void PRVM_ED_PrintEdict_f (void)
809 const char *wildcard_fieldname;
811 if(Cmd_Argc() < 3 || Cmd_Argc() > 4)
813 Con_Print("prvm_edict <program name> <edict number> <optional field name wildcard>\n");
818 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
821 i = atoi (Cmd_Argv(2));
822 if (i >= prog->num_edicts)
824 Con_Print("Bad edict number\n");
829 // Optional Wildcard Provided
830 wildcard_fieldname = Cmd_Argv(3);
833 wildcard_fieldname = NULL;
834 PRVM_ED_PrintNum (i, wildcard_fieldname);
846 // 2 possibilities : 1. just displaying the active edict count
847 // 2. making a function pointer [x]
848 void PRVM_ED_Count_f (void)
856 Con_Print("prvm_count <program name>\n");
861 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
864 if(prog->count_edicts)
865 prog->count_edicts();
869 for (i=0 ; i<prog->num_edicts ; i++)
871 ent = PRVM_EDICT_NUM(i);
872 if (ent->priv.required->free)
877 Con_Printf("num_edicts:%3i\n", prog->num_edicts);
878 Con_Printf("active :%3i\n", active);
885 ==============================================================================
889 FIXME: need to tag constants, doesn't really work
890 ==============================================================================
898 void PRVM_ED_WriteGlobals (qfile_t *f)
906 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
908 def = &prog->globaldefs[i];
910 if ( !(def->type & DEF_SAVEGLOBAL) )
912 type &= ~DEF_SAVEGLOBAL;
914 if (type != ev_string && type != ev_float && type != ev_entity)
917 name = PRVM_GetString(def->s_name);
919 if(developer_entityparsing.integer)
920 Con_Printf("PRVM_ED_WriteGlobals: at global %s\n", name);
922 prog->statestring = va("PRVM_ED_WriteGlobals, name=%s", name);
923 FS_Printf(f,"\"%s\" ", name);
924 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString((etype_t)type, (prvm_eval_t *)&prog->globals.generic[def->ofs]));
925 prog->statestring = NULL;
935 void PRVM_ED_ParseGlobals (const char *data)
937 char keyname[MAX_INPUTLINE];
943 if (!COM_ParseToken_Simple(&data, false, false))
944 PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace");
945 if (com_token[0] == '}')
948 if (developer_entityparsing.integer)
949 Con_Printf("Key: \"%s\"", com_token);
951 strlcpy (keyname, com_token, sizeof(keyname));
954 if (!COM_ParseToken_Simple(&data, false, true))
955 PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace");
957 if (developer_entityparsing.integer)
958 Con_Printf(" \"%s\"\n", com_token);
960 if (com_token[0] == '}')
961 PRVM_ERROR ("PRVM_ED_ParseGlobals: closing brace without data");
963 key = PRVM_ED_FindGlobal (keyname);
966 Con_DPrintf("'%s' is not a global on %s\n", keyname, PRVM_NAME);
970 if (!PRVM_ED_ParseEpair(NULL, key, com_token, true))
971 PRVM_ERROR ("PRVM_ED_ParseGlobals: parse error");
975 //============================================================================
982 Can parse either fields or globals
983 returns false if error
986 qboolean PRVM_ED_ParseEpair(prvm_edict_t *ent, ddef_t *key, const char *s, qboolean parsebackslash)
995 val = (prvm_eval_t *)(ent->fields.vp + key->ofs);
997 val = (prvm_eval_t *)(prog->globals.generic + key->ofs);
998 switch (key->type & ~DEF_SAVEGLOBAL)
1001 l = (int)strlen(s) + 1;
1002 val->string = PRVM_AllocString(l, &new_p);
1003 for (i = 0;i < l;i++)
1005 if (s[i] == '\\' && s[i+1] && parsebackslash)
1010 else if (s[i] == 'r')
1021 while (*s && ISWHITESPACE(*s))
1023 val->_float = atof(s);
1027 for (i = 0;i < 3;i++)
1029 while (*s && ISWHITESPACE(*s))
1033 val->vector[i] = atof(s);
1034 while (!ISWHITESPACE(*s))
1042 while (*s && ISWHITESPACE(*s))
1045 if (i >= prog->limit_edicts)
1046 Con_Printf("PRVM_ED_ParseEpair: ev_entity reference too large (edict %u >= MAX_EDICTS %u) on %s\n", (unsigned int)i, prog->limit_edicts, PRVM_NAME);
1047 while (i >= prog->max_edicts)
1048 PRVM_MEM_IncreaseEdicts();
1049 // if IncreaseEdicts was called the base pointer needs to be updated
1051 val = (prvm_eval_t *)(ent->fields.vp + key->ofs);
1052 val->edict = PRVM_EDICT_TO_PROG(PRVM_EDICT_NUM((int)i));
1058 Con_DPrintf("PRVM_ED_ParseEpair: Bogus field name %s in %s\n", s, PRVM_NAME);
1061 def = PRVM_ED_FindField(s + 1);
1064 Con_DPrintf("PRVM_ED_ParseEpair: Can't find field %s in %s\n", s, PRVM_NAME);
1067 val->_int = def->ofs;
1071 func = PRVM_ED_FindFunction(s);
1074 Con_Printf("PRVM_ED_ParseEpair: Can't find function %s in %s\n", s, PRVM_NAME);
1077 val->function = func - prog->functions;
1081 Con_Printf("PRVM_ED_ParseEpair: Unknown key->type %i for key \"%s\" on %s\n", key->type, PRVM_GetString(key->s_name), PRVM_NAME);
1091 Console command to send a string to QC function GameCommand of the
1095 sv_cmd adminmsg 3 "do not teamkill"
1096 cl_cmd someclientcommand
1097 menu_cmd somemenucommand
1099 All progs can support this extension; sg calls it in server QC, cg in client
1103 void PRVM_GameCommand(const char *whichprogs, const char *whichcmd)
1107 Con_Printf("%s text...\n", whichcmd);
1112 if(!PRVM_SetProgFromString(whichprogs))
1113 // note: this is not PRVM_SetProg because that one aborts "hard" using PRVM_Error
1114 // also, it makes printing error messages easier!
1116 Con_Printf("%s program not loaded.\n", whichprogs);
1120 if(!prog->funcoffsets.GameCommand)
1122 Con_Printf("%s program do not support GameCommand!\n", whichprogs);
1126 int restorevm_tempstringsbuf_cursize;
1131 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
1132 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(s ? s : "");
1133 PRVM_ExecuteProgram (prog->funcoffsets.GameCommand, "QC function GameCommand is missing");
1134 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1139 void PRVM_GameCommand_Server_f(void)
1141 PRVM_GameCommand("server", "sv_cmd");
1143 void PRVM_GameCommand_Client_f(void)
1145 PRVM_GameCommand("client", "cl_cmd");
1147 void PRVM_GameCommand_Menu_f(void)
1149 PRVM_GameCommand("menu", "menu_cmd");
1156 Console command to load a field of a specified edict
1159 void PRVM_ED_EdictGet_f(void)
1166 if(Cmd_Argc() != 4 && Cmd_Argc() != 5)
1168 Con_Print("prvm_edictget <program name> <edict number> <field> [<cvar>]\n");
1173 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1175 Con_Printf("Wrong program name %s !\n", Cmd_Argv(1));
1179 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1181 if((key = PRVM_ED_FindField(Cmd_Argv(3))) == 0)
1183 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1187 v = (prvm_eval_t *)(ed->fields.vp + key->ofs);
1188 s = PRVM_UglyValueString(key->type, v);
1191 cvar_t *cvar = Cvar_FindVar(Cmd_Argv(4));
1192 if (cvar && cvar->flags & CVAR_READONLY)
1194 Con_Printf("prvm_edictget: %s is read-only\n", cvar->name);
1197 Cvar_Get(Cmd_Argv(4), s, 0, NULL);
1200 Con_Printf("%s\n", s);
1206 void PRVM_ED_GlobalGet_f(void)
1212 if(Cmd_Argc() != 3 && Cmd_Argc() != 4)
1214 Con_Print("prvm_globalget <program name> <global> [<cvar>]\n");
1219 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1221 Con_Printf("Wrong program name %s !\n", Cmd_Argv(1));
1225 key = PRVM_ED_FindGlobal(Cmd_Argv(2));
1228 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
1232 v = (prvm_eval_t *) &prog->globals.generic[key->ofs];
1233 s = PRVM_UglyValueString(key->type, v);
1236 cvar_t *cvar = Cvar_FindVar(Cmd_Argv(3));
1237 if (cvar && cvar->flags & CVAR_READONLY)
1239 Con_Printf("prvm_globalget: %s is read-only\n", cvar->name);
1242 Cvar_Get(Cmd_Argv(3), s, 0, NULL);
1245 Con_Printf("%s\n", s);
1255 Console command to set a field of a specified edict
1258 void PRVM_ED_EdictSet_f(void)
1265 Con_Print("prvm_edictset <program name> <edict number> <field> <value>\n");
1270 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1272 Con_Printf("Wrong program name %s !\n", Cmd_Argv(1));
1276 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1278 if((key = PRVM_ED_FindField(Cmd_Argv(3))) == 0)
1279 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1281 PRVM_ED_ParseEpair(ed, key, Cmd_Argv(4), true);
1287 ====================
1290 Parses an edict out of the given string, returning the new position
1291 ed should be a properly initialized empty edict.
1292 Used for initial level load and for savegames.
1293 ====================
1295 const char *PRVM_ED_ParseEdict (const char *data, prvm_edict_t *ent)
1305 // go through all the dictionary pairs
1309 if (!COM_ParseToken_Simple(&data, false, false))
1310 PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1311 if (developer_entityparsing.integer)
1312 Con_Printf("Key: \"%s\"", com_token);
1313 if (com_token[0] == '}')
1316 // anglehack is to allow QuakeEd to write single scalar angles
1317 // and allow them to be turned into vectors. (FIXME...)
1318 if (!strcmp(com_token, "angle"))
1320 strlcpy (com_token, "angles", sizeof(com_token));
1326 // FIXME: change light to _light to get rid of this hack
1327 if (!strcmp(com_token, "light"))
1328 strlcpy (com_token, "light_lev", sizeof(com_token)); // hack for single light def
1330 strlcpy (keyname, com_token, sizeof(keyname));
1332 // another hack to fix keynames with trailing spaces
1333 n = strlen(keyname);
1334 while (n && keyname[n-1] == ' ')
1341 if (!COM_ParseToken_Simple(&data, false, false))
1342 PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1343 if (developer_entityparsing.integer)
1344 Con_Printf(" \"%s\"\n", com_token);
1346 if (com_token[0] == '}')
1347 PRVM_ERROR ("PRVM_ED_ParseEdict: closing brace without data");
1351 // ignore attempts to set key "" (this problem occurs in nehahra neh1m8.bsp)
1355 // keynames with a leading underscore are used for utility comments,
1356 // and are immediately discarded by quake
1357 if (keyname[0] == '_')
1360 key = PRVM_ED_FindField (keyname);
1363 Con_DPrintf("%s: '%s' is not a field\n", PRVM_NAME, keyname);
1370 strlcpy (temp, com_token, sizeof(temp));
1371 dpsnprintf (com_token, sizeof(com_token), "0 %s 0", temp);
1374 if (!PRVM_ED_ParseEpair(ent, key, com_token, strcmp(keyname, "wad") != 0))
1375 PRVM_ERROR ("PRVM_ED_ParseEdict: parse error");
1379 ent->priv.required->free = true;
1387 PRVM_ED_LoadFromFile
1389 The entities are directly placed in the array, rather than allocated with
1390 PRVM_ED_Alloc, because otherwise an error loading the map would have entity
1391 number references out of order.
1393 Creates a server's entity / program execution context by
1394 parsing textual entity definitions out of an ent file.
1396 Used for both fresh maps and savegame loads. A fresh map would also need
1397 to call PRVM_ED_CallSpawnFunctions () to let the objects initialize themselves.
1400 void PRVM_ED_LoadFromFile (const char *data)
1403 int parsed, inhibited, spawned, died;
1404 const char *funcname;
1412 prvm_reuseedicts_always_allow = realtime;
1417 // parse the opening brace
1418 if (!COM_ParseToken_Simple(&data, false, false))
1420 if (com_token[0] != '{')
1421 PRVM_ERROR ("PRVM_ED_LoadFromFile: %s: found %s when expecting {", PRVM_NAME, com_token);
1423 // CHANGED: this is not conform to PR_LoadFromFile
1424 if(prog->loadintoworld)
1426 prog->loadintoworld = false;
1427 ent = PRVM_EDICT_NUM(0);
1430 ent = PRVM_ED_Alloc();
1433 if (ent != prog->edicts) // hack
1434 memset (ent->fields.vp, 0, prog->progs->entityfields * 4);
1436 data = PRVM_ED_ParseEdict (data, ent);
1439 // remove the entity ?
1440 if(prog->load_edict && !prog->load_edict(ent))
1447 if (prog->funcoffsets.SV_OnEntityPreSpawnFunction)
1450 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1451 PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityPreSpawnFunction, "QC function SV_OnEntityPreSpawnFunction is missing");
1454 if(ent->priv.required->free)
1461 // immediately call spawn function, but only if there is a self global and a classname
1463 if(!ent->priv.required->free)
1464 if(prog->globaloffsets.self >= 0 && prog->fieldoffsets.classname >= 0)
1466 string_t handle = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.classname)->string;
1469 Con_Print("No classname for:\n");
1470 PRVM_ED_Print(ent, NULL);
1475 // look for the spawn function
1476 funcname = PRVM_GetString(handle);
1477 func = PRVM_ED_FindFunction (va("spawnfunc_%s", funcname));
1479 if(prog->globaloffsets.require_spawnfunc_prefix < 0)
1480 func = PRVM_ED_FindFunction (funcname);
1484 // check for OnEntityNoSpawnFunction
1485 if (prog->funcoffsets.SV_OnEntityNoSpawnFunction)
1488 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1489 PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityNoSpawnFunction, "QC function SV_OnEntityNoSpawnFunction is missing");
1493 if (developer.integer) // don't confuse non-developers with errors
1495 Con_Print("No spawn function for:\n");
1496 PRVM_ED_Print(ent, NULL);
1499 continue; // not included in "inhibited" count
1505 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1506 PRVM_ExecuteProgram (func - prog->functions, "");
1510 if(!ent->priv.required->free)
1511 if (prog->funcoffsets.SV_OnEntityPostSpawnFunction)
1514 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1515 PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityPostSpawnFunction, "QC function SV_OnEntityPostSpawnFunction is missing");
1519 if (ent->priv.required->free)
1523 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);
1525 prvm_reuseedicts_always_allow = 0;
1528 void PRVM_FindOffsets(void)
1530 // field and global searches use -1 for NULL
1531 memset(&prog->fieldoffsets, -1, sizeof(prog->fieldoffsets));
1532 memset(&prog->globaloffsets, -1, sizeof(prog->globaloffsets));
1533 // functions use 0 for NULL
1534 memset(&prog->funcoffsets, 0, sizeof(prog->funcoffsets));
1536 // server and client qc use a lot of similar fields, so this is combined
1537 prog->fieldoffsets.SendEntity = PRVM_ED_FindFieldOffset("SendEntity");
1538 prog->fieldoffsets.SendFlags = PRVM_ED_FindFieldOffset("SendFlags");
1539 prog->fieldoffsets.Version = PRVM_ED_FindFieldOffset("Version");
1540 prog->fieldoffsets.alpha = PRVM_ED_FindFieldOffset("alpha");
1541 prog->fieldoffsets.ammo_cells1 = PRVM_ED_FindFieldOffset("ammo_cells1");
1542 prog->fieldoffsets.ammo_lava_nails = PRVM_ED_FindFieldOffset("ammo_lava_nails");
1543 prog->fieldoffsets.ammo_multi_rockets = PRVM_ED_FindFieldOffset("ammo_multi_rockets");
1544 prog->fieldoffsets.ammo_nails1 = PRVM_ED_FindFieldOffset("ammo_nails1");
1545 prog->fieldoffsets.ammo_plasma = PRVM_ED_FindFieldOffset("ammo_plasma");
1546 prog->fieldoffsets.ammo_rockets1 = PRVM_ED_FindFieldOffset("ammo_rockets1");
1547 prog->fieldoffsets.ammo_shells1 = PRVM_ED_FindFieldOffset("ammo_shells1");
1548 prog->fieldoffsets.angles = PRVM_ED_FindFieldOffset("angles");
1549 prog->fieldoffsets.button3 = PRVM_ED_FindFieldOffset("button3");
1550 prog->fieldoffsets.button4 = PRVM_ED_FindFieldOffset("button4");
1551 prog->fieldoffsets.button5 = PRVM_ED_FindFieldOffset("button5");
1552 prog->fieldoffsets.button6 = PRVM_ED_FindFieldOffset("button6");
1553 prog->fieldoffsets.button7 = PRVM_ED_FindFieldOffset("button7");
1554 prog->fieldoffsets.button8 = PRVM_ED_FindFieldOffset("button8");
1555 prog->fieldoffsets.button9 = PRVM_ED_FindFieldOffset("button9");
1556 prog->fieldoffsets.button10 = PRVM_ED_FindFieldOffset("button10");
1557 prog->fieldoffsets.button11 = PRVM_ED_FindFieldOffset("button11");
1558 prog->fieldoffsets.button12 = PRVM_ED_FindFieldOffset("button12");
1559 prog->fieldoffsets.button13 = PRVM_ED_FindFieldOffset("button13");
1560 prog->fieldoffsets.button14 = PRVM_ED_FindFieldOffset("button14");
1561 prog->fieldoffsets.button15 = PRVM_ED_FindFieldOffset("button15");
1562 prog->fieldoffsets.button16 = PRVM_ED_FindFieldOffset("button16");
1563 prog->fieldoffsets.buttonchat = PRVM_ED_FindFieldOffset("buttonchat");
1564 prog->fieldoffsets.buttonuse = PRVM_ED_FindFieldOffset("buttonuse");
1565 prog->fieldoffsets.chain = PRVM_ED_FindFieldOffset("chain");
1566 prog->fieldoffsets.classname = PRVM_ED_FindFieldOffset("classname");
1567 prog->fieldoffsets.clientcamera = PRVM_ED_FindFieldOffset("clientcamera");
1568 prog->fieldoffsets.clientcolors = PRVM_ED_FindFieldOffset("clientcolors");
1569 prog->fieldoffsets.clientstatus = PRVM_ED_FindFieldOffset("clientstatus");
1570 prog->fieldoffsets.color = PRVM_ED_FindFieldOffset("color");
1571 prog->fieldoffsets.colormod = PRVM_ED_FindFieldOffset("colormod");
1572 prog->fieldoffsets.contentstransition = PRVM_ED_FindFieldOffset("contentstransition");
1573 prog->fieldoffsets.cursor_active = PRVM_ED_FindFieldOffset("cursor_active");
1574 prog->fieldoffsets.cursor_screen = PRVM_ED_FindFieldOffset("cursor_screen");
1575 prog->fieldoffsets.cursor_trace_endpos = PRVM_ED_FindFieldOffset("cursor_trace_endpos");
1576 prog->fieldoffsets.cursor_trace_ent = PRVM_ED_FindFieldOffset("cursor_trace_ent");
1577 prog->fieldoffsets.cursor_trace_start = PRVM_ED_FindFieldOffset("cursor_trace_start");
1578 prog->fieldoffsets.customizeentityforclient = PRVM_ED_FindFieldOffset("customizeentityforclient");
1579 prog->fieldoffsets.dimension_hit = PRVM_ED_FindFieldOffset("dimension_hit");
1580 prog->fieldoffsets.dimension_solid = PRVM_ED_FindFieldOffset("dimension_solid");
1581 prog->fieldoffsets.disableclientprediction = PRVM_ED_FindFieldOffset("disableclientprediction");
1582 prog->fieldoffsets.dphitcontentsmask = PRVM_ED_FindFieldOffset("dphitcontentsmask");
1583 prog->fieldoffsets.drawonlytoclient = PRVM_ED_FindFieldOffset("drawonlytoclient");
1584 prog->fieldoffsets.exteriormodeltoclient = PRVM_ED_FindFieldOffset("exteriormodeltoclient");
1585 prog->fieldoffsets.fatness = PRVM_ED_FindFieldOffset("fatness");
1586 prog->fieldoffsets.forceshader = PRVM_ED_FindFieldOffset("forceshader");
1587 prog->fieldoffsets.frame = PRVM_ED_FindFieldOffset("frame");
1588 prog->fieldoffsets.frame1time = PRVM_ED_FindFieldOffset("frame1time");
1589 prog->fieldoffsets.frame2 = PRVM_ED_FindFieldOffset("frame2");
1590 prog->fieldoffsets.frame2time = PRVM_ED_FindFieldOffset("frame2time");
1591 prog->fieldoffsets.frame3 = PRVM_ED_FindFieldOffset("frame3");
1592 prog->fieldoffsets.frame3time = PRVM_ED_FindFieldOffset("frame3time");
1593 prog->fieldoffsets.frame4 = PRVM_ED_FindFieldOffset("frame4");
1594 prog->fieldoffsets.frame4time = PRVM_ED_FindFieldOffset("frame4time");
1595 prog->fieldoffsets.fullbright = PRVM_ED_FindFieldOffset("fullbright");
1596 prog->fieldoffsets.glow_color = PRVM_ED_FindFieldOffset("glow_color");
1597 prog->fieldoffsets.glow_size = PRVM_ED_FindFieldOffset("glow_size");
1598 prog->fieldoffsets.glow_trail = PRVM_ED_FindFieldOffset("glow_trail");
1599 prog->fieldoffsets.glowmod = PRVM_ED_FindFieldOffset("glowmod");
1600 prog->fieldoffsets.gravity = PRVM_ED_FindFieldOffset("gravity");
1601 prog->fieldoffsets.groundentity = PRVM_ED_FindFieldOffset("groundentity");
1602 prog->fieldoffsets.hull = PRVM_ED_FindFieldOffset("hull");
1603 prog->fieldoffsets.ideal_yaw = PRVM_ED_FindFieldOffset("ideal_yaw");
1604 prog->fieldoffsets.idealpitch = PRVM_ED_FindFieldOffset("idealpitch");
1605 prog->fieldoffsets.items2 = PRVM_ED_FindFieldOffset("items2");
1606 prog->fieldoffsets.lerpfrac = PRVM_ED_FindFieldOffset("lerpfrac");
1607 prog->fieldoffsets.lerpfrac3 = PRVM_ED_FindFieldOffset("lerpfrac3");
1608 prog->fieldoffsets.lerpfrac4 = PRVM_ED_FindFieldOffset("lerpfrac4");
1609 prog->fieldoffsets.light_lev = PRVM_ED_FindFieldOffset("light_lev");
1610 prog->fieldoffsets.message = PRVM_ED_FindFieldOffset("message");
1611 prog->fieldoffsets.modelflags = PRVM_ED_FindFieldOffset("modelflags");
1612 prog->fieldoffsets.movement = PRVM_ED_FindFieldOffset("movement");
1613 prog->fieldoffsets.movetypesteplandevent = PRVM_ED_FindFieldOffset("movetypesteplandevent");
1614 prog->fieldoffsets.netaddress = PRVM_ED_FindFieldOffset("netaddress");
1615 prog->fieldoffsets.nextthink = PRVM_ED_FindFieldOffset("nextthink");
1616 prog->fieldoffsets.nodrawtoclient = PRVM_ED_FindFieldOffset("nodrawtoclient");
1617 prog->fieldoffsets.pflags = PRVM_ED_FindFieldOffset("pflags");
1618 prog->fieldoffsets.ping = PRVM_ED_FindFieldOffset("ping");
1619 prog->fieldoffsets.packetloss = PRVM_ED_FindFieldOffset("ping_packetloss");
1620 prog->fieldoffsets.movementloss = PRVM_ED_FindFieldOffset("ping_movementloss");
1621 prog->fieldoffsets.pitch_speed = PRVM_ED_FindFieldOffset("pitch_speed");
1622 prog->fieldoffsets.playermodel = PRVM_ED_FindFieldOffset("playermodel");
1623 prog->fieldoffsets.playerskin = PRVM_ED_FindFieldOffset("playerskin");
1624 prog->fieldoffsets.pmodel = PRVM_ED_FindFieldOffset("pmodel");
1625 prog->fieldoffsets.punchvector = PRVM_ED_FindFieldOffset("punchvector");
1626 prog->fieldoffsets.renderamt = PRVM_ED_FindFieldOffset("renderamt"); // HalfLife support
1627 prog->fieldoffsets.renderflags = PRVM_ED_FindFieldOffset("renderflags");
1628 prog->fieldoffsets.rendermode = PRVM_ED_FindFieldOffset("rendermode"); // HalfLife support
1629 prog->fieldoffsets.scale = PRVM_ED_FindFieldOffset("scale");
1630 prog->fieldoffsets.shadertime = PRVM_ED_FindFieldOffset("shadertime");
1631 prog->fieldoffsets.skeletonindex = PRVM_ED_FindFieldOffset("skeletonindex");
1632 prog->fieldoffsets.style = PRVM_ED_FindFieldOffset("style");
1633 prog->fieldoffsets.tag_entity = PRVM_ED_FindFieldOffset("tag_entity");
1634 prog->fieldoffsets.tag_index = PRVM_ED_FindFieldOffset("tag_index");
1635 prog->fieldoffsets.think = PRVM_ED_FindFieldOffset("think");
1636 prog->fieldoffsets.viewmodelforclient = PRVM_ED_FindFieldOffset("viewmodelforclient");
1637 prog->fieldoffsets.viewzoom = PRVM_ED_FindFieldOffset("viewzoom");
1638 prog->fieldoffsets.yaw_speed = PRVM_ED_FindFieldOffset("yaw_speed");
1639 prog->fieldoffsets.bouncefactor = PRVM_ED_FindFieldOffset("bouncefactor");
1640 prog->fieldoffsets.bouncestop = PRVM_ED_FindFieldOffset("bouncestop");
1642 prog->fieldoffsets.solid = PRVM_ED_FindFieldOffset("solid");
1643 prog->fieldoffsets.movetype = PRVM_ED_FindFieldOffset("movetype");
1644 prog->fieldoffsets.modelindex = PRVM_ED_FindFieldOffset("modelindex");
1645 prog->fieldoffsets.mins = PRVM_ED_FindFieldOffset("mins");
1646 prog->fieldoffsets.maxs = PRVM_ED_FindFieldOffset("maxs");
1647 prog->fieldoffsets.mass = PRVM_ED_FindFieldOffset("mass");
1648 prog->fieldoffsets.origin = PRVM_ED_FindFieldOffset("origin");
1649 prog->fieldoffsets.velocity = PRVM_ED_FindFieldOffset("velocity");
1650 //prog->fieldoffsets.axis_forward = PRVM_ED_FindFieldOffset("axis_forward");
1651 //prog->fieldoffsets.axis_left = PRVM_ED_FindFieldOffset("axis_left");
1652 //prog->fieldoffsets.axis_up = PRVM_ED_FindFieldOffset("axis_up");
1653 //prog->fieldoffsets.spinvelocity = PRVM_ED_FindFieldOffset("spinvelocity");
1654 prog->fieldoffsets.angles = PRVM_ED_FindFieldOffset("angles");
1655 prog->fieldoffsets.avelocity = PRVM_ED_FindFieldOffset("avelocity");
1656 prog->fieldoffsets.aiment = PRVM_ED_FindFieldOffset("aiment");
1657 prog->fieldoffsets.enemy = PRVM_ED_FindFieldOffset("enemy");
1658 prog->fieldoffsets.jointtype = PRVM_ED_FindFieldOffset("jointtype");
1659 prog->fieldoffsets.movedir = PRVM_ED_FindFieldOffset("movedir");
1661 prog->funcoffsets.CSQC_ConsoleCommand = PRVM_ED_FindFunctionOffset("CSQC_ConsoleCommand");
1662 prog->funcoffsets.CSQC_Ent_Remove = PRVM_ED_FindFunctionOffset("CSQC_Ent_Remove");
1663 prog->funcoffsets.CSQC_Ent_Spawn = PRVM_ED_FindFunctionOffset("CSQC_Ent_Spawn");
1664 prog->funcoffsets.CSQC_Ent_Update = PRVM_ED_FindFunctionOffset("CSQC_Ent_Update");
1665 prog->funcoffsets.CSQC_Event = PRVM_ED_FindFunctionOffset("CSQC_Event");
1666 prog->funcoffsets.CSQC_Event_Sound = PRVM_ED_FindFunctionOffset("CSQC_Event_Sound");
1667 prog->funcoffsets.CSQC_Init = PRVM_ED_FindFunctionOffset("CSQC_Init");
1668 prog->funcoffsets.CSQC_InputEvent = PRVM_ED_FindFunctionOffset("CSQC_InputEvent");
1669 prog->funcoffsets.CSQC_Parse_CenterPrint = PRVM_ED_FindFunctionOffset("CSQC_Parse_CenterPrint");
1670 prog->funcoffsets.CSQC_Parse_Print = PRVM_ED_FindFunctionOffset("CSQC_Parse_Print");
1671 prog->funcoffsets.CSQC_Parse_StuffCmd = PRVM_ED_FindFunctionOffset("CSQC_Parse_StuffCmd");
1672 prog->funcoffsets.CSQC_Parse_TempEntity = PRVM_ED_FindFunctionOffset("CSQC_Parse_TempEntity");
1673 prog->funcoffsets.CSQC_Shutdown = PRVM_ED_FindFunctionOffset("CSQC_Shutdown");
1674 prog->funcoffsets.CSQC_UpdateView = PRVM_ED_FindFunctionOffset("CSQC_UpdateView");
1675 prog->funcoffsets.EndFrame = PRVM_ED_FindFunctionOffset("EndFrame");
1676 prog->funcoffsets.GameCommand = PRVM_ED_FindFunctionOffset("GameCommand");
1677 prog->funcoffsets.Gecko_Query = PRVM_ED_FindFunctionOffset("Gecko_Query");
1678 prog->funcoffsets.RestoreGame = PRVM_ED_FindFunctionOffset("RestoreGame");
1679 prog->funcoffsets.SV_ChangeTeam = PRVM_ED_FindFunctionOffset("SV_ChangeTeam");
1680 prog->funcoffsets.SV_OnEntityNoSpawnFunction = PRVM_ED_FindFunctionOffset("SV_OnEntityNoSpawnFunction");
1681 prog->funcoffsets.SV_OnEntityPostSpawnFunction = PRVM_ED_FindFunctionOffset("SV_OnEntityPostSpawnFunction");
1682 prog->funcoffsets.SV_OnEntityPreSpawnFunction = PRVM_ED_FindFunctionOffset("SV_OnEntityPreSpawnFunction");
1683 prog->funcoffsets.SV_ParseClientCommand = PRVM_ED_FindFunctionOffset("SV_ParseClientCommand");
1684 prog->funcoffsets.SV_PausedTic = PRVM_ED_FindFunctionOffset("SV_PausedTic");
1685 prog->funcoffsets.SV_PlayerPhysics = PRVM_ED_FindFunctionOffset("SV_PlayerPhysics");
1686 prog->funcoffsets.SV_Shutdown = PRVM_ED_FindFunctionOffset("SV_Shutdown");
1687 prog->funcoffsets.URI_Get_Callback = PRVM_ED_FindFunctionOffset("URI_Get_Callback");
1688 prog->globaloffsets.SV_InitCmd = PRVM_ED_FindGlobalOffset("SV_InitCmd");
1689 prog->globaloffsets.coop = PRVM_ED_FindGlobalOffset("coop");
1690 prog->globaloffsets.deathmatch = PRVM_ED_FindGlobalOffset("deathmatch");
1691 prog->globaloffsets.dmg_origin = PRVM_ED_FindGlobalOffset("dmg_origin");
1692 prog->globaloffsets.dmg_save = PRVM_ED_FindGlobalOffset("dmg_save");
1693 prog->globaloffsets.dmg_take = PRVM_ED_FindGlobalOffset("dmg_take");
1694 prog->globaloffsets.drawfont = PRVM_ED_FindGlobalOffset("drawfont");
1695 prog->globaloffsets.drawfontscale = PRVM_ED_FindGlobalOffset("drawfontscale");
1696 prog->globaloffsets.gettaginfo_forward = PRVM_ED_FindGlobalOffset("gettaginfo_forward");
1697 prog->globaloffsets.gettaginfo_name = PRVM_ED_FindGlobalOffset("gettaginfo_name");
1698 prog->globaloffsets.gettaginfo_offset = PRVM_ED_FindGlobalOffset("gettaginfo_offset");
1699 prog->globaloffsets.gettaginfo_parent = PRVM_ED_FindGlobalOffset("gettaginfo_parent");
1700 prog->globaloffsets.gettaginfo_right = PRVM_ED_FindGlobalOffset("gettaginfo_right");
1701 prog->globaloffsets.gettaginfo_up = PRVM_ED_FindGlobalOffset("gettaginfo_up");
1702 prog->globaloffsets.transparent_offset = PRVM_ED_FindGlobalOffset("transparent_offset");
1703 prog->globaloffsets.intermission = PRVM_ED_FindGlobalOffset("intermission");
1704 prog->globaloffsets.require_spawnfunc_prefix = PRVM_ED_FindGlobalOffset("require_spawnfunc_prefix");
1705 prog->globaloffsets.sb_showscores = PRVM_ED_FindGlobalOffset("sb_showscores");
1706 prog->globaloffsets.self = PRVM_ED_FindGlobalOffset("self");
1707 prog->globaloffsets.serverdeltatime = PRVM_ED_FindGlobalOffset("serverdeltatime");
1708 prog->globaloffsets.serverprevtime = PRVM_ED_FindGlobalOffset("serverprevtime");
1709 prog->globaloffsets.servertime = PRVM_ED_FindGlobalOffset("servertime");
1710 prog->globaloffsets.time = PRVM_ED_FindGlobalOffset("time");
1711 prog->globaloffsets.trace_allsolid = PRVM_ED_FindGlobalOffset("trace_allsolid");
1712 prog->globaloffsets.trace_dphitcontents = PRVM_ED_FindGlobalOffset("trace_dphitcontents");
1713 prog->globaloffsets.trace_dphitq3surfaceflags = PRVM_ED_FindGlobalOffset("trace_dphitq3surfaceflags");
1714 prog->globaloffsets.trace_dphittexturename = PRVM_ED_FindGlobalOffset("trace_dphittexturename");
1715 prog->globaloffsets.trace_dpstartcontents = PRVM_ED_FindGlobalOffset("trace_dpstartcontents");
1716 prog->globaloffsets.trace_endpos = PRVM_ED_FindGlobalOffset("trace_endpos");
1717 prog->globaloffsets.trace_ent = PRVM_ED_FindGlobalOffset("trace_ent");
1718 prog->globaloffsets.trace_fraction = PRVM_ED_FindGlobalOffset("trace_fraction");
1719 prog->globaloffsets.trace_inopen = PRVM_ED_FindGlobalOffset("trace_inopen");
1720 prog->globaloffsets.trace_inwater = PRVM_ED_FindGlobalOffset("trace_inwater");
1721 prog->globaloffsets.trace_networkentity = PRVM_ED_FindGlobalOffset("trace_networkentity");
1722 prog->globaloffsets.trace_plane_dist = PRVM_ED_FindGlobalOffset("trace_plane_dist");
1723 prog->globaloffsets.trace_plane_normal = PRVM_ED_FindGlobalOffset("trace_plane_normal");
1724 prog->globaloffsets.trace_startsolid = PRVM_ED_FindGlobalOffset("trace_startsolid");
1725 prog->globaloffsets.v_forward = PRVM_ED_FindGlobalOffset("v_forward");
1726 prog->globaloffsets.v_right = PRVM_ED_FindGlobalOffset("v_right");
1727 prog->globaloffsets.v_up = PRVM_ED_FindGlobalOffset("v_up");
1728 prog->globaloffsets.view_angles = PRVM_ED_FindGlobalOffset("view_angles");
1729 prog->globaloffsets.worldstatus = PRVM_ED_FindGlobalOffset("worldstatus");
1731 // menu qc only uses some functions, nothing else
1732 prog->funcoffsets.m_draw = PRVM_ED_FindFunctionOffset("m_draw");
1733 prog->funcoffsets.m_init = PRVM_ED_FindFunctionOffset("m_init");
1734 prog->funcoffsets.m_keydown = PRVM_ED_FindFunctionOffset("m_keydown");
1735 prog->funcoffsets.m_keyup = PRVM_ED_FindFunctionOffset("m_keyup");
1736 prog->funcoffsets.m_shutdown = PRVM_ED_FindFunctionOffset("m_shutdown");
1737 prog->funcoffsets.m_toggle = PRVM_ED_FindFunctionOffset("m_toggle");
1742 typedef struct dpfield_s
1749 #define DPFIELDS (sizeof(dpfields) / sizeof(dpfield_t))
1751 dpfield_t dpfields[] =
1762 #define PO_HASHSIZE 16384
1763 typedef struct po_string_s
1766 struct po_string_s *nextonhashchain;
1771 po_string_t *hashtable[PO_HASHSIZE];
1774 void PRVM_PO_UnparseString(char *out, const char *in, size_t outsize)
1783 case '\a': if(outsize >= 2) { *out++ = '\\'; *out++ = 'a'; outsize -= 2; } break;
1784 case '\b': if(outsize >= 2) { *out++ = '\\'; *out++ = 'b'; outsize -= 2; } break;
1785 case '\t': if(outsize >= 2) { *out++ = '\\'; *out++ = 't'; outsize -= 2; } break;
1786 case '\r': if(outsize >= 2) { *out++ = '\\'; *out++ = 'r'; outsize -= 2; } break;
1787 case '\n': if(outsize >= 2) { *out++ = '\\'; *out++ = 'n'; outsize -= 2; } break;
1788 case '\\': if(outsize >= 2) { *out++ = '\\'; *out++ = '\\'; outsize -= 2; } break;
1789 case '"': if(outsize >= 2) { *out++ = '\\'; *out++ = '"'; outsize -= 2; } break;
1791 if(*in >= 0 && *in <= 0x1F)
1796 *out++ = '0' + ((*in & 0700) >> 6);
1797 *out++ = '0' + ((*in & 0070) >> 3);
1798 *out++ = '0' + ((*in & 0007));
1815 void PRVM_PO_ParseString(char *out, const char *in, size_t outsize)
1828 case 'a': if(outsize > 0) { *out++ = '\a'; --outsize; } break;
1829 case 'b': if(outsize > 0) { *out++ = '\b'; --outsize; } break;
1830 case 't': if(outsize > 0) { *out++ = '\t'; --outsize; } break;
1831 case 'r': if(outsize > 0) { *out++ = '\r'; --outsize; } break;
1832 case 'n': if(outsize > 0) { *out++ = '\n'; --outsize; } break;
1833 case '\\': if(outsize > 0) { *out++ = '\\'; --outsize; } break;
1834 case '"': if(outsize > 0) { *out++ = '"'; --outsize; } break;
1835 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
1839 if(*in >= '0' && *in <= '7')
1842 *out = (*out << 3) | (*in - '0');
1845 if(*in >= '0' && *in <= '7')
1848 *out = (*out << 3) | (*in - '0');
1859 if(outsize > 0) { *out++ = *in; --outsize; }
1874 po_t *PRVM_PO_Load(const char *filename, mempool_t *pool)
1879 char inbuf[MAX_INPUTLINE];
1880 char decodedbuf[MAX_INPUTLINE];
1883 po_string_t thisstr;
1884 const char *buf = (const char *) FS_LoadFile(filename, pool, true, NULL);
1889 po = Mem_Alloc(pool, sizeof(*po));
1890 memset(po, 0, sizeof(*po));
1898 p = strchr(p, '\n');
1904 if(*p == '\r' || *p == '\n')
1909 if(!strncmp(p, "msgid \"", 7))
1914 else if(!strncmp(p, "msgstr \"", 8))
1921 p = strchr(p, '\n');
1931 q = strchr(p, '\n');
1938 if((size_t)(q - p) >= (size_t) sizeof(inbuf))
1940 strlcpy(inbuf, p, q - p); // not - 1, because this adds a NUL
1941 PRVM_PO_ParseString(decodedbuf + decodedpos, inbuf, sizeof(decodedbuf) - decodedpos);
1942 decodedpos += strlen(decodedbuf + decodedpos);
1952 Mem_Free(thisstr.key);
1953 thisstr.key = Mem_Alloc(pool, decodedpos + 1);
1954 memcpy(thisstr.key, decodedbuf, decodedpos + 1);
1956 else if(decodedpos > 0 && thisstr.key) // skip empty translation results
1958 thisstr.value = Mem_Alloc(pool, decodedpos + 1);
1959 memcpy(thisstr.value, decodedbuf, decodedpos + 1);
1960 hashindex = CRC_Block((const unsigned char *) thisstr.key, strlen(thisstr.key)) % PO_HASHSIZE;
1961 thisstr.nextonhashchain = po->hashtable[hashindex];
1962 po->hashtable[hashindex] = Mem_Alloc(pool, sizeof(thisstr));
1963 memcpy(po->hashtable[hashindex], &thisstr, sizeof(thisstr));
1964 memset(&thisstr, 0, sizeof(thisstr));
1968 Mem_Free((char *) buf);
1971 const char *PRVM_PO_Lookup(po_t *po, const char *str)
1973 int hashindex = CRC_Block((const unsigned char *) str, strlen(str)) % PO_HASHSIZE;
1974 po_string_t *p = po->hashtable[hashindex];
1977 if(!strcmp(str, p->key))
1979 p = p->nextonhashchain;
1983 void PRVM_PO_Destroy(po_t *po)
1986 for(i = 0; i < PO_HASHSIZE; ++i)
1988 po_string_t *p = po->hashtable[i];
1992 p = p->nextonhashchain;
2001 void PRVM_LeakTest(void);
2002 void PRVM_ResetProg(void)
2005 PRVM_GCALL(reset_cmd)();
2006 Mem_FreePool(&prog->progs_mempool);
2008 PRVM_PO_Destroy((po_t *) prog->po);
2009 memset(prog,0,sizeof(prvm_prog_t));
2010 prog->starttime = Sys_DoubleTime();
2018 void PRVM_LoadLNO( const char *progname ) {
2019 fs_offset_t filesize;
2021 unsigned int *header;
2024 FS_StripExtension( progname, filename, sizeof( filename ) );
2025 strlcat( filename, ".lno", sizeof( filename ) );
2027 lno = FS_LoadFile( filename, tempmempool, false, &filesize );
2033 <Spike> SafeWrite (h, &lnotype, sizeof(int));
2034 <Spike> SafeWrite (h, &version, sizeof(int));
2035 <Spike> SafeWrite (h, &numglobaldefs, sizeof(int));
2036 <Spike> SafeWrite (h, &numpr_globals, sizeof(int));
2037 <Spike> SafeWrite (h, &numfielddefs, sizeof(int));
2038 <Spike> SafeWrite (h, &numstatements, sizeof(int));
2039 <Spike> SafeWrite (h, statement_linenums, numstatements*sizeof(int));
2041 if( (unsigned) filesize < (6 + prog->progs->numstatements) * sizeof( int ) ) {
2046 header = (unsigned int *) lno;
2047 if( header[ 0 ] == *(unsigned int *) "LNOF" &&
2048 LittleLong( header[ 1 ] ) == 1 &&
2049 (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs->numglobaldefs &&
2050 (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs->numglobals &&
2051 (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs->numfielddefs &&
2052 (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs->numstatements )
2054 prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs->numstatements * sizeof( int ) );
2055 memcpy( prog->statement_linenums, (int *) lno + 6, prog->progs->numstatements * sizeof( int ) );
2065 void PRVM_LoadProgs (const char * filename, int numrequiredfunc, char **required_func, int numrequiredfields, prvm_required_field_t *required_field, int numrequiredglobals, char **required_global)
2069 ddef_t *infielddefs;
2070 dfunction_t *dfunctions;
2071 fs_offset_t filesize;
2073 if( prog->loaded ) {
2074 PRVM_ERROR ("PRVM_LoadProgs: there is already a %s program loaded!", PRVM_NAME );
2077 prog->progs = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
2078 if (prog->progs == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
2079 PRVM_ERROR ("PRVM_LoadProgs: couldn't load %s for %s", filename, PRVM_NAME);
2080 // TODO bounds check header fields (e.g. numstatements), they must never go behind end of file
2082 Con_DPrintf("%s programs occupy %iK.\n", PRVM_NAME, (int)(filesize/1024));
2084 prog->filecrc = CRC_Block((unsigned char *)prog->progs, filesize);
2086 // byte swap the header
2087 for (i = 0;i < (int) sizeof(*prog->progs) / 4;i++)
2088 ((int *)prog->progs)[i] = LittleLong ( ((int *)prog->progs)[i] );
2090 if (prog->progs->version != PROG_VERSION)
2091 PRVM_ERROR ("%s: %s has wrong version number (%i should be %i)", PRVM_NAME, filename, prog->progs->version, PROG_VERSION);
2092 if (prog->progs->crc != prog->headercrc && prog->progs->crc != prog->headercrc2)
2093 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);
2095 //prog->functions = (dfunction_t *)((unsigned char *)progs + progs->ofs_functions);
2096 dfunctions = (dfunction_t *)((unsigned char *)prog->progs + prog->progs->ofs_functions);
2098 if (prog->progs->ofs_strings + prog->progs->numstrings >= (int)filesize)
2099 PRVM_ERROR ("%s: %s strings go past end of file", PRVM_NAME, filename);
2100 prog->strings = (char *)prog->progs + prog->progs->ofs_strings;
2101 prog->stringssize = prog->progs->numstrings;
2103 prog->numknownstrings = 0;
2104 prog->maxknownstrings = 0;
2105 prog->knownstrings = NULL;
2106 prog->knownstrings_freeable = NULL;
2108 Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
2110 prog->globaldefs = (ddef_t *)((unsigned char *)prog->progs + prog->progs->ofs_globaldefs);
2112 // we need to expand the fielddefs list to include all the engine fields,
2113 // so allocate a new place for it
2114 infielddefs = (ddef_t *)((unsigned char *)prog->progs + prog->progs->ofs_fielddefs);
2116 prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs->numfielddefs + numrequiredfields) * sizeof(ddef_t));
2118 prog->statements = (dstatement_t *)((unsigned char *)prog->progs + prog->progs->ofs_statements);
2120 prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs->numstatements * sizeof(*prog->statement_profile));
2122 //pr_global_struct = (globalvars_t *)((unsigned char *)progs + progs->ofs_globals);
2123 prog->globals.generic = (float *)((unsigned char *)prog->progs + prog->progs->ofs_globals);
2125 // byte swap the lumps
2126 for (i=0 ; i<prog->progs->numstatements ; i++)
2128 prog->statements[i].op = LittleShort(prog->statements[i].op);
2129 prog->statements[i].a = LittleShort(prog->statements[i].a);
2130 prog->statements[i].b = LittleShort(prog->statements[i].b);
2131 prog->statements[i].c = LittleShort(prog->statements[i].c);
2134 prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs->numfunctions);
2135 for (i = 0;i < prog->progs->numfunctions;i++)
2137 prog->functions[i].first_statement = LittleLong (dfunctions[i].first_statement);
2138 prog->functions[i].parm_start = LittleLong (dfunctions[i].parm_start);
2139 prog->functions[i].s_name = LittleLong (dfunctions[i].s_name);
2140 prog->functions[i].s_file = LittleLong (dfunctions[i].s_file);
2141 prog->functions[i].numparms = LittleLong (dfunctions[i].numparms);
2142 prog->functions[i].locals = LittleLong (dfunctions[i].locals);
2143 memcpy(prog->functions[i].parm_size, dfunctions[i].parm_size, sizeof(dfunctions[i].parm_size));
2144 if(prog->functions[i].first_statement >= prog->progs->numstatements)
2145 PRVM_ERROR("PRVM_LoadProgs: out of bounds function statement (function %d) in %s", i, PRVM_NAME);
2146 // TODO bounds check parm_start, s_name, s_file, numparms, locals, parm_size
2149 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
2151 prog->globaldefs[i].type = LittleShort (prog->globaldefs[i].type);
2152 prog->globaldefs[i].ofs = LittleShort (prog->globaldefs[i].ofs);
2153 prog->globaldefs[i].s_name = LittleLong (prog->globaldefs[i].s_name);
2154 // TODO bounds check ofs, s_name
2157 // copy the progs fields to the new fields list
2158 for (i = 0;i < prog->progs->numfielddefs;i++)
2160 prog->fielddefs[i].type = LittleShort (infielddefs[i].type);
2161 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
2162 PRVM_ERROR ("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", PRVM_NAME);
2163 prog->fielddefs[i].ofs = LittleShort (infielddefs[i].ofs);
2164 prog->fielddefs[i].s_name = LittleLong (infielddefs[i].s_name);
2165 // TODO bounds check ofs, s_name
2168 // append the required fields
2169 for (i = 0;i < (int) numrequiredfields;i++)
2171 prog->fielddefs[prog->progs->numfielddefs].type = required_field[i].type;
2172 prog->fielddefs[prog->progs->numfielddefs].ofs = prog->progs->entityfields;
2173 prog->fielddefs[prog->progs->numfielddefs].s_name = PRVM_SetEngineString(required_field[i].name);
2174 // TODO bounds check ofs, s_name
2175 if (prog->fielddefs[prog->progs->numfielddefs].type == ev_vector)
2176 prog->progs->entityfields += 3;
2178 prog->progs->entityfields++;
2179 prog->progs->numfielddefs++;
2181 prog->entityfields = prog->progs->entityfields;
2183 // check required functions
2184 for(i=0 ; i < numrequiredfunc ; i++)
2185 if(PRVM_ED_FindFunction(required_func[i]) == 0)
2186 PRVM_ERROR("%s: %s not found in %s",PRVM_NAME, required_func[i], filename);
2188 // check required globals
2189 for(i=0 ; i < numrequiredglobals ; i++)
2190 if(PRVM_ED_FindGlobal(required_global[i]) == 0)
2191 PRVM_ERROR("%s: %s not found in %s",PRVM_NAME, required_global[i], filename);
2193 for (i=0 ; i<prog->progs->numglobals ; i++)
2194 ((int *)prog->globals.generic)[i] = LittleLong (((int *)prog->globals.generic)[i]);
2196 // LordHavoc: bounds check anything static
2197 for (i = 0,st = prog->statements;i < prog->progs->numstatements;i++,st++)
2203 if ((unsigned short) st->a >= prog->progs->numglobals || st->b + i < 0 || st->b + i >= prog->progs->numstatements)
2204 PRVM_ERROR("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", i, PRVM_NAME);
2207 if (st->a + i < 0 || st->a + i >= prog->progs->numstatements)
2208 PRVM_ERROR("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, PRVM_NAME);
2210 // global global global
2245 if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->b >= prog->progs->numglobals || (unsigned short) st->c >= prog->progs->numglobals)
2246 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
2248 // global none global
2254 if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->c >= prog->progs->numglobals)
2255 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
2271 if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->b >= prog->progs->numglobals)
2272 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
2286 if ((unsigned short) st->a >= prog->progs->numglobals)
2287 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
2290 Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", st->op, i, PRVM_NAME);
2294 if(prog->progs->numstatements < 1)
2296 PRVM_ERROR("PRVM_LoadProgs: empty program in %s", PRVM_NAME);
2298 else switch(prog->statements[prog->progs->numstatements - 1].op)
2305 PRVM_ERROR("PRVM_LoadProgs: program may fall off the edge (does not end with RETURN, GOTO or DONE) in %s", PRVM_NAME);
2309 PRVM_LoadLNO(filename);
2313 if(*prvm_language.string)
2314 // in CSQC we really shouldn't be able to change how stuff works... sorry for now
2315 // later idea: include a list of authorized .po file checksums with the csprogs
2317 qboolean deftrans = !!strcmp(PRVM_NAME, "client");
2318 if(!strcmp(prvm_language.string, "dump"))
2320 qfile_t *f = FS_OpenRealFile(va("%s.%s.po", filename, prvm_language.string), "w", false);
2321 Con_Printf("Dumping to %s.%s.po\n", filename, prvm_language.string);
2324 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
2327 name = PRVM_GetString(prog->globaldefs[i].s_name);
2328 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2329 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2331 prvm_eval_t *val = (prvm_eval_t *)(prog->globals.generic + prog->globaldefs[i].ofs);
2332 const char *value = PRVM_GetString(val->string);
2335 char buf[MAX_INPUTLINE];
2336 PRVM_PO_UnparseString(buf, value, sizeof(buf));
2337 FS_Printf(f, "msgid \"%s\"\nmsgstr \"\"\n\n", buf);
2346 po_t *po = PRVM_PO_Load(va("%s.%s.po", filename, prvm_language.string), prog->progs_mempool);
2349 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
2352 name = PRVM_GetString(prog->globaldefs[i].s_name);
2353 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2354 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2356 prvm_eval_t *val = (prvm_eval_t *)(prog->globals.generic + prog->globaldefs[i].ofs);
2357 const char *value = PRVM_GetString(val->string);
2360 value = PRVM_PO_Lookup(po, value);
2362 val->string = PRVM_SetEngineString(value);
2370 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
2373 name = PRVM_GetString(prog->globaldefs[i].s_name);
2374 //Con_Printf("found var %s\n", name);
2376 && !strncmp(name, "autocvar_", 9)
2377 && !(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
2380 prvm_eval_t *val = (prvm_eval_t *)(prog->globals.generic + prog->globaldefs[i].ofs);
2381 cvar_t *cvar = Cvar_FindVar(name + 9);
2382 //Con_Printf("PRVM_LoadProgs: autocvar global %s in %s, processing...\n", name, PRVM_NAME);
2387 Con_Printf("PRVM_LoadProgs: no cvar for autocvar global %s in %s, creating...\n", name, PRVM_NAME);
2388 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2391 if((float)((int)(val->_float)) == val->_float)
2392 dpsnprintf(buf, sizeof(buf), "%i", (int)(val->_float));
2394 dpsnprintf(buf, sizeof(buf), "%f", val->_float);
2398 dpsnprintf(buf, sizeof(buf), "%f %f %f", val->vector[0], val->vector[1], val->vector[2]); value = buf;
2401 value = PRVM_GetString(val->string);
2404 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, PRVM_NAME);
2407 cvar = Cvar_Get(name + 9, value, 0, NULL);
2408 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2410 val->string = PRVM_SetEngineString(cvar->string);
2411 cvar->globaldefindex_stringno[prog - prog_list] = val->string;
2414 PRVM_ERROR("PRVM_LoadProgs: could not create cvar for autocvar global %s in %s", name, PRVM_NAME);
2415 cvar->globaldefindex_progid[prog - prog_list] = prog->id;
2416 cvar->globaldefindex[prog - prog_list] = i;
2418 else if((cvar->flags & CVAR_PRIVATE) == 0)
2420 // MUST BE SYNCED WITH cvar.c Cvar_Set
2423 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2426 val->_float = cvar->value;
2430 VectorClear(val->vector);
2431 for (j = 0;j < 3;j++)
2433 while (*s && ISWHITESPACE(*s))
2437 val->vector[j] = atof(s);
2438 while (!ISWHITESPACE(*s))
2445 val->string = PRVM_SetEngineString(cvar->string);
2446 cvar->globaldefindex_stringno[prog - prog_list] = val->string;
2449 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, PRVM_NAME);
2452 cvar->globaldefindex_progid[prog - prog_list] = prog->id;
2453 cvar->globaldefindex[prog - prog_list] = i;
2456 Con_Printf("PRVM_LoadProgs: private cvar for autocvar global %s in %s\n", name, PRVM_NAME);
2462 prog->loaded = TRUE;
2464 // set flags & ddef_ts in prog
2470 PRVM_GCALL(init_cmd)();
2477 void PRVM_Fields_f (void)
2479 int i, j, ednum, used, usedamount;
2481 char tempstring[MAX_INPUTLINE], tempstring2[260];
2491 Con_Print("no progs loaded\n");
2498 Con_Print("prvm_fields <program name>\n");
2503 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
2506 counts = (int *)Mem_Alloc(tempmempool, prog->progs->numfielddefs * sizeof(int));
2507 for (ednum = 0;ednum < prog->max_edicts;ednum++)
2509 ed = PRVM_EDICT_NUM(ednum);
2510 if (ed->priv.required->free)
2512 for (i = 1;i < prog->progs->numfielddefs;i++)
2514 d = &prog->fielddefs[i];
2515 name = PRVM_GetString(d->s_name);
2516 if (name[strlen(name)-2] == '_')
2517 continue; // skip _x, _y, _z vars
2518 v = (int *)(ed->fields.vp + d->ofs);
2519 // if the value is still all 0, skip the field
2520 for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
2533 for (i = 0;i < prog->progs->numfielddefs;i++)
2535 d = &prog->fielddefs[i];
2536 name = PRVM_GetString(d->s_name);
2537 if (name[strlen(name)-2] == '_')
2538 continue; // skip _x, _y, _z vars
2539 switch(d->type & ~DEF_SAVEGLOBAL)
2542 strlcat(tempstring, "string ", sizeof(tempstring));
2545 strlcat(tempstring, "entity ", sizeof(tempstring));
2548 strlcat(tempstring, "function ", sizeof(tempstring));
2551 strlcat(tempstring, "field ", sizeof(tempstring));
2554 strlcat(tempstring, "void ", sizeof(tempstring));
2557 strlcat(tempstring, "float ", sizeof(tempstring));
2560 strlcat(tempstring, "vector ", sizeof(tempstring));
2563 strlcat(tempstring, "pointer ", sizeof(tempstring));
2566 dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
2567 strlcat(tempstring, tempstring2, sizeof(tempstring));
2570 if (strlen(name) > sizeof(tempstring2)-4)
2572 memcpy (tempstring2, name, sizeof(tempstring2)-4);
2573 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
2574 tempstring2[sizeof(tempstring2)-1] = 0;
2577 strlcat(tempstring, name, sizeof(tempstring));
2578 for (j = (int)strlen(name);j < 25;j++)
2579 strlcat(tempstring, " ", sizeof(tempstring));
2580 dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
2581 strlcat(tempstring, tempstring2, sizeof(tempstring));
2582 strlcat(tempstring, "\n", sizeof(tempstring));
2583 if (strlen(tempstring) >= sizeof(tempstring)/2)
2585 Con_Print(tempstring);
2591 usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
2595 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);
2600 void PRVM_Globals_f (void)
2603 const char *wildcard;
2609 Con_Print("no progs loaded\n");
2612 if(Cmd_Argc () < 2 || Cmd_Argc() > 3)
2614 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
2619 if(!PRVM_SetProgFromString (Cmd_Argv (1)))
2622 if( Cmd_Argc() == 3)
2623 wildcard = Cmd_Argv(2);
2627 Con_Printf("%s :", PRVM_NAME);
2629 for (i = 0;i < prog->progs->numglobaldefs;i++)
2632 if( !matchpattern( PRVM_GetString(prog->globaldefs[i].s_name), wildcard, 1) )
2637 Con_Printf("%s\n", PRVM_GetString(prog->globaldefs[i].s_name));
2639 Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->progs->numglobals, numculled, prog->progs->numglobals * 4);
2649 void PRVM_Global_f(void)
2652 if( Cmd_Argc() != 3 ) {
2653 Con_Printf( "prvm_global <program name> <global name>\n" );
2658 if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
2661 global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
2663 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2665 Con_Printf( "%s: %s\n", Cmd_Argv(2), PRVM_ValueString( (etype_t)global->type, (prvm_eval_t *) &prog->globals.generic[ global->ofs ] ) );
2674 void PRVM_GlobalSet_f(void)
2677 if( Cmd_Argc() != 4 ) {
2678 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2683 if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
2686 global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
2688 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2690 PRVM_ED_ParseEpair( NULL, global, Cmd_Argv(3), true );
2699 void PRVM_Init (void)
2701 Cmd_AddCommand ("prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2702 Cmd_AddCommand ("prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2703 Cmd_AddCommand ("prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2704 Cmd_AddCommand ("prvm_profile", PRVM_Profile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu)");
2705 Cmd_AddCommand ("prvm_childprofile", PRVM_ChildProfile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu), sorted by time taken in function with child calls");
2706 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)");
2707 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)");
2708 Cmd_AddCommand ("prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2709 Cmd_AddCommand ("prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2710 Cmd_AddCommand ("prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2711 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)");
2712 Cmd_AddCommand ("prvm_edictget", PRVM_ED_EdictGet_f, "retrieves the value of a specified property of a specified entity in the selected VM (server, client menu) into a cvar or to the console");
2713 Cmd_AddCommand ("prvm_globalget", PRVM_ED_GlobalGet_f, "retrieves the value of a specified global variable in the selected VM (server, client menu) into a cvar or to the console");
2714 Cmd_AddCommand ("prvm_printfunction", PRVM_PrintFunction_f, "prints a disassembly (QuakeC instructions) of the specified function in the selected VM (server, client, menu)");
2715 Cmd_AddCommand ("cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2716 Cmd_AddCommand ("menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2717 Cmd_AddCommand ("sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2719 // COMMANDLINEOPTION: PRVM: -noboundscheck disables the bounds checks (security hole if CSQC is in use!)
2720 prvm_boundscheck = !COM_CheckParm("-noboundscheck");
2722 Cvar_RegisterVariable (&prvm_language);
2723 Cvar_RegisterVariable (&prvm_traceqc);
2724 Cvar_RegisterVariable (&prvm_statementprofiling);
2725 Cvar_RegisterVariable (&prvm_backtraceforwarnings);
2726 Cvar_RegisterVariable (&prvm_leaktest);
2727 Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames);
2728 Cvar_RegisterVariable (&prvm_errordump);
2729 Cvar_RegisterVariable (&prvm_reuseedicts_startuptime);
2730 Cvar_RegisterVariable (&prvm_reuseedicts_neverinsameframe);
2732 // COMMANDLINEOPTION: PRVM: -norunaway disables the runaway loop check (it might be impossible to exit DarkPlaces if used!)
2733 prvm_runawaycheck = !COM_CheckParm("-norunaway");
2743 void PRVM_InitProg(int prognr)
2745 static unsigned int progid = 0;
2747 if(prognr < 0 || prognr >= PRVM_MAXPROGS)
2748 Sys_Error("PRVM_InitProg: Invalid program number %i",prognr);
2750 prog = &prog_list[prognr];
2755 memset(prog, 0, sizeof(prvm_prog_t));
2756 prog->starttime = Sys_DoubleTime();
2757 prog->id = ++progid;
2759 prog->error_cmd = Host_Error;
2760 prog->leaktest_active = prvm_leaktest.integer != 0;
2763 int PRVM_GetProgNr(void)
2765 return prog - prog_list;
2768 void *_PRVM_Alloc(size_t buffersize, const char *filename, int fileline)
2770 return _Mem_Alloc(prog->progs_mempool, NULL, buffersize, 16, filename, fileline);
2773 void _PRVM_Free(void *buffer, const char *filename, int fileline)
2775 _Mem_Free(buffer, filename, fileline);
2778 void _PRVM_FreeAll(const char *filename, int fileline)
2781 prog->fielddefs = NULL;
2782 prog->functions = NULL;
2783 _Mem_EmptyPool(prog->progs_mempool, filename, fileline);
2786 // LordHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
2787 unsigned int PRVM_EDICT_NUM_ERROR(unsigned int n, char *filename, int fileline)
2789 PRVM_ERROR ("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", PRVM_NAME, n, filename, fileline);
2793 sizebuf_t vm_tempstringsbuf;
2795 const char *PRVM_GetString(int num)
2799 if (num < prog->stringssize)
2800 return prog->strings + num;
2803 if (num <= prog->stringssize + vm_tempstringsbuf.maxsize)
2805 num -= prog->stringssize;
2806 if (num < vm_tempstringsbuf.cursize)
2807 return (char *)vm_tempstringsbuf.data + num;
2810 VM_Warning("PRVM_GetString: Invalid temp-string offset (%i >= %i vm_tempstringsbuf.cursize)\n", num, vm_tempstringsbuf.cursize);
2817 VM_Warning("PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
2827 // special range reserved for tempstrings
2829 if (num < vm_tempstringsbuf.cursize)
2830 return (char *)vm_tempstringsbuf.data + num;
2833 VM_Warning("PRVM_GetString: Invalid temp-string offset (%i >= %i vm_tempstringsbuf.cursize)\n", num, vm_tempstringsbuf.cursize);
2839 if (num < prog->numknownstrings)
2841 if (!prog->knownstrings[num])
2843 VM_Warning("PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
2846 return prog->knownstrings[num];
2850 VM_Warning("PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
2856 const char *PRVM_ChangeEngineString(int i, const char *s)
2860 if(i < 0 || i >= prog->numknownstrings)
2861 PRVM_ERROR("PRVM_ChangeEngineString: s is not an engine string");
2862 old = prog->knownstrings[i];
2863 prog->knownstrings[i] = s;
2867 int PRVM_SetEngineString(const char *s)
2872 if (s >= prog->strings && s <= prog->strings + prog->stringssize)
2873 PRVM_ERROR("PRVM_SetEngineString: s in prog->strings area");
2874 // if it's in the tempstrings area, use a reserved range
2875 // (otherwise we'd get millions of useless string offsets cluttering the database)
2876 if (s >= (char *)vm_tempstringsbuf.data && s < (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.maxsize)
2878 return prog->stringssize + (s - (char *)vm_tempstringsbuf.data);
2880 return -1 - ((1<<30) + (s - (char *)vm_tempstringsbuf.data));
2882 // see if it's a known string address
2883 for (i = 0;i < prog->numknownstrings;i++)
2884 if (prog->knownstrings[i] == s)
2886 // new unknown engine string
2887 if (developer_insane.integer)
2888 Con_DPrintf("new engine string %p = \"%s\"\n", s, s);
2889 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2890 if (!prog->knownstrings[i])
2892 if (i >= prog->numknownstrings)
2894 if (i >= prog->maxknownstrings)
2896 const char **oldstrings = prog->knownstrings;
2897 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2898 const char **oldstrings_origin = prog->knownstrings_origin;
2899 prog->maxknownstrings += 128;
2900 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2901 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2902 if(prog->leaktest_active)
2903 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2904 if (prog->numknownstrings)
2906 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2907 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2908 if(prog->leaktest_active)
2909 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
2912 prog->numknownstrings++;
2914 prog->firstfreeknownstring = i + 1;
2915 prog->knownstrings[i] = s;
2916 prog->knownstrings_freeable[i] = false;
2917 if(prog->leaktest_active)
2918 prog->knownstrings_origin[i] = NULL;
2922 // temp string handling
2924 // all tempstrings go into this buffer consecutively, and it is reset
2925 // whenever PRVM_ExecuteProgram returns to the engine
2926 // (technically each PRVM_ExecuteProgram call saves the cursize value and
2927 // restores it on return, so multiple recursive calls can share the same
2929 // the buffer size is automatically grown as needed
2931 int PRVM_SetTempString(const char *s)
2937 size = (int)strlen(s) + 1;
2938 if (developer_insane.integer)
2939 Con_DPrintf("PRVM_SetTempString: cursize %i, size %i\n", vm_tempstringsbuf.cursize, size);
2940 if (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
2942 sizebuf_t old = vm_tempstringsbuf;
2943 if (vm_tempstringsbuf.cursize + size >= 1<<28)
2944 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);
2945 vm_tempstringsbuf.maxsize = max(vm_tempstringsbuf.maxsize, 65536);
2946 while (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
2947 vm_tempstringsbuf.maxsize *= 2;
2948 if (vm_tempstringsbuf.maxsize != old.maxsize || vm_tempstringsbuf.data == NULL)
2950 Con_DPrintf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, vm_tempstringsbuf.maxsize/1024);
2951 vm_tempstringsbuf.data = (unsigned char *) Mem_Alloc(sv_mempool, vm_tempstringsbuf.maxsize);
2953 memcpy(vm_tempstringsbuf.data, old.data, old.cursize);
2958 t = (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.cursize;
2960 vm_tempstringsbuf.cursize += size;
2961 return PRVM_SetEngineString(t);
2964 int PRVM_AllocString(size_t bufferlength, char **pointer)
2969 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2970 if (!prog->knownstrings[i])
2972 if (i >= prog->numknownstrings)
2974 if (i >= prog->maxknownstrings)
2976 const char **oldstrings = prog->knownstrings;
2977 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2978 const char **oldstrings_origin = prog->knownstrings_origin;
2979 prog->maxknownstrings += 128;
2980 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2981 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2982 if(prog->leaktest_active)
2983 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2984 if (prog->numknownstrings)
2986 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2987 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2988 if(prog->leaktest_active)
2989 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
2991 // TODO why not Mem_Free the old ones?
2993 prog->numknownstrings++;
2995 prog->firstfreeknownstring = i + 1;
2996 prog->knownstrings[i] = (char *)PRVM_Alloc(bufferlength);
2997 prog->knownstrings_freeable[i] = true;
2998 if(prog->leaktest_active)
2999 prog->knownstrings_origin[i] = PRVM_AllocationOrigin();
3001 *pointer = (char *)(prog->knownstrings[i]);
3005 void PRVM_FreeString(int num)
3008 PRVM_ERROR("PRVM_FreeString: attempt to free a NULL string");
3009 else if (num >= 0 && num < prog->stringssize)
3010 PRVM_ERROR("PRVM_FreeString: attempt to free a constant string");
3011 else if (num < 0 && num >= -prog->numknownstrings)
3014 if (!prog->knownstrings[num])
3015 PRVM_ERROR("PRVM_FreeString: attempt to free a non-existent or already freed string");
3016 if (!prog->knownstrings_freeable[num])
3017 PRVM_ERROR("PRVM_FreeString: attempt to free a string owned by the engine");
3018 PRVM_Free((char *)prog->knownstrings[num]);
3019 if(prog->leaktest_active)
3020 if(prog->knownstrings_origin[num])
3021 PRVM_Free((char *)prog->knownstrings_origin[num]);
3022 prog->knownstrings[num] = NULL;
3023 prog->knownstrings_freeable[num] = false;
3024 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
3027 PRVM_ERROR("PRVM_FreeString: invalid string offset %i", num);
3030 static qboolean PRVM_IsStringReferenced(string_t string)
3034 for (i = 0;i < prog->progs->numglobaldefs;i++)
3036 ddef_t *d = &prog->globaldefs[i];
3037 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3039 if(string == ((prvm_eval_t *) &prog->globals.generic[d->ofs])->string)
3043 for(j = 0; j < prog->num_edicts; ++j)
3045 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3046 if (ed->priv.required->free)
3048 for (i=0; i<prog->progs->numfielddefs; ++i)
3050 ddef_t *d = &prog->fielddefs[i];
3051 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3053 if(string == ((prvm_eval_t *) &ed->fields.vp[d->ofs])->string)
3061 static qboolean PRVM_IsEdictRelevant(prvm_edict_t *edict)
3063 if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
3064 return true; // world or clients
3065 switch(prog - prog_list)
3067 case PRVM_SERVERPROG:
3069 entvars_t *ev = edict->fields.server;
3070 if(ev->solid) // can block other stuff, or is a trigger?
3072 if(ev->modelindex) // visible ent?
3074 if(ev->effects) // particle effect?
3076 if(ev->think) // has a think function?
3077 if(ev->nextthink > 0) // that actually will eventually run?
3081 if(*prvm_leaktest_ignore_classnames.string)
3083 if(strstr(va(" %s ", prvm_leaktest_ignore_classnames.string), va(" %s ", PRVM_GetString(ev->classname))))
3088 case PRVM_CLIENTPROG:
3090 // TODO someone add more stuff here
3091 cl_entvars_t *ev = edict->fields.client;
3092 if(ev->entnum) // csqc networked
3094 if(ev->modelindex) // visible ent?
3096 if(ev->effects) // particle effect?
3098 if(ev->think) // has a think function?
3099 if(ev->nextthink > 0) // that actually will eventually run?
3101 if(*prvm_leaktest_ignore_classnames.string)
3103 if(strstr(va(" %s ", prvm_leaktest_ignore_classnames.string), va(" %s ", PRVM_GetString(ev->classname))))
3109 // menu prog does not have classnames
3115 static qboolean PRVM_IsEdictReferenced(prvm_edict_t *edict, int mark)
3118 int edictnum = PRVM_NUM_FOR_EDICT(edict);
3119 const char *targetname = NULL;
3121 switch(prog - prog_list)
3123 case PRVM_SERVERPROG:
3124 targetname = PRVM_GetString(edict->fields.server->targetname);
3129 if(!*targetname) // ""
3132 for (i = 0;i < prog->progs->numglobaldefs;i++)
3134 ddef_t *d = &prog->globaldefs[i];
3135 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3137 if(edictnum == ((prvm_eval_t *) &prog->globals.generic[d->ofs])->edict)
3141 for(j = 0; j < prog->num_edicts; ++j)
3143 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3144 if (ed->priv.required->mark < mark)
3150 const char *target = PRVM_GetString(ed->fields.server->target);
3152 if(!strcmp(target, targetname))
3155 for (i=0; i<prog->progs->numfielddefs; ++i)
3157 ddef_t *d = &prog->fielddefs[i];
3158 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3160 if(edictnum == ((prvm_eval_t *) &ed->fields.vp[d->ofs])->edict)
3168 static void PRVM_MarkReferencedEdicts(void)
3174 for(j = 0; j < prog->num_edicts; ++j)
3176 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3177 if(ed->priv.required->free)
3179 ed->priv.required->mark = PRVM_IsEdictRelevant(ed) ? 1 : 0;
3186 for(j = 0; j < prog->num_edicts; ++j)
3188 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3189 if(ed->priv.required->free)
3191 if(ed->priv.required->mark)
3193 if(PRVM_IsEdictReferenced(ed, stage))
3195 ed->priv.required->mark = stage + 1;
3202 Con_DPrintf("leak check used %d stages to find all references\n", stage);
3205 void PRVM_LeakTest(void)
3208 qboolean leaked = false;
3210 if(!prog->leaktest_active)
3214 for (i = 0; i < prog->numknownstrings; ++i)
3216 if(prog->knownstrings[i])
3217 if(prog->knownstrings_freeable[i])
3218 if(prog->knownstrings_origin[i])
3219 if(!PRVM_IsStringReferenced(-1 - i))
3221 Con_Printf("Unreferenced string found!\n Value: %s\n Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
3227 PRVM_MarkReferencedEdicts();
3228 for(j = 0; j < prog->num_edicts; ++j)
3230 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3231 if(ed->priv.required->free)
3233 if(!ed->priv.required->mark)
3234 if(ed->priv.required->allocation_origin)
3236 Con_Printf("Unreferenced edict found!\n Allocated at: %s\n", ed->priv.required->allocation_origin);
3237 PRVM_ED_Print(ed, NULL);
3243 for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
3245 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
3247 if(stringbuffer->origin)
3249 Con_Printf("Open string buffer handle found!\n Allocated at: %s\n", stringbuffer->origin);
3254 for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
3256 if(prog->openfiles[i])
3257 if(prog->openfiles_origin[i])
3259 Con_Printf("Open file handle found!\n Allocated at: %s\n", prog->openfiles_origin[i]);
3264 for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
3266 if(prog->opensearches[i])
3267 if(prog->opensearches_origin[i])
3269 Con_Printf("Open search handle found!\n Allocated at: %s\n", prog->opensearches_origin[i]);
3275 Con_Printf("Congratulations. No leaks found.\n");