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 extern sizebuf_t vm_tempstringsbuf;
51 //============================================================================
59 void PRVM_MEM_Alloc(void)
63 // reserve space for the null entity aka world
64 // check bound of max_edicts
65 prog->max_edicts = bound(1 + prog->reserved_edicts, prog->max_edicts, prog->limit_edicts);
66 prog->num_edicts = bound(1 + prog->reserved_edicts, prog->num_edicts, prog->max_edicts);
68 // edictprivate_size has to be min as big prvm_edict_private_t
69 prog->edictprivate_size = max(prog->edictprivate_size,(int)sizeof(prvm_edict_private_t));
72 prog->edicts = (prvm_edict_t *)Mem_Alloc(prog->progs_mempool,prog->limit_edicts * sizeof(prvm_edict_t));
74 // alloc edict private space
75 prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size);
78 prog->entityfieldsarea = prog->entityfields * prog->max_edicts;
79 prog->edictsfields = (vec_t *)Mem_Alloc(prog->progs_mempool, prog->entityfieldsarea * sizeof(vec_t));
82 for(i = 0; i < prog->max_edicts; i++)
84 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
85 prog->edicts[i].fields.vp = prog->edictsfields + i * prog->entityfields;
91 PRVM_MEM_IncreaseEdicts
94 void PRVM_MEM_IncreaseEdicts(void)
98 if(prog->max_edicts >= prog->limit_edicts)
101 PRVM_GCALL(begin_increase_edicts)();
104 prog->max_edicts = min(prog->max_edicts + 256, prog->limit_edicts);
106 prog->entityfieldsarea = prog->entityfields * prog->max_edicts;
107 prog->edictsfields = (vec_t*)Mem_Realloc(prog->progs_mempool, (void *)prog->edictsfields, prog->entityfieldsarea * sizeof(vec_t));
108 prog->edictprivate = (void *)Mem_Realloc(prog->progs_mempool, (void *)prog->edictprivate, prog->max_edicts * prog->edictprivate_size);
110 //set e and v pointers
111 for(i = 0; i < prog->max_edicts; i++)
113 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
114 prog->edicts[i].fields.vp = prog->edictsfields + i * prog->entityfields;
117 PRVM_GCALL(end_increase_edicts)();
120 //============================================================================
123 int PRVM_ED_FindFieldOffset(const char *field)
126 d = PRVM_ED_FindField(field);
132 int PRVM_ED_FindGlobalOffset(const char *global)
135 d = PRVM_ED_FindGlobal(global);
141 func_t PRVM_ED_FindFunctionOffset(const char *function)
144 f = PRVM_ED_FindFunction(function);
147 return (func_t)(f - prog->functions);
150 qboolean PRVM_ProgLoaded(int prognr)
152 if(prognr < 0 || prognr >= PRVM_MAXPROGS)
155 return (prog_list[prognr].loaded ? TRUE : FALSE);
160 PRVM_SetProgFromString
163 // perhaps add a return value when the str doesnt exist
164 qboolean PRVM_SetProgFromString(const char *str)
167 for(; i < PRVM_MAXPROGS ; i++)
168 if(prog_list[i].name && !strcmp(prog_list[i].name,str))
170 if(prog_list[i].loaded)
172 prog = &prog_list[i];
177 Con_Printf("%s not loaded !\n",PRVM_NAME);
182 Con_Printf("Invalid program name %s !\n", str);
191 void PRVM_SetProg(int prognr)
193 if(0 <= prognr && prognr < PRVM_MAXPROGS)
195 if(prog_list[prognr].loaded)
196 prog = &prog_list[prognr];
198 PRVM_ERROR("%i not loaded !", prognr);
201 PRVM_ERROR("Invalid program number %i", prognr);
208 Sets everything to NULL
211 void PRVM_ED_ClearEdict (prvm_edict_t *e)
213 memset (e->fields.vp, 0, prog->progs->entityfields * 4);
214 e->priv.required->free = false;
216 // AK: Let the init_edict function determine if something needs to be initialized
217 PRVM_GCALL(init_edict)(e);
220 const char *PRVM_AllocationOrigin(void)
223 if(prog->leaktest_active)
224 if(prog->depth > 0) // actually in QC code and not just parsing the entities block of a map/savegame
226 buf = (char *)PRVM_Alloc(128);
227 PRVM_ShortStackTrace(buf, 128);
236 Returns if this particular edict could get allocated by PRVM_ED_Alloc
239 qboolean PRVM_ED_CanAlloc(prvm_edict_t *e)
241 if(!e->priv.required->free)
243 if(prvm_reuseedicts_always_allow == realtime)
245 if(realtime <= e->priv.required->freetime && prvm_reuseedicts_neverinsameframe.integer)
246 return false; // never allow reuse in same frame (causes networking trouble)
247 if(e->priv.required->freetime < prog->starttime + prvm_reuseedicts_startuptime.value)
249 if(realtime > e->priv.required->freetime + 1)
251 return false; // entity slot still blocked because the entity was freed less than one second ago
258 Either finds a free edict, or allocates a new one.
259 Try to avoid reusing an entity that was recently freed, because it
260 can cause the client to think the entity morphed into something else
261 instead of being removed and recreated, which can cause interpolated
262 angles and bad trails.
265 prvm_edict_t *PRVM_ED_Alloc (void)
270 // the client qc dont need maxclients
271 // thus it doesnt need to use svs.maxclients
272 // AK: changed i=svs.maxclients+1
273 // AK: changed so the edict 0 wont spawn -> used as reserved/world entity
274 // although the menu/client has no world
275 for (i = prog->reserved_edicts + 1;i < prog->num_edicts;i++)
277 e = PRVM_EDICT_NUM(i);
278 if(PRVM_ED_CanAlloc(e))
280 PRVM_ED_ClearEdict (e);
281 e->priv.required->allocation_origin = PRVM_AllocationOrigin();
286 if (i == prog->limit_edicts)
287 PRVM_ERROR ("%s: PRVM_ED_Alloc: no free edicts",PRVM_NAME);
290 if (prog->num_edicts >= prog->max_edicts)
291 PRVM_MEM_IncreaseEdicts();
293 e = PRVM_EDICT_NUM(i);
294 PRVM_ED_ClearEdict (e);
296 e->priv.required->allocation_origin = PRVM_AllocationOrigin();
305 Marks the edict as free
306 FIXME: walk all entities and NULL out references to this entity
309 void PRVM_ED_Free (prvm_edict_t *ed)
311 // dont delete the null entity (world) or reserved edicts
312 if(PRVM_NUM_FOR_EDICT(ed) <= prog->reserved_edicts )
315 PRVM_GCALL(free_edict)(ed);
317 ed->priv.required->free = true;
318 ed->priv.required->freetime = realtime;
319 if(ed->priv.required->allocation_origin)
321 PRVM_Free((char *)ed->priv.required->allocation_origin);
322 ed->priv.required->allocation_origin = NULL;
326 //===========================================================================
333 ddef_t *PRVM_ED_GlobalAtOfs (int ofs)
338 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
340 def = &prog->globaldefs[i];
352 ddef_t *PRVM_ED_FieldAtOfs (int ofs)
357 for (i=0 ; i<prog->progs->numfielddefs ; i++)
359 def = &prog->fielddefs[i];
371 ddef_t *PRVM_ED_FindField (const char *name)
376 for (i=0 ; i<prog->progs->numfielddefs ; i++)
378 def = &prog->fielddefs[i];
379 if (!strcmp(PRVM_GetString(def->s_name), name))
390 ddef_t *PRVM_ED_FindGlobal (const char *name)
395 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
397 def = &prog->globaldefs[i];
398 if (!strcmp(PRVM_GetString(def->s_name), name))
410 mfunction_t *PRVM_ED_FindFunction (const char *name)
415 for (i=0 ; i<prog->progs->numfunctions ; i++)
417 func = &prog->functions[i];
418 if (!strcmp(PRVM_GetString(func->s_name), name))
429 Returns a string describing *data in a type specific manner
432 char *PRVM_ValueString (etype_t type, prvm_eval_t *val)
434 static char line[MAX_INPUTLINE];
439 type = (etype_t)((int) type & ~DEF_SAVEGLOBAL);
444 strlcpy (line, PRVM_GetString (val->string), sizeof (line));
448 if (n < 0 || n >= prog->max_edicts)
449 dpsnprintf (line, sizeof(line), "entity %i (invalid!)", n);
451 dpsnprintf (line, sizeof(line), "entity %i", n);
454 f = prog->functions + val->function;
455 dpsnprintf (line, sizeof(line), "%s()", PRVM_GetString(f->s_name));
458 def = PRVM_ED_FieldAtOfs ( val->_int );
459 dpsnprintf (line, sizeof(line), ".%s", PRVM_GetString(def->s_name));
462 dpsnprintf (line, sizeof(line), "void");
465 // LordHavoc: changed from %5.1f to %10.4f
466 dpsnprintf (line, sizeof(line), "%10.4f", val->_float);
469 // LordHavoc: changed from %5.1f to %10.4f
470 dpsnprintf (line, sizeof(line), "'%10.4f %10.4f %10.4f'", val->vector[0], val->vector[1], val->vector[2]);
473 dpsnprintf (line, sizeof(line), "pointer");
476 dpsnprintf (line, sizeof(line), "bad type %i", (int) type);
487 Returns a string describing *data in a type specific manner
488 Easier to parse than PR_ValueString
491 char *PRVM_UglyValueString (etype_t type, prvm_eval_t *val)
493 static char line[MAX_INPUTLINE];
499 type = (etype_t)((int)type & ~DEF_SAVEGLOBAL);
504 // Parse the string a bit to turn special characters
505 // (like newline, specifically) into escape codes,
506 // this fixes saving games from various mods
507 s = PRVM_GetString (val->string);
508 for (i = 0;i < (int)sizeof(line) - 2 && *s;)
537 dpsnprintf (line, sizeof (line), "%i", PRVM_NUM_FOR_EDICT(PRVM_PROG_TO_EDICT(val->edict)));
540 f = prog->functions + val->function;
541 strlcpy (line, PRVM_GetString (f->s_name), sizeof (line));
544 def = PRVM_ED_FieldAtOfs ( val->_int );
545 dpsnprintf (line, sizeof (line), ".%s", PRVM_GetString(def->s_name));
548 dpsnprintf (line, sizeof (line), "void");
551 dpsnprintf (line, sizeof (line), "%.9g", val->_float);
554 dpsnprintf (line, sizeof (line), "%.9g %.9g %.9g", val->vector[0], val->vector[1], val->vector[2]);
557 dpsnprintf (line, sizeof (line), "bad type %i", type);
568 Returns a string with a description and the contents of a global,
569 padded to 20 field width
572 char *PRVM_GlobalString (int ofs)
578 static char line[128];
580 val = (void *)&prog->globals.generic[ofs];
581 def = PRVM_ED_GlobalAtOfs(ofs);
583 dpsnprintf (line, sizeof(line), "GLOBAL%i", ofs);
586 s = PRVM_ValueString ((etype_t)def->type, (prvm_eval_t *)val);
587 dpsnprintf (line, sizeof(line), "%s (=%s)", PRVM_GetString(def->s_name), s);
591 //for ( ; i<20 ; i++)
592 // strcat (line," ");
598 char *PRVM_GlobalStringNoContents (int ofs)
602 static char line[128];
604 def = PRVM_ED_GlobalAtOfs(ofs);
606 dpsnprintf (line, sizeof(line), "GLOBAL%i", ofs);
608 dpsnprintf (line, sizeof(line), "%s", PRVM_GetString(def->s_name));
611 //for ( ; i<20 ; i++)
612 // strcat (line," ");
626 // LordHavoc: optimized this to print out much more quickly (tempstring)
627 // LordHavoc: changed to print out every 4096 characters (incase there are a lot of fields to print)
628 void PRVM_ED_Print(prvm_edict_t *ed, const char *wildcard_fieldname)
636 char tempstring[MAX_INPUTLINE], tempstring2[260]; // temporary string buffers
638 if (ed->priv.required->free)
640 Con_Printf("%s: FREE\n",PRVM_NAME);
645 dpsnprintf(tempstring, sizeof(tempstring), "\n%s EDICT %i:\n", PRVM_NAME, PRVM_NUM_FOR_EDICT(ed));
646 for (i=1 ; i<prog->progs->numfielddefs ; i++)
648 d = &prog->fielddefs[i];
649 name = PRVM_GetString(d->s_name);
650 if (name[strlen(name)-2] == '_')
651 continue; // skip _x, _y, _z vars
653 // Check Field Name Wildcard
654 if(wildcard_fieldname)
655 if( !matchpattern(name, wildcard_fieldname, 1) )
656 // Didn't match; skip
659 v = (int *)(ed->fields.vp + d->ofs);
661 // if the value is still all 0, skip the field
662 type = d->type & ~DEF_SAVEGLOBAL;
664 for (j=0 ; j<prvm_type_size[type] ; j++)
667 if (j == prvm_type_size[type])
670 if (strlen(name) > sizeof(tempstring2)-4)
672 memcpy (tempstring2, name, sizeof(tempstring2)-4);
673 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
674 tempstring2[sizeof(tempstring2)-1] = 0;
677 strlcat(tempstring, name, sizeof(tempstring));
678 for (l = strlen(name);l < 14;l++)
679 strlcat(tempstring, " ", sizeof(tempstring));
680 strlcat(tempstring, " ", sizeof(tempstring));
682 name = PRVM_ValueString((etype_t)d->type, (prvm_eval_t *)v);
683 if (strlen(name) > sizeof(tempstring2)-4)
685 memcpy (tempstring2, name, sizeof(tempstring2)-4);
686 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
687 tempstring2[sizeof(tempstring2)-1] = 0;
690 strlcat(tempstring, name, sizeof(tempstring));
691 strlcat(tempstring, "\n", sizeof(tempstring));
692 if (strlen(tempstring) >= sizeof(tempstring)/2)
694 Con_Print(tempstring);
699 Con_Print(tempstring);
709 extern cvar_t developer_entityparsing;
710 void PRVM_ED_Write (qfile_t *f, prvm_edict_t *ed)
720 if (ed->priv.required->free)
726 for (i=1 ; i<prog->progs->numfielddefs ; i++)
728 d = &prog->fielddefs[i];
729 name = PRVM_GetString(d->s_name);
731 if(developer_entityparsing.integer)
732 Con_Printf("PRVM_ED_Write: at entity %d field %s\n", PRVM_NUM_FOR_EDICT(ed), name);
734 if (name[strlen(name)-2] == '_')
735 continue; // skip _x, _y, _z vars
737 v = (int *)(ed->fields.vp + d->ofs);
739 // if the value is still all 0, skip the field
740 type = d->type & ~DEF_SAVEGLOBAL;
741 for (j=0 ; j<prvm_type_size[type] ; j++)
744 if (j == prvm_type_size[type])
747 FS_Printf(f,"\"%s\" ",name);
748 prog->statestring = va("PRVM_ED_Write, ent=%d, name=%s", i, name);
749 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString((etype_t)d->type, (prvm_eval_t *)v));
750 prog->statestring = NULL;
756 void PRVM_ED_PrintNum (int ent, const char *wildcard_fieldname)
758 PRVM_ED_Print(PRVM_EDICT_NUM(ent), wildcard_fieldname);
763 PRVM_ED_PrintEdicts_f
765 For debugging, prints all the entities in the current server
768 void PRVM_ED_PrintEdicts_f (void)
771 const char *wildcard_fieldname;
773 if(Cmd_Argc() < 2 || Cmd_Argc() > 3)
775 Con_Print("prvm_edicts <program name> <optional field name wildcard>\n");
780 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
784 wildcard_fieldname = Cmd_Argv(2);
786 wildcard_fieldname = NULL;
788 Con_Printf("%s: %i entities\n", PRVM_NAME, prog->num_edicts);
789 for (i=0 ; i<prog->num_edicts ; i++)
790 PRVM_ED_PrintNum (i, wildcard_fieldname);
799 For debugging, prints a single edict
802 void PRVM_ED_PrintEdict_f (void)
805 const char *wildcard_fieldname;
807 if(Cmd_Argc() < 3 || Cmd_Argc() > 4)
809 Con_Print("prvm_edict <program name> <edict number> <optional field name wildcard>\n");
814 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
817 i = atoi (Cmd_Argv(2));
818 if (i >= prog->num_edicts)
820 Con_Print("Bad edict number\n");
825 // Optional Wildcard Provided
826 wildcard_fieldname = Cmd_Argv(3);
829 wildcard_fieldname = NULL;
830 PRVM_ED_PrintNum (i, wildcard_fieldname);
842 // 2 possibilities : 1. just displaying the active edict count
843 // 2. making a function pointer [x]
844 void PRVM_ED_Count_f (void)
852 Con_Print("prvm_count <program name>\n");
857 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
860 if(prog->count_edicts)
861 prog->count_edicts();
865 for (i=0 ; i<prog->num_edicts ; i++)
867 ent = PRVM_EDICT_NUM(i);
868 if (ent->priv.required->free)
873 Con_Printf("num_edicts:%3i\n", prog->num_edicts);
874 Con_Printf("active :%3i\n", active);
881 ==============================================================================
885 FIXME: need to tag constants, doesn't really work
886 ==============================================================================
894 void PRVM_ED_WriteGlobals (qfile_t *f)
902 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
904 def = &prog->globaldefs[i];
906 if ( !(def->type & DEF_SAVEGLOBAL) )
908 type &= ~DEF_SAVEGLOBAL;
910 if (type != ev_string && type != ev_float && type != ev_entity)
913 name = PRVM_GetString(def->s_name);
915 if(developer_entityparsing.integer)
916 Con_Printf("PRVM_ED_WriteGlobals: at global %s\n", name);
918 prog->statestring = va("PRVM_ED_WriteGlobals, name=%s", name);
919 FS_Printf(f,"\"%s\" ", name);
920 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString((etype_t)type, (prvm_eval_t *)&prog->globals.generic[def->ofs]));
921 prog->statestring = NULL;
931 void PRVM_ED_ParseGlobals (const char *data)
933 char keyname[MAX_INPUTLINE];
939 if (!COM_ParseToken_Simple(&data, false, false))
940 PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace");
941 if (com_token[0] == '}')
944 if (developer_entityparsing.integer)
945 Con_Printf("Key: \"%s\"", com_token);
947 strlcpy (keyname, com_token, sizeof(keyname));
950 if (!COM_ParseToken_Simple(&data, false, true))
951 PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace");
953 if (developer_entityparsing.integer)
954 Con_Printf(" \"%s\"\n", com_token);
956 if (com_token[0] == '}')
957 PRVM_ERROR ("PRVM_ED_ParseGlobals: closing brace without data");
959 key = PRVM_ED_FindGlobal (keyname);
962 Con_DPrintf("'%s' is not a global on %s\n", keyname, PRVM_NAME);
966 if (!PRVM_ED_ParseEpair(NULL, key, com_token, true))
967 PRVM_ERROR ("PRVM_ED_ParseGlobals: parse error");
971 //============================================================================
978 Can parse either fields or globals
979 returns false if error
982 qboolean PRVM_ED_ParseEpair(prvm_edict_t *ent, ddef_t *key, const char *s, qboolean parsebackslash)
991 val = (prvm_eval_t *)(ent->fields.vp + key->ofs);
993 val = (prvm_eval_t *)(prog->globals.generic + key->ofs);
994 switch (key->type & ~DEF_SAVEGLOBAL)
997 l = (int)strlen(s) + 1;
998 val->string = PRVM_AllocString(l, &new_p);
999 for (i = 0;i < l;i++)
1001 if (s[i] == '\\' && s[i+1] && parsebackslash)
1006 else if (s[i] == 'r')
1017 while (*s && ISWHITESPACE(*s))
1019 val->_float = atof(s);
1023 for (i = 0;i < 3;i++)
1025 while (*s && ISWHITESPACE(*s))
1029 val->vector[i] = atof(s);
1030 while (!ISWHITESPACE(*s))
1038 while (*s && ISWHITESPACE(*s))
1041 if (i >= prog->limit_edicts)
1042 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);
1043 while (i >= prog->max_edicts)
1044 PRVM_MEM_IncreaseEdicts();
1045 // if IncreaseEdicts was called the base pointer needs to be updated
1047 val = (prvm_eval_t *)(ent->fields.vp + key->ofs);
1048 val->edict = PRVM_EDICT_TO_PROG(PRVM_EDICT_NUM((int)i));
1054 Con_DPrintf("PRVM_ED_ParseEpair: Bogus field name %s in %s\n", s, PRVM_NAME);
1057 def = PRVM_ED_FindField(s + 1);
1060 Con_DPrintf("PRVM_ED_ParseEpair: Can't find field %s in %s\n", s, PRVM_NAME);
1063 val->_int = def->ofs;
1067 func = PRVM_ED_FindFunction(s);
1070 Con_Printf("PRVM_ED_ParseEpair: Can't find function %s in %s\n", s, PRVM_NAME);
1073 val->function = func - prog->functions;
1077 Con_Printf("PRVM_ED_ParseEpair: Unknown key->type %i for key \"%s\" on %s\n", key->type, PRVM_GetString(key->s_name), PRVM_NAME);
1087 Console command to send a string to QC function GameCommand of the
1091 sv_cmd adminmsg 3 "do not teamkill"
1092 cl_cmd someclientcommand
1093 menu_cmd somemenucommand
1095 All progs can support this extension; sg calls it in server QC, cg in client
1099 void PRVM_GameCommand(const char *whichprogs, const char *whichcmd)
1103 Con_Printf("%s text...\n", whichcmd);
1108 if(!PRVM_SetProgFromString(whichprogs))
1109 // note: this is not PRVM_SetProg because that one aborts "hard" using PRVM_Error
1110 // also, it makes printing error messages easier!
1112 Con_Printf("%s program not loaded.\n", whichprogs);
1116 if(!prog->funcoffsets.GameCommand)
1118 Con_Printf("%s program do not support GameCommand!\n", whichprogs);
1122 int restorevm_tempstringsbuf_cursize;
1127 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
1128 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(s ? s : "");
1129 PRVM_ExecuteProgram (prog->funcoffsets.GameCommand, "QC function GameCommand is missing");
1130 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1135 void PRVM_GameCommand_Server_f(void)
1137 PRVM_GameCommand("server", "sv_cmd");
1139 void PRVM_GameCommand_Client_f(void)
1141 PRVM_GameCommand("client", "cl_cmd");
1143 void PRVM_GameCommand_Menu_f(void)
1145 PRVM_GameCommand("menu", "menu_cmd");
1152 Console command to load a field of a specified edict
1155 void PRVM_ED_EdictGet_f(void)
1162 if(Cmd_Argc() != 4 && Cmd_Argc() != 5)
1164 Con_Print("prvm_edictget <program name> <edict number> <field> [<cvar>]\n");
1169 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1171 Con_Printf("Wrong program name %s !\n", Cmd_Argv(1));
1175 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1177 if((key = PRVM_ED_FindField(Cmd_Argv(3))) == 0)
1179 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1183 v = (prvm_eval_t *)(ed->fields.vp + key->ofs);
1184 s = PRVM_UglyValueString(key->type, v);
1187 cvar_t *cvar = Cvar_FindVar(Cmd_Argv(4));
1188 if (cvar && cvar->flags & CVAR_READONLY)
1190 Con_Printf("prvm_edictget: %s is read-only\n", cvar->name);
1193 Cvar_Get(Cmd_Argv(4), s, 0, NULL);
1196 Con_Printf("%s\n", s);
1202 void PRVM_ED_GlobalGet_f(void)
1208 if(Cmd_Argc() != 3 && Cmd_Argc() != 4)
1210 Con_Print("prvm_globalget <program name> <global> [<cvar>]\n");
1215 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1217 Con_Printf("Wrong program name %s !\n", Cmd_Argv(1));
1221 key = PRVM_ED_FindGlobal(Cmd_Argv(2));
1224 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
1228 v = (prvm_eval_t *) &prog->globals.generic[key->ofs];
1229 s = PRVM_UglyValueString(key->type, v);
1232 cvar_t *cvar = Cvar_FindVar(Cmd_Argv(3));
1233 if (cvar && cvar->flags & CVAR_READONLY)
1235 Con_Printf("prvm_globalget: %s is read-only\n", cvar->name);
1238 Cvar_Get(Cmd_Argv(3), s, 0, NULL);
1241 Con_Printf("%s\n", s);
1251 Console command to set a field of a specified edict
1254 void PRVM_ED_EdictSet_f(void)
1261 Con_Print("prvm_edictset <program name> <edict number> <field> <value>\n");
1266 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1268 Con_Printf("Wrong program name %s !\n", Cmd_Argv(1));
1272 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1274 if((key = PRVM_ED_FindField(Cmd_Argv(3))) == 0)
1275 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1277 PRVM_ED_ParseEpair(ed, key, Cmd_Argv(4), true);
1283 ====================
1286 Parses an edict out of the given string, returning the new position
1287 ed should be a properly initialized empty edict.
1288 Used for initial level load and for savegames.
1289 ====================
1291 const char *PRVM_ED_ParseEdict (const char *data, prvm_edict_t *ent)
1301 // go through all the dictionary pairs
1305 if (!COM_ParseToken_Simple(&data, false, false))
1306 PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1307 if (developer_entityparsing.integer)
1308 Con_Printf("Key: \"%s\"", com_token);
1309 if (com_token[0] == '}')
1312 // anglehack is to allow QuakeEd to write single scalar angles
1313 // and allow them to be turned into vectors. (FIXME...)
1314 if (!strcmp(com_token, "angle"))
1316 strlcpy (com_token, "angles", sizeof(com_token));
1322 // FIXME: change light to _light to get rid of this hack
1323 if (!strcmp(com_token, "light"))
1324 strlcpy (com_token, "light_lev", sizeof(com_token)); // hack for single light def
1326 strlcpy (keyname, com_token, sizeof(keyname));
1328 // another hack to fix keynames with trailing spaces
1329 n = strlen(keyname);
1330 while (n && keyname[n-1] == ' ')
1337 if (!COM_ParseToken_Simple(&data, false, false))
1338 PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1339 if (developer_entityparsing.integer)
1340 Con_Printf(" \"%s\"\n", com_token);
1342 if (com_token[0] == '}')
1343 PRVM_ERROR ("PRVM_ED_ParseEdict: closing brace without data");
1347 // ignore attempts to set key "" (this problem occurs in nehahra neh1m8.bsp)
1351 // keynames with a leading underscore are used for utility comments,
1352 // and are immediately discarded by quake
1353 if (keyname[0] == '_')
1356 key = PRVM_ED_FindField (keyname);
1359 Con_DPrintf("%s: '%s' is not a field\n", PRVM_NAME, keyname);
1366 strlcpy (temp, com_token, sizeof(temp));
1367 dpsnprintf (com_token, sizeof(com_token), "0 %s 0", temp);
1370 if (!PRVM_ED_ParseEpair(ent, key, com_token, strcmp(keyname, "wad") != 0))
1371 PRVM_ERROR ("PRVM_ED_ParseEdict: parse error");
1375 ent->priv.required->free = true;
1383 PRVM_ED_LoadFromFile
1385 The entities are directly placed in the array, rather than allocated with
1386 PRVM_ED_Alloc, because otherwise an error loading the map would have entity
1387 number references out of order.
1389 Creates a server's entity / program execution context by
1390 parsing textual entity definitions out of an ent file.
1392 Used for both fresh maps and savegame loads. A fresh map would also need
1393 to call PRVM_ED_CallSpawnFunctions () to let the objects initialize themselves.
1396 void PRVM_ED_LoadFromFile (const char *data)
1399 int parsed, inhibited, spawned, died;
1400 const char *funcname;
1408 prvm_reuseedicts_always_allow = realtime;
1413 // parse the opening brace
1414 if (!COM_ParseToken_Simple(&data, false, false))
1416 if (com_token[0] != '{')
1417 PRVM_ERROR ("PRVM_ED_LoadFromFile: %s: found %s when expecting {", PRVM_NAME, com_token);
1419 // CHANGED: this is not conform to PR_LoadFromFile
1420 if(prog->loadintoworld)
1422 prog->loadintoworld = false;
1423 ent = PRVM_EDICT_NUM(0);
1426 ent = PRVM_ED_Alloc();
1429 if (ent != prog->edicts) // hack
1430 memset (ent->fields.vp, 0, prog->progs->entityfields * 4);
1432 data = PRVM_ED_ParseEdict (data, ent);
1435 // remove the entity ?
1436 if(prog->load_edict && !prog->load_edict(ent))
1443 if (prog->funcoffsets.SV_OnEntityPreSpawnFunction)
1446 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1447 PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityPreSpawnFunction, "QC function SV_OnEntityPreSpawnFunction is missing");
1450 if(ent->priv.required->free)
1457 // immediately call spawn function, but only if there is a self global and a classname
1459 if(!ent->priv.required->free)
1460 if(prog->globaloffsets.self >= 0 && prog->fieldoffsets.classname >= 0)
1462 string_t handle = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.classname)->string;
1465 Con_Print("No classname for:\n");
1466 PRVM_ED_Print(ent, NULL);
1471 // look for the spawn function
1472 funcname = PRVM_GetString(handle);
1473 func = PRVM_ED_FindFunction (va("spawnfunc_%s", funcname));
1475 if(prog->globaloffsets.require_spawnfunc_prefix < 0)
1476 func = PRVM_ED_FindFunction (funcname);
1480 // check for OnEntityNoSpawnFunction
1481 if (prog->funcoffsets.SV_OnEntityNoSpawnFunction)
1484 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1485 PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityNoSpawnFunction, "QC function SV_OnEntityNoSpawnFunction is missing");
1489 if (developer.integer > 0) // don't confuse non-developers with errors
1491 Con_Print("No spawn function for:\n");
1492 PRVM_ED_Print(ent, NULL);
1495 continue; // not included in "inhibited" count
1501 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1502 PRVM_ExecuteProgram (func - prog->functions, "");
1506 if(!ent->priv.required->free)
1507 if (prog->funcoffsets.SV_OnEntityPostSpawnFunction)
1510 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1511 PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityPostSpawnFunction, "QC function SV_OnEntityPostSpawnFunction is missing");
1515 if (ent->priv.required->free)
1519 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);
1521 prvm_reuseedicts_always_allow = 0;
1524 void PRVM_FindOffsets(void)
1526 // field and global searches use -1 for NULL
1527 memset(&prog->fieldoffsets, -1, sizeof(prog->fieldoffsets));
1528 memset(&prog->globaloffsets, -1, sizeof(prog->globaloffsets));
1529 // functions use 0 for NULL
1530 memset(&prog->funcoffsets, 0, sizeof(prog->funcoffsets));
1532 // server and client qc use a lot of similar fields, so this is combined
1533 prog->fieldoffsets.SendEntity = PRVM_ED_FindFieldOffset("SendEntity");
1534 prog->fieldoffsets.SendFlags = PRVM_ED_FindFieldOffset("SendFlags");
1535 prog->fieldoffsets.Version = PRVM_ED_FindFieldOffset("Version");
1536 prog->fieldoffsets.alpha = PRVM_ED_FindFieldOffset("alpha");
1537 prog->fieldoffsets.ammo_cells1 = PRVM_ED_FindFieldOffset("ammo_cells1");
1538 prog->fieldoffsets.ammo_lava_nails = PRVM_ED_FindFieldOffset("ammo_lava_nails");
1539 prog->fieldoffsets.ammo_multi_rockets = PRVM_ED_FindFieldOffset("ammo_multi_rockets");
1540 prog->fieldoffsets.ammo_nails1 = PRVM_ED_FindFieldOffset("ammo_nails1");
1541 prog->fieldoffsets.ammo_plasma = PRVM_ED_FindFieldOffset("ammo_plasma");
1542 prog->fieldoffsets.ammo_rockets1 = PRVM_ED_FindFieldOffset("ammo_rockets1");
1543 prog->fieldoffsets.ammo_shells1 = PRVM_ED_FindFieldOffset("ammo_shells1");
1544 prog->fieldoffsets.angles = PRVM_ED_FindFieldOffset("angles");
1545 prog->fieldoffsets.button3 = PRVM_ED_FindFieldOffset("button3");
1546 prog->fieldoffsets.button4 = PRVM_ED_FindFieldOffset("button4");
1547 prog->fieldoffsets.button5 = PRVM_ED_FindFieldOffset("button5");
1548 prog->fieldoffsets.button6 = PRVM_ED_FindFieldOffset("button6");
1549 prog->fieldoffsets.button7 = PRVM_ED_FindFieldOffset("button7");
1550 prog->fieldoffsets.button8 = PRVM_ED_FindFieldOffset("button8");
1551 prog->fieldoffsets.button9 = PRVM_ED_FindFieldOffset("button9");
1552 prog->fieldoffsets.button10 = PRVM_ED_FindFieldOffset("button10");
1553 prog->fieldoffsets.button11 = PRVM_ED_FindFieldOffset("button11");
1554 prog->fieldoffsets.button12 = PRVM_ED_FindFieldOffset("button12");
1555 prog->fieldoffsets.button13 = PRVM_ED_FindFieldOffset("button13");
1556 prog->fieldoffsets.button14 = PRVM_ED_FindFieldOffset("button14");
1557 prog->fieldoffsets.button15 = PRVM_ED_FindFieldOffset("button15");
1558 prog->fieldoffsets.button16 = PRVM_ED_FindFieldOffset("button16");
1559 prog->fieldoffsets.buttonchat = PRVM_ED_FindFieldOffset("buttonchat");
1560 prog->fieldoffsets.buttonuse = PRVM_ED_FindFieldOffset("buttonuse");
1561 prog->fieldoffsets.chain = PRVM_ED_FindFieldOffset("chain");
1562 prog->fieldoffsets.classname = PRVM_ED_FindFieldOffset("classname");
1563 prog->fieldoffsets.clientcamera = PRVM_ED_FindFieldOffset("clientcamera");
1564 prog->fieldoffsets.clientcolors = PRVM_ED_FindFieldOffset("clientcolors");
1565 prog->fieldoffsets.clientstatus = PRVM_ED_FindFieldOffset("clientstatus");
1566 prog->fieldoffsets.color = PRVM_ED_FindFieldOffset("color");
1567 prog->fieldoffsets.colormod = PRVM_ED_FindFieldOffset("colormod");
1568 prog->fieldoffsets.contentstransition = PRVM_ED_FindFieldOffset("contentstransition");
1569 prog->fieldoffsets.cursor_active = PRVM_ED_FindFieldOffset("cursor_active");
1570 prog->fieldoffsets.cursor_screen = PRVM_ED_FindFieldOffset("cursor_screen");
1571 prog->fieldoffsets.cursor_trace_endpos = PRVM_ED_FindFieldOffset("cursor_trace_endpos");
1572 prog->fieldoffsets.cursor_trace_ent = PRVM_ED_FindFieldOffset("cursor_trace_ent");
1573 prog->fieldoffsets.cursor_trace_start = PRVM_ED_FindFieldOffset("cursor_trace_start");
1574 prog->fieldoffsets.customizeentityforclient = PRVM_ED_FindFieldOffset("customizeentityforclient");
1575 prog->fieldoffsets.dimension_hit = PRVM_ED_FindFieldOffset("dimension_hit");
1576 prog->fieldoffsets.dimension_solid = PRVM_ED_FindFieldOffset("dimension_solid");
1577 prog->fieldoffsets.disableclientprediction = PRVM_ED_FindFieldOffset("disableclientprediction");
1578 prog->fieldoffsets.discardabledemo = PRVM_ED_FindFieldOffset("discardabledemo");
1579 prog->fieldoffsets.dphitcontentsmask = PRVM_ED_FindFieldOffset("dphitcontentsmask");
1580 prog->fieldoffsets.drawonlytoclient = PRVM_ED_FindFieldOffset("drawonlytoclient");
1581 prog->fieldoffsets.exteriormodeltoclient = PRVM_ED_FindFieldOffset("exteriormodeltoclient");
1582 prog->fieldoffsets.fatness = PRVM_ED_FindFieldOffset("fatness");
1583 prog->fieldoffsets.forceshader = PRVM_ED_FindFieldOffset("forceshader");
1584 prog->fieldoffsets.frame = PRVM_ED_FindFieldOffset("frame");
1585 prog->fieldoffsets.frame1time = PRVM_ED_FindFieldOffset("frame1time");
1586 prog->fieldoffsets.frame2 = PRVM_ED_FindFieldOffset("frame2");
1587 prog->fieldoffsets.frame2time = PRVM_ED_FindFieldOffset("frame2time");
1588 prog->fieldoffsets.frame3 = PRVM_ED_FindFieldOffset("frame3");
1589 prog->fieldoffsets.frame3time = PRVM_ED_FindFieldOffset("frame3time");
1590 prog->fieldoffsets.frame4 = PRVM_ED_FindFieldOffset("frame4");
1591 prog->fieldoffsets.frame4time = PRVM_ED_FindFieldOffset("frame4time");
1592 prog->fieldoffsets.fullbright = PRVM_ED_FindFieldOffset("fullbright");
1593 prog->fieldoffsets.glow_color = PRVM_ED_FindFieldOffset("glow_color");
1594 prog->fieldoffsets.glow_size = PRVM_ED_FindFieldOffset("glow_size");
1595 prog->fieldoffsets.glow_trail = PRVM_ED_FindFieldOffset("glow_trail");
1596 prog->fieldoffsets.glowmod = PRVM_ED_FindFieldOffset("glowmod");
1597 prog->fieldoffsets.gravity = PRVM_ED_FindFieldOffset("gravity");
1598 prog->fieldoffsets.groundentity = PRVM_ED_FindFieldOffset("groundentity");
1599 prog->fieldoffsets.hull = PRVM_ED_FindFieldOffset("hull");
1600 prog->fieldoffsets.ideal_yaw = PRVM_ED_FindFieldOffset("ideal_yaw");
1601 prog->fieldoffsets.idealpitch = PRVM_ED_FindFieldOffset("idealpitch");
1602 prog->fieldoffsets.items2 = PRVM_ED_FindFieldOffset("items2");
1603 prog->fieldoffsets.lerpfrac = PRVM_ED_FindFieldOffset("lerpfrac");
1604 prog->fieldoffsets.lerpfrac3 = PRVM_ED_FindFieldOffset("lerpfrac3");
1605 prog->fieldoffsets.lerpfrac4 = PRVM_ED_FindFieldOffset("lerpfrac4");
1606 prog->fieldoffsets.light_lev = PRVM_ED_FindFieldOffset("light_lev");
1607 prog->fieldoffsets.message = PRVM_ED_FindFieldOffset("message");
1608 prog->fieldoffsets.modelflags = PRVM_ED_FindFieldOffset("modelflags");
1609 prog->fieldoffsets.movement = PRVM_ED_FindFieldOffset("movement");
1610 prog->fieldoffsets.movetypesteplandevent = PRVM_ED_FindFieldOffset("movetypesteplandevent");
1611 prog->fieldoffsets.netaddress = PRVM_ED_FindFieldOffset("netaddress");
1612 prog->fieldoffsets.nextthink = PRVM_ED_FindFieldOffset("nextthink");
1613 prog->fieldoffsets.nodrawtoclient = PRVM_ED_FindFieldOffset("nodrawtoclient");
1614 prog->fieldoffsets.pflags = PRVM_ED_FindFieldOffset("pflags");
1615 prog->fieldoffsets.ping = PRVM_ED_FindFieldOffset("ping");
1616 prog->fieldoffsets.packetloss = PRVM_ED_FindFieldOffset("ping_packetloss");
1617 prog->fieldoffsets.movementloss = PRVM_ED_FindFieldOffset("ping_movementloss");
1618 prog->fieldoffsets.pitch_speed = PRVM_ED_FindFieldOffset("pitch_speed");
1619 prog->fieldoffsets.playermodel = PRVM_ED_FindFieldOffset("playermodel");
1620 prog->fieldoffsets.playerskin = PRVM_ED_FindFieldOffset("playerskin");
1621 prog->fieldoffsets.pmodel = PRVM_ED_FindFieldOffset("pmodel");
1622 prog->fieldoffsets.punchvector = PRVM_ED_FindFieldOffset("punchvector");
1623 prog->fieldoffsets.renderamt = PRVM_ED_FindFieldOffset("renderamt"); // HalfLife support
1624 prog->fieldoffsets.renderflags = PRVM_ED_FindFieldOffset("renderflags");
1625 prog->fieldoffsets.rendermode = PRVM_ED_FindFieldOffset("rendermode"); // HalfLife support
1626 prog->fieldoffsets.scale = PRVM_ED_FindFieldOffset("scale");
1627 prog->fieldoffsets.shadertime = PRVM_ED_FindFieldOffset("shadertime");
1628 prog->fieldoffsets.skeletonindex = PRVM_ED_FindFieldOffset("skeletonindex");
1629 prog->fieldoffsets.style = PRVM_ED_FindFieldOffset("style");
1630 prog->fieldoffsets.tag_entity = PRVM_ED_FindFieldOffset("tag_entity");
1631 prog->fieldoffsets.tag_index = PRVM_ED_FindFieldOffset("tag_index");
1632 prog->fieldoffsets.think = PRVM_ED_FindFieldOffset("think");
1633 prog->fieldoffsets.viewmodelforclient = PRVM_ED_FindFieldOffset("viewmodelforclient");
1634 prog->fieldoffsets.viewzoom = PRVM_ED_FindFieldOffset("viewzoom");
1635 prog->fieldoffsets.yaw_speed = PRVM_ED_FindFieldOffset("yaw_speed");
1636 prog->fieldoffsets.bouncefactor = PRVM_ED_FindFieldOffset("bouncefactor");
1637 prog->fieldoffsets.bouncestop = PRVM_ED_FindFieldOffset("bouncestop");
1639 prog->fieldoffsets.solid = PRVM_ED_FindFieldOffset("solid");
1640 prog->fieldoffsets.movetype = PRVM_ED_FindFieldOffset("movetype");
1641 prog->fieldoffsets.modelindex = PRVM_ED_FindFieldOffset("modelindex");
1642 prog->fieldoffsets.mins = PRVM_ED_FindFieldOffset("mins");
1643 prog->fieldoffsets.maxs = PRVM_ED_FindFieldOffset("maxs");
1644 prog->fieldoffsets.mass = PRVM_ED_FindFieldOffset("mass");
1645 prog->fieldoffsets.origin = PRVM_ED_FindFieldOffset("origin");
1646 prog->fieldoffsets.velocity = PRVM_ED_FindFieldOffset("velocity");
1647 //prog->fieldoffsets.axis_forward = PRVM_ED_FindFieldOffset("axis_forward");
1648 //prog->fieldoffsets.axis_left = PRVM_ED_FindFieldOffset("axis_left");
1649 //prog->fieldoffsets.axis_up = PRVM_ED_FindFieldOffset("axis_up");
1650 //prog->fieldoffsets.spinvelocity = PRVM_ED_FindFieldOffset("spinvelocity");
1651 prog->fieldoffsets.angles = PRVM_ED_FindFieldOffset("angles");
1652 prog->fieldoffsets.avelocity = PRVM_ED_FindFieldOffset("avelocity");
1653 prog->fieldoffsets.aiment = PRVM_ED_FindFieldOffset("aiment");
1654 prog->fieldoffsets.enemy = PRVM_ED_FindFieldOffset("enemy");
1655 prog->fieldoffsets.jointtype = PRVM_ED_FindFieldOffset("jointtype");
1656 prog->fieldoffsets.movedir = PRVM_ED_FindFieldOffset("movedir");
1658 prog->fieldoffsets.camera_transform = PRVM_ED_FindFieldOffset("camera_transform");
1659 prog->fieldoffsets.userwavefunc_param0 = PRVM_ED_FindFieldOffset("userwavefunc_param0");
1660 prog->fieldoffsets.userwavefunc_param1 = PRVM_ED_FindFieldOffset("userwavefunc_param1");
1661 prog->fieldoffsets.userwavefunc_param2 = PRVM_ED_FindFieldOffset("userwavefunc_param2");
1662 prog->fieldoffsets.userwavefunc_param3 = PRVM_ED_FindFieldOffset("userwavefunc_param3");
1664 prog->funcoffsets.CSQC_ConsoleCommand = PRVM_ED_FindFunctionOffset("CSQC_ConsoleCommand");
1665 prog->funcoffsets.CSQC_Ent_Remove = PRVM_ED_FindFunctionOffset("CSQC_Ent_Remove");
1666 prog->funcoffsets.CSQC_Ent_Spawn = PRVM_ED_FindFunctionOffset("CSQC_Ent_Spawn");
1667 prog->funcoffsets.CSQC_Ent_Update = PRVM_ED_FindFunctionOffset("CSQC_Ent_Update");
1668 prog->funcoffsets.CSQC_Event = PRVM_ED_FindFunctionOffset("CSQC_Event");
1669 prog->funcoffsets.CSQC_Event_Sound = PRVM_ED_FindFunctionOffset("CSQC_Event_Sound");
1670 prog->funcoffsets.CSQC_Init = PRVM_ED_FindFunctionOffset("CSQC_Init");
1671 prog->funcoffsets.CSQC_InputEvent = PRVM_ED_FindFunctionOffset("CSQC_InputEvent");
1672 prog->funcoffsets.CSQC_Parse_CenterPrint = PRVM_ED_FindFunctionOffset("CSQC_Parse_CenterPrint");
1673 prog->funcoffsets.CSQC_Parse_Print = PRVM_ED_FindFunctionOffset("CSQC_Parse_Print");
1674 prog->funcoffsets.CSQC_Parse_StuffCmd = PRVM_ED_FindFunctionOffset("CSQC_Parse_StuffCmd");
1675 prog->funcoffsets.CSQC_Parse_TempEntity = PRVM_ED_FindFunctionOffset("CSQC_Parse_TempEntity");
1676 prog->funcoffsets.CSQC_Shutdown = PRVM_ED_FindFunctionOffset("CSQC_Shutdown");
1677 prog->funcoffsets.CSQC_UpdateView = PRVM_ED_FindFunctionOffset("CSQC_UpdateView");
1678 prog->funcoffsets.EndFrame = PRVM_ED_FindFunctionOffset("EndFrame");
1679 prog->funcoffsets.GameCommand = PRVM_ED_FindFunctionOffset("GameCommand");
1680 prog->funcoffsets.Gecko_Query = PRVM_ED_FindFunctionOffset("Gecko_Query");
1681 prog->funcoffsets.RestoreGame = PRVM_ED_FindFunctionOffset("RestoreGame");
1682 prog->funcoffsets.SV_ChangeTeam = PRVM_ED_FindFunctionOffset("SV_ChangeTeam");
1683 prog->funcoffsets.SV_OnEntityNoSpawnFunction = PRVM_ED_FindFunctionOffset("SV_OnEntityNoSpawnFunction");
1684 prog->funcoffsets.SV_OnEntityPostSpawnFunction = PRVM_ED_FindFunctionOffset("SV_OnEntityPostSpawnFunction");
1685 prog->funcoffsets.SV_OnEntityPreSpawnFunction = PRVM_ED_FindFunctionOffset("SV_OnEntityPreSpawnFunction");
1686 prog->funcoffsets.SV_ParseClientCommand = PRVM_ED_FindFunctionOffset("SV_ParseClientCommand");
1687 prog->funcoffsets.SV_PausedTic = PRVM_ED_FindFunctionOffset("SV_PausedTic");
1688 prog->funcoffsets.SV_PlayerPhysics = PRVM_ED_FindFunctionOffset("SV_PlayerPhysics");
1689 prog->funcoffsets.SV_Shutdown = PRVM_ED_FindFunctionOffset("SV_Shutdown");
1690 prog->funcoffsets.URI_Get_Callback = PRVM_ED_FindFunctionOffset("URI_Get_Callback");
1691 prog->globaloffsets.SV_InitCmd = PRVM_ED_FindGlobalOffset("SV_InitCmd");
1692 prog->globaloffsets.coop = PRVM_ED_FindGlobalOffset("coop");
1693 prog->globaloffsets.deathmatch = PRVM_ED_FindGlobalOffset("deathmatch");
1694 prog->globaloffsets.dmg_origin = PRVM_ED_FindGlobalOffset("dmg_origin");
1695 prog->globaloffsets.dmg_save = PRVM_ED_FindGlobalOffset("dmg_save");
1696 prog->globaloffsets.dmg_take = PRVM_ED_FindGlobalOffset("dmg_take");
1697 prog->globaloffsets.drawfont = PRVM_ED_FindGlobalOffset("drawfont");
1698 prog->globaloffsets.drawfontscale = PRVM_ED_FindGlobalOffset("drawfontscale");
1699 prog->globaloffsets.gettaginfo_forward = PRVM_ED_FindGlobalOffset("gettaginfo_forward");
1700 prog->globaloffsets.gettaginfo_name = PRVM_ED_FindGlobalOffset("gettaginfo_name");
1701 prog->globaloffsets.gettaginfo_offset = PRVM_ED_FindGlobalOffset("gettaginfo_offset");
1702 prog->globaloffsets.gettaginfo_parent = PRVM_ED_FindGlobalOffset("gettaginfo_parent");
1703 prog->globaloffsets.gettaginfo_right = PRVM_ED_FindGlobalOffset("gettaginfo_right");
1704 prog->globaloffsets.gettaginfo_up = PRVM_ED_FindGlobalOffset("gettaginfo_up");
1705 prog->globaloffsets.transparent_offset = PRVM_ED_FindGlobalOffset("transparent_offset");
1706 prog->globaloffsets.intermission = PRVM_ED_FindGlobalOffset("intermission");
1707 prog->globaloffsets.require_spawnfunc_prefix = PRVM_ED_FindGlobalOffset("require_spawnfunc_prefix");
1708 prog->globaloffsets.sb_showscores = PRVM_ED_FindGlobalOffset("sb_showscores");
1709 prog->globaloffsets.self = PRVM_ED_FindGlobalOffset("self");
1710 prog->globaloffsets.serverdeltatime = PRVM_ED_FindGlobalOffset("serverdeltatime");
1711 prog->globaloffsets.serverprevtime = PRVM_ED_FindGlobalOffset("serverprevtime");
1712 prog->globaloffsets.servertime = PRVM_ED_FindGlobalOffset("servertime");
1713 prog->globaloffsets.time = PRVM_ED_FindGlobalOffset("time");
1714 prog->globaloffsets.trace_allsolid = PRVM_ED_FindGlobalOffset("trace_allsolid");
1715 prog->globaloffsets.trace_dphitcontents = PRVM_ED_FindGlobalOffset("trace_dphitcontents");
1716 prog->globaloffsets.trace_dphitq3surfaceflags = PRVM_ED_FindGlobalOffset("trace_dphitq3surfaceflags");
1717 prog->globaloffsets.trace_dphittexturename = PRVM_ED_FindGlobalOffset("trace_dphittexturename");
1718 prog->globaloffsets.trace_dpstartcontents = PRVM_ED_FindGlobalOffset("trace_dpstartcontents");
1719 prog->globaloffsets.trace_endpos = PRVM_ED_FindGlobalOffset("trace_endpos");
1720 prog->globaloffsets.trace_ent = PRVM_ED_FindGlobalOffset("trace_ent");
1721 prog->globaloffsets.trace_fraction = PRVM_ED_FindGlobalOffset("trace_fraction");
1722 prog->globaloffsets.trace_inopen = PRVM_ED_FindGlobalOffset("trace_inopen");
1723 prog->globaloffsets.trace_inwater = PRVM_ED_FindGlobalOffset("trace_inwater");
1724 prog->globaloffsets.trace_networkentity = PRVM_ED_FindGlobalOffset("trace_networkentity");
1725 prog->globaloffsets.trace_plane_dist = PRVM_ED_FindGlobalOffset("trace_plane_dist");
1726 prog->globaloffsets.trace_plane_normal = PRVM_ED_FindGlobalOffset("trace_plane_normal");
1727 prog->globaloffsets.trace_startsolid = PRVM_ED_FindGlobalOffset("trace_startsolid");
1728 prog->globaloffsets.v_forward = PRVM_ED_FindGlobalOffset("v_forward");
1729 prog->globaloffsets.v_right = PRVM_ED_FindGlobalOffset("v_right");
1730 prog->globaloffsets.v_up = PRVM_ED_FindGlobalOffset("v_up");
1731 prog->globaloffsets.view_angles = PRVM_ED_FindGlobalOffset("view_angles");
1732 prog->globaloffsets.worldstatus = PRVM_ED_FindGlobalOffset("worldstatus");
1733 prog->globaloffsets.particles_alphamin = PRVM_ED_FindGlobalOffset("particles_alphamin");
1734 prog->globaloffsets.particles_alphamax = PRVM_ED_FindGlobalOffset("particles_alphamax");
1735 prog->globaloffsets.particles_colormin = PRVM_ED_FindGlobalOffset("particles_colormin");
1736 prog->globaloffsets.particles_colormax = PRVM_ED_FindGlobalOffset("particles_colormax");
1738 // menu qc only uses some functions, nothing else
1739 prog->funcoffsets.m_draw = PRVM_ED_FindFunctionOffset("m_draw");
1740 prog->funcoffsets.m_init = PRVM_ED_FindFunctionOffset("m_init");
1741 prog->funcoffsets.m_keydown = PRVM_ED_FindFunctionOffset("m_keydown");
1742 prog->funcoffsets.m_keyup = PRVM_ED_FindFunctionOffset("m_keyup");
1743 prog->funcoffsets.m_shutdown = PRVM_ED_FindFunctionOffset("m_shutdown");
1744 prog->funcoffsets.m_toggle = PRVM_ED_FindFunctionOffset("m_toggle");
1749 typedef struct dpfield_s
1756 #define DPFIELDS (sizeof(dpfields) / sizeof(dpfield_t))
1758 dpfield_t dpfields[] =
1769 #define PO_HASHSIZE 16384
1770 typedef struct po_string_s
1773 struct po_string_s *nextonhashchain;
1778 po_string_t *hashtable[PO_HASHSIZE];
1781 void PRVM_PO_UnparseString(char *out, const char *in, size_t outsize)
1790 case '\a': if(outsize >= 2) { *out++ = '\\'; *out++ = 'a'; outsize -= 2; } break;
1791 case '\b': if(outsize >= 2) { *out++ = '\\'; *out++ = 'b'; outsize -= 2; } break;
1792 case '\t': if(outsize >= 2) { *out++ = '\\'; *out++ = 't'; outsize -= 2; } break;
1793 case '\r': if(outsize >= 2) { *out++ = '\\'; *out++ = 'r'; outsize -= 2; } break;
1794 case '\n': if(outsize >= 2) { *out++ = '\\'; *out++ = 'n'; outsize -= 2; } break;
1795 case '\\': if(outsize >= 2) { *out++ = '\\'; *out++ = '\\'; outsize -= 2; } break;
1796 case '"': if(outsize >= 2) { *out++ = '\\'; *out++ = '"'; outsize -= 2; } break;
1798 if(*in >= 0 && *in <= 0x1F)
1803 *out++ = '0' + ((*in & 0700) >> 6);
1804 *out++ = '0' + ((*in & 0070) >> 3);
1805 *out++ = '0' + ((*in & 0007));
1822 void PRVM_PO_ParseString(char *out, const char *in, size_t outsize)
1835 case 'a': if(outsize > 0) { *out++ = '\a'; --outsize; } break;
1836 case 'b': if(outsize > 0) { *out++ = '\b'; --outsize; } break;
1837 case 't': if(outsize > 0) { *out++ = '\t'; --outsize; } break;
1838 case 'r': if(outsize > 0) { *out++ = '\r'; --outsize; } break;
1839 case 'n': if(outsize > 0) { *out++ = '\n'; --outsize; } break;
1840 case '\\': if(outsize > 0) { *out++ = '\\'; --outsize; } break;
1841 case '"': if(outsize > 0) { *out++ = '"'; --outsize; } break;
1842 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
1846 if(*in >= '0' && *in <= '7')
1849 *out = (*out << 3) | (*in - '0');
1852 if(*in >= '0' && *in <= '7')
1855 *out = (*out << 3) | (*in - '0');
1866 if(outsize > 0) { *out++ = *in; --outsize; }
1881 po_t *PRVM_PO_Load(const char *filename, mempool_t *pool)
1886 char inbuf[MAX_INPUTLINE];
1887 char decodedbuf[MAX_INPUTLINE];
1890 po_string_t thisstr;
1891 const char *buf = (const char *) FS_LoadFile(filename, pool, true, NULL);
1896 po = Mem_Alloc(pool, sizeof(*po));
1897 memset(po, 0, sizeof(*po));
1905 p = strchr(p, '\n');
1911 if(*p == '\r' || *p == '\n')
1916 if(!strncmp(p, "msgid \"", 7))
1921 else if(!strncmp(p, "msgstr \"", 8))
1928 p = strchr(p, '\n');
1938 q = strchr(p, '\n');
1945 if((size_t)(q - p) >= (size_t) sizeof(inbuf))
1947 strlcpy(inbuf, p, q - p); // not - 1, because this adds a NUL
1948 PRVM_PO_ParseString(decodedbuf + decodedpos, inbuf, sizeof(decodedbuf) - decodedpos);
1949 decodedpos += strlen(decodedbuf + decodedpos);
1959 Mem_Free(thisstr.key);
1960 thisstr.key = Mem_Alloc(pool, decodedpos + 1);
1961 memcpy(thisstr.key, decodedbuf, decodedpos + 1);
1963 else if(decodedpos > 0 && thisstr.key) // skip empty translation results
1965 thisstr.value = Mem_Alloc(pool, decodedpos + 1);
1966 memcpy(thisstr.value, decodedbuf, decodedpos + 1);
1967 hashindex = CRC_Block((const unsigned char *) thisstr.key, strlen(thisstr.key)) % PO_HASHSIZE;
1968 thisstr.nextonhashchain = po->hashtable[hashindex];
1969 po->hashtable[hashindex] = Mem_Alloc(pool, sizeof(thisstr));
1970 memcpy(po->hashtable[hashindex], &thisstr, sizeof(thisstr));
1971 memset(&thisstr, 0, sizeof(thisstr));
1975 Mem_Free((char *) buf);
1978 const char *PRVM_PO_Lookup(po_t *po, const char *str)
1980 int hashindex = CRC_Block((const unsigned char *) str, strlen(str)) % PO_HASHSIZE;
1981 po_string_t *p = po->hashtable[hashindex];
1984 if(!strcmp(str, p->key))
1986 p = p->nextonhashchain;
1990 void PRVM_PO_Destroy(po_t *po)
1993 for(i = 0; i < PO_HASHSIZE; ++i)
1995 po_string_t *p = po->hashtable[i];
1999 p = p->nextonhashchain;
2008 void PRVM_LeakTest(void);
2009 void PRVM_ResetProg(void)
2012 PRVM_GCALL(reset_cmd)();
2013 Mem_FreePool(&prog->progs_mempool);
2015 PRVM_PO_Destroy((po_t *) prog->po);
2016 memset(prog,0,sizeof(prvm_prog_t));
2017 prog->starttime = Sys_DoubleTime();
2025 void PRVM_LoadLNO( const char *progname ) {
2026 fs_offset_t filesize;
2028 unsigned int *header;
2031 FS_StripExtension( progname, filename, sizeof( filename ) );
2032 strlcat( filename, ".lno", sizeof( filename ) );
2034 lno = FS_LoadFile( filename, tempmempool, false, &filesize );
2040 <Spike> SafeWrite (h, &lnotype, sizeof(int));
2041 <Spike> SafeWrite (h, &version, sizeof(int));
2042 <Spike> SafeWrite (h, &numglobaldefs, sizeof(int));
2043 <Spike> SafeWrite (h, &numpr_globals, sizeof(int));
2044 <Spike> SafeWrite (h, &numfielddefs, sizeof(int));
2045 <Spike> SafeWrite (h, &numstatements, sizeof(int));
2046 <Spike> SafeWrite (h, statement_linenums, numstatements*sizeof(int));
2048 if( (unsigned) filesize < (6 + prog->progs->numstatements) * sizeof( int ) ) {
2053 header = (unsigned int *) lno;
2054 if( header[ 0 ] == *(unsigned int *) "LNOF" &&
2055 LittleLong( header[ 1 ] ) == 1 &&
2056 (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs->numglobaldefs &&
2057 (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs->numglobals &&
2058 (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs->numfielddefs &&
2059 (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs->numstatements )
2061 prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs->numstatements * sizeof( int ) );
2062 memcpy( prog->statement_linenums, (int *) lno + 6, prog->progs->numstatements * sizeof( int ) );
2072 void PRVM_LoadProgs (const char * filename, int numrequiredfunc, char **required_func, int numrequiredfields, prvm_required_field_t *required_field, int numrequiredglobals, char **required_global)
2076 ddef_t *infielddefs;
2077 dfunction_t *dfunctions;
2078 fs_offset_t filesize;
2080 if( prog->loaded ) {
2081 PRVM_ERROR ("PRVM_LoadProgs: there is already a %s program loaded!", PRVM_NAME );
2084 prog->progs = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
2085 if (prog->progs == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
2086 PRVM_ERROR ("PRVM_LoadProgs: couldn't load %s for %s", filename, PRVM_NAME);
2087 // TODO bounds check header fields (e.g. numstatements), they must never go behind end of file
2089 Con_DPrintf("%s programs occupy %iK.\n", PRVM_NAME, (int)(filesize/1024));
2091 prog->filecrc = CRC_Block((unsigned char *)prog->progs, filesize);
2093 // byte swap the header
2094 for (i = 0;i < (int) sizeof(*prog->progs) / 4;i++)
2095 ((int *)prog->progs)[i] = LittleLong ( ((int *)prog->progs)[i] );
2097 if (prog->progs->version != PROG_VERSION)
2098 PRVM_ERROR ("%s: %s has wrong version number (%i should be %i)", PRVM_NAME, filename, prog->progs->version, PROG_VERSION);
2099 if (prog->progs->crc != prog->headercrc && prog->progs->crc != prog->headercrc2)
2100 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);
2102 //prog->functions = (dfunction_t *)((unsigned char *)progs + progs->ofs_functions);
2103 dfunctions = (dfunction_t *)((unsigned char *)prog->progs + prog->progs->ofs_functions);
2105 if (prog->progs->ofs_strings + prog->progs->numstrings >= (int)filesize)
2106 PRVM_ERROR ("%s: %s strings go past end of file", PRVM_NAME, filename);
2107 prog->strings = (char *)prog->progs + prog->progs->ofs_strings;
2108 prog->stringssize = prog->progs->numstrings;
2110 prog->numknownstrings = 0;
2111 prog->maxknownstrings = 0;
2112 prog->knownstrings = NULL;
2113 prog->knownstrings_freeable = NULL;
2115 Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
2117 prog->globaldefs = (ddef_t *)((unsigned char *)prog->progs + prog->progs->ofs_globaldefs);
2119 // we need to expand the fielddefs list to include all the engine fields,
2120 // so allocate a new place for it
2121 infielddefs = (ddef_t *)((unsigned char *)prog->progs + prog->progs->ofs_fielddefs);
2123 prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs->numfielddefs + numrequiredfields) * sizeof(ddef_t));
2125 prog->statements = (dstatement_t *)((unsigned char *)prog->progs + prog->progs->ofs_statements);
2127 prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs->numstatements * sizeof(*prog->statement_profile));
2129 //pr_global_struct = (globalvars_t *)((unsigned char *)progs + progs->ofs_globals);
2130 prog->globals.generic = (float *)((unsigned char *)prog->progs + prog->progs->ofs_globals);
2132 // byte swap the lumps
2133 for (i=0 ; i<prog->progs->numstatements ; i++)
2135 prog->statements[i].op = LittleShort(prog->statements[i].op);
2136 prog->statements[i].a = LittleShort(prog->statements[i].a);
2137 prog->statements[i].b = LittleShort(prog->statements[i].b);
2138 prog->statements[i].c = LittleShort(prog->statements[i].c);
2141 prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs->numfunctions);
2142 for (i = 0;i < prog->progs->numfunctions;i++)
2144 prog->functions[i].first_statement = LittleLong (dfunctions[i].first_statement);
2145 prog->functions[i].parm_start = LittleLong (dfunctions[i].parm_start);
2146 prog->functions[i].s_name = LittleLong (dfunctions[i].s_name);
2147 prog->functions[i].s_file = LittleLong (dfunctions[i].s_file);
2148 prog->functions[i].numparms = LittleLong (dfunctions[i].numparms);
2149 prog->functions[i].locals = LittleLong (dfunctions[i].locals);
2150 memcpy(prog->functions[i].parm_size, dfunctions[i].parm_size, sizeof(dfunctions[i].parm_size));
2151 if(prog->functions[i].first_statement >= prog->progs->numstatements)
2152 PRVM_ERROR("PRVM_LoadProgs: out of bounds function statement (function %d) in %s", i, PRVM_NAME);
2153 // TODO bounds check parm_start, s_name, s_file, numparms, locals, parm_size
2156 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
2158 prog->globaldefs[i].type = LittleShort (prog->globaldefs[i].type);
2159 prog->globaldefs[i].ofs = LittleShort (prog->globaldefs[i].ofs);
2160 prog->globaldefs[i].s_name = LittleLong (prog->globaldefs[i].s_name);
2161 // TODO bounds check ofs, s_name
2164 // copy the progs fields to the new fields list
2165 for (i = 0;i < prog->progs->numfielddefs;i++)
2167 prog->fielddefs[i].type = LittleShort (infielddefs[i].type);
2168 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
2169 PRVM_ERROR ("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", PRVM_NAME);
2170 prog->fielddefs[i].ofs = LittleShort (infielddefs[i].ofs);
2171 prog->fielddefs[i].s_name = LittleLong (infielddefs[i].s_name);
2172 // TODO bounds check ofs, s_name
2175 // append the required fields
2176 for (i = 0;i < (int) numrequiredfields;i++)
2178 prog->fielddefs[prog->progs->numfielddefs].type = required_field[i].type;
2179 prog->fielddefs[prog->progs->numfielddefs].ofs = prog->progs->entityfields;
2180 prog->fielddefs[prog->progs->numfielddefs].s_name = PRVM_SetEngineString(required_field[i].name);
2181 // TODO bounds check ofs, s_name
2182 if (prog->fielddefs[prog->progs->numfielddefs].type == ev_vector)
2183 prog->progs->entityfields += 3;
2185 prog->progs->entityfields++;
2186 prog->progs->numfielddefs++;
2188 prog->entityfields = prog->progs->entityfields;
2190 // check required functions
2191 for(i=0 ; i < numrequiredfunc ; i++)
2192 if(PRVM_ED_FindFunction(required_func[i]) == 0)
2193 PRVM_ERROR("%s: %s not found in %s",PRVM_NAME, required_func[i], filename);
2195 // check required globals
2196 for(i=0 ; i < numrequiredglobals ; i++)
2197 if(PRVM_ED_FindGlobal(required_global[i]) == 0)
2198 PRVM_ERROR("%s: %s not found in %s",PRVM_NAME, required_global[i], filename);
2200 for (i=0 ; i<prog->progs->numglobals ; i++)
2201 ((int *)prog->globals.generic)[i] = LittleLong (((int *)prog->globals.generic)[i]);
2203 // LordHavoc: bounds check anything static
2204 for (i = 0,st = prog->statements;i < prog->progs->numstatements;i++,st++)
2210 if ((unsigned short) st->a >= prog->progs->numglobals || st->b + i < 0 || st->b + i >= prog->progs->numstatements)
2211 PRVM_ERROR("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", i, PRVM_NAME);
2214 if (st->a + i < 0 || st->a + i >= prog->progs->numstatements)
2215 PRVM_ERROR("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, PRVM_NAME);
2217 // global global global
2252 if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->b >= prog->progs->numglobals || (unsigned short) st->c >= prog->progs->numglobals)
2253 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
2255 // global none global
2261 if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->c >= prog->progs->numglobals)
2262 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
2278 if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->b >= prog->progs->numglobals)
2279 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
2293 if ((unsigned short) st->a >= prog->progs->numglobals)
2294 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
2297 Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", st->op, i, PRVM_NAME);
2301 if(prog->progs->numstatements < 1)
2303 PRVM_ERROR("PRVM_LoadProgs: empty program in %s", PRVM_NAME);
2305 else switch(prog->statements[prog->progs->numstatements - 1].op)
2312 PRVM_ERROR("PRVM_LoadProgs: program may fall off the edge (does not end with RETURN, GOTO or DONE) in %s", PRVM_NAME);
2316 PRVM_LoadLNO(filename);
2320 if(*prvm_language.string)
2321 // in CSQC we really shouldn't be able to change how stuff works... sorry for now
2322 // later idea: include a list of authorized .po file checksums with the csprogs
2324 qboolean deftrans = !!strcmp(PRVM_NAME, "client");
2325 if(deftrans) // once we have dotranslate_ strings, ALWAYS use the opt-in method!
2327 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
2330 name = PRVM_GetString(prog->globaldefs[i].s_name);
2331 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2332 if(name && !strncmp(name, "dotranslate_", 12))
2339 if(!strcmp(prvm_language.string, "dump"))
2341 qfile_t *f = FS_OpenRealFile(va("%s.%s.po", filename, prvm_language.string), "w", false);
2342 Con_Printf("Dumping to %s.%s.po\n", filename, prvm_language.string);
2345 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
2348 name = PRVM_GetString(prog->globaldefs[i].s_name);
2349 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2350 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2352 prvm_eval_t *val = (prvm_eval_t *)(prog->globals.generic + prog->globaldefs[i].ofs);
2353 const char *value = PRVM_GetString(val->string);
2356 char buf[MAX_INPUTLINE];
2357 PRVM_PO_UnparseString(buf, value, sizeof(buf));
2358 FS_Printf(f, "msgid \"%s\"\nmsgstr \"\"\n\n", buf);
2367 po_t *po = PRVM_PO_Load(va("%s.%s.po", filename, prvm_language.string), prog->progs_mempool);
2370 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
2373 name = PRVM_GetString(prog->globaldefs[i].s_name);
2374 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2375 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2377 prvm_eval_t *val = (prvm_eval_t *)(prog->globals.generic + prog->globaldefs[i].ofs);
2378 const char *value = PRVM_GetString(val->string);
2381 value = PRVM_PO_Lookup(po, value);
2383 val->string = PRVM_SetEngineString(value);
2391 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
2394 name = PRVM_GetString(prog->globaldefs[i].s_name);
2395 //Con_Printf("found var %s\n", name);
2397 && !strncmp(name, "autocvar_", 9)
2398 && !(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
2401 prvm_eval_t *val = (prvm_eval_t *)(prog->globals.generic + prog->globaldefs[i].ofs);
2402 cvar_t *cvar = Cvar_FindVar(name + 9);
2403 //Con_Printf("PRVM_LoadProgs: autocvar global %s in %s, processing...\n", name, PRVM_NAME);
2408 Con_Printf("PRVM_LoadProgs: no cvar for autocvar global %s in %s, creating...\n", name, PRVM_NAME);
2409 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2412 if((float)((int)(val->_float)) == val->_float)
2413 dpsnprintf(buf, sizeof(buf), "%i", (int)(val->_float));
2415 dpsnprintf(buf, sizeof(buf), "%.9g", val->_float);
2419 dpsnprintf(buf, sizeof(buf), "%.9g %.9g %.9g", val->vector[0], val->vector[1], val->vector[2]); value = buf;
2422 value = PRVM_GetString(val->string);
2425 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, PRVM_NAME);
2428 cvar = Cvar_Get(name + 9, value, 0, NULL);
2429 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2431 val->string = PRVM_SetEngineString(cvar->string);
2432 cvar->globaldefindex_stringno[prog - prog_list] = val->string;
2435 PRVM_ERROR("PRVM_LoadProgs: could not create cvar for autocvar global %s in %s", name, PRVM_NAME);
2436 cvar->globaldefindex_progid[prog - prog_list] = prog->id;
2437 cvar->globaldefindex[prog - prog_list] = i;
2439 else if((cvar->flags & CVAR_PRIVATE) == 0)
2441 // MUST BE SYNCED WITH cvar.c Cvar_Set
2444 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2447 val->_float = cvar->value;
2451 VectorClear(val->vector);
2452 for (j = 0;j < 3;j++)
2454 while (*s && ISWHITESPACE(*s))
2458 val->vector[j] = atof(s);
2459 while (!ISWHITESPACE(*s))
2466 val->string = PRVM_SetEngineString(cvar->string);
2467 cvar->globaldefindex_stringno[prog - prog_list] = val->string;
2470 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, PRVM_NAME);
2473 cvar->globaldefindex_progid[prog - prog_list] = prog->id;
2474 cvar->globaldefindex[prog - prog_list] = i;
2477 Con_Printf("PRVM_LoadProgs: private cvar for autocvar global %s in %s\n", name, PRVM_NAME);
2483 prog->loaded = TRUE;
2485 // set flags & ddef_ts in prog
2491 PRVM_GCALL(init_cmd)();
2498 void PRVM_Fields_f (void)
2500 int i, j, ednum, used, usedamount;
2502 char tempstring[MAX_INPUTLINE], tempstring2[260];
2512 Con_Print("no progs loaded\n");
2519 Con_Print("prvm_fields <program name>\n");
2524 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
2527 counts = (int *)Mem_Alloc(tempmempool, prog->progs->numfielddefs * sizeof(int));
2528 for (ednum = 0;ednum < prog->max_edicts;ednum++)
2530 ed = PRVM_EDICT_NUM(ednum);
2531 if (ed->priv.required->free)
2533 for (i = 1;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 v = (int *)(ed->fields.vp + d->ofs);
2540 // if the value is still all 0, skip the field
2541 for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
2554 for (i = 0;i < prog->progs->numfielddefs;i++)
2556 d = &prog->fielddefs[i];
2557 name = PRVM_GetString(d->s_name);
2558 if (name[strlen(name)-2] == '_')
2559 continue; // skip _x, _y, _z vars
2560 switch(d->type & ~DEF_SAVEGLOBAL)
2563 strlcat(tempstring, "string ", sizeof(tempstring));
2566 strlcat(tempstring, "entity ", sizeof(tempstring));
2569 strlcat(tempstring, "function ", sizeof(tempstring));
2572 strlcat(tempstring, "field ", sizeof(tempstring));
2575 strlcat(tempstring, "void ", sizeof(tempstring));
2578 strlcat(tempstring, "float ", sizeof(tempstring));
2581 strlcat(tempstring, "vector ", sizeof(tempstring));
2584 strlcat(tempstring, "pointer ", sizeof(tempstring));
2587 dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
2588 strlcat(tempstring, tempstring2, sizeof(tempstring));
2591 if (strlen(name) > sizeof(tempstring2)-4)
2593 memcpy (tempstring2, name, sizeof(tempstring2)-4);
2594 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
2595 tempstring2[sizeof(tempstring2)-1] = 0;
2598 strlcat(tempstring, name, sizeof(tempstring));
2599 for (j = (int)strlen(name);j < 25;j++)
2600 strlcat(tempstring, " ", sizeof(tempstring));
2601 dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
2602 strlcat(tempstring, tempstring2, sizeof(tempstring));
2603 strlcat(tempstring, "\n", sizeof(tempstring));
2604 if (strlen(tempstring) >= sizeof(tempstring)/2)
2606 Con_Print(tempstring);
2612 usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
2616 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);
2621 void PRVM_Globals_f (void)
2624 const char *wildcard;
2630 Con_Print("no progs loaded\n");
2633 if(Cmd_Argc () < 2 || Cmd_Argc() > 3)
2635 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
2640 if(!PRVM_SetProgFromString (Cmd_Argv (1)))
2643 if( Cmd_Argc() == 3)
2644 wildcard = Cmd_Argv(2);
2648 Con_Printf("%s :", PRVM_NAME);
2650 for (i = 0;i < prog->progs->numglobaldefs;i++)
2653 if( !matchpattern( PRVM_GetString(prog->globaldefs[i].s_name), wildcard, 1) )
2658 Con_Printf("%s\n", PRVM_GetString(prog->globaldefs[i].s_name));
2660 Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->progs->numglobals, numculled, prog->progs->numglobals * 4);
2670 void PRVM_Global_f(void)
2673 if( Cmd_Argc() != 3 ) {
2674 Con_Printf( "prvm_global <program name> <global name>\n" );
2679 if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
2682 global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
2684 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2686 Con_Printf( "%s: %s\n", Cmd_Argv(2), PRVM_ValueString( (etype_t)global->type, (prvm_eval_t *) &prog->globals.generic[ global->ofs ] ) );
2695 void PRVM_GlobalSet_f(void)
2698 if( Cmd_Argc() != 4 ) {
2699 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2704 if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
2707 global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
2709 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2711 PRVM_ED_ParseEpair( NULL, global, Cmd_Argv(3), true );
2720 void PRVM_Init (void)
2722 Cmd_AddCommand ("prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2723 Cmd_AddCommand ("prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2724 Cmd_AddCommand ("prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2725 Cmd_AddCommand ("prvm_profile", PRVM_Profile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu)");
2726 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");
2727 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)");
2728 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)");
2729 Cmd_AddCommand ("prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2730 Cmd_AddCommand ("prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2731 Cmd_AddCommand ("prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2732 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)");
2733 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");
2734 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");
2735 Cmd_AddCommand ("prvm_printfunction", PRVM_PrintFunction_f, "prints a disassembly (QuakeC instructions) of the specified function in the selected VM (server, client, menu)");
2736 Cmd_AddCommand ("cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2737 Cmd_AddCommand ("menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2738 Cmd_AddCommand ("sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2740 Cvar_RegisterVariable (&prvm_language);
2741 Cvar_RegisterVariable (&prvm_traceqc);
2742 Cvar_RegisterVariable (&prvm_statementprofiling);
2743 Cvar_RegisterVariable (&prvm_backtraceforwarnings);
2744 Cvar_RegisterVariable (&prvm_leaktest);
2745 Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames);
2746 Cvar_RegisterVariable (&prvm_errordump);
2747 Cvar_RegisterVariable (&prvm_reuseedicts_startuptime);
2748 Cvar_RegisterVariable (&prvm_reuseedicts_neverinsameframe);
2750 // COMMANDLINEOPTION: PRVM: -norunaway disables the runaway loop check (it might be impossible to exit DarkPlaces if used!)
2751 prvm_runawaycheck = !COM_CheckParm("-norunaway");
2761 void PRVM_InitProg(int prognr)
2763 static unsigned int progid = 0;
2765 if(prognr < 0 || prognr >= PRVM_MAXPROGS)
2766 Sys_Error("PRVM_InitProg: Invalid program number %i",prognr);
2768 prog = &prog_list[prognr];
2773 memset(prog, 0, sizeof(prvm_prog_t));
2774 prog->starttime = Sys_DoubleTime();
2775 prog->id = ++progid;
2777 prog->error_cmd = Host_Error;
2778 prog->leaktest_active = prvm_leaktest.integer != 0;
2781 int PRVM_GetProgNr(void)
2783 return prog - prog_list;
2786 void *_PRVM_Alloc(size_t buffersize, const char *filename, int fileline)
2788 return _Mem_Alloc(prog->progs_mempool, NULL, buffersize, 16, filename, fileline);
2791 void _PRVM_Free(void *buffer, const char *filename, int fileline)
2793 _Mem_Free(buffer, filename, fileline);
2796 void _PRVM_FreeAll(const char *filename, int fileline)
2799 prog->fielddefs = NULL;
2800 prog->functions = NULL;
2801 _Mem_EmptyPool(prog->progs_mempool, filename, fileline);
2804 // LordHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
2805 unsigned int PRVM_EDICT_NUM_ERROR(unsigned int n, char *filename, int fileline)
2807 PRVM_ERROR ("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", PRVM_NAME, n, filename, fileline);
2811 sizebuf_t vm_tempstringsbuf;
2812 #define PRVM_KNOWNSTRINGBASE 0x40000000
2814 const char *PRVM_GetString(int num)
2819 VM_Warning("PRVM_GetString: Invalid string offset (%i < 0)\n", num);
2822 else if (num < prog->stringssize)
2824 // constant string from progs.dat
2825 return prog->strings + num;
2827 else if (num <= prog->stringssize + vm_tempstringsbuf.maxsize)
2829 // tempstring returned by engine to QC (becomes invalid after returning to engine)
2830 num -= prog->stringssize;
2831 if (num < vm_tempstringsbuf.cursize)
2832 return (char *)vm_tempstringsbuf.data + num;
2835 VM_Warning("PRVM_GetString: Invalid temp-string offset (%i >= %i vm_tempstringsbuf.cursize)\n", num, vm_tempstringsbuf.cursize);
2839 else if (num & PRVM_KNOWNSTRINGBASE)
2842 num = num - PRVM_KNOWNSTRINGBASE;
2843 if (num >= 0 && num < prog->numknownstrings)
2845 if (!prog->knownstrings[num])
2847 VM_Warning("PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
2850 return prog->knownstrings[num];
2854 VM_Warning("PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
2860 // invalid string offset
2861 VM_Warning("PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
2866 const char *PRVM_ChangeEngineString(int i, const char *s)
2869 i = i - PRVM_KNOWNSTRINGBASE;
2870 if(i < 0 || i >= prog->numknownstrings)
2871 PRVM_ERROR("PRVM_ChangeEngineString: s is not an engine string");
2872 old = prog->knownstrings[i];
2873 prog->knownstrings[i] = s;
2877 int PRVM_SetEngineString(const char *s)
2882 if (s >= prog->strings && s <= prog->strings + prog->stringssize)
2883 PRVM_ERROR("PRVM_SetEngineString: s in prog->strings area");
2884 // if it's in the tempstrings area, use a reserved range
2885 // (otherwise we'd get millions of useless string offsets cluttering the database)
2886 if (s >= (char *)vm_tempstringsbuf.data && s < (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.maxsize)
2888 return prog->stringssize + (s - (char *)vm_tempstringsbuf.data);
2890 // see if it's a known string address
2891 for (i = 0;i < prog->numknownstrings;i++)
2892 if (prog->knownstrings[i] == s)
2893 return PRVM_KNOWNSTRINGBASE + i;
2894 // new unknown engine string
2895 if (developer_insane.integer)
2896 Con_DPrintf("new engine string %p = \"%s\"\n", s, s);
2897 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2898 if (!prog->knownstrings[i])
2900 if (i >= prog->numknownstrings)
2902 if (i >= prog->maxknownstrings)
2904 const char **oldstrings = prog->knownstrings;
2905 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2906 const char **oldstrings_origin = prog->knownstrings_origin;
2907 prog->maxknownstrings += 128;
2908 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2909 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2910 if(prog->leaktest_active)
2911 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2912 if (prog->numknownstrings)
2914 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2915 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2916 if(prog->leaktest_active)
2917 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
2920 prog->numknownstrings++;
2922 prog->firstfreeknownstring = i + 1;
2923 prog->knownstrings[i] = s;
2924 prog->knownstrings_freeable[i] = false;
2925 if(prog->leaktest_active)
2926 prog->knownstrings_origin[i] = NULL;
2927 return PRVM_KNOWNSTRINGBASE + i;
2930 // temp string handling
2932 // all tempstrings go into this buffer consecutively, and it is reset
2933 // whenever PRVM_ExecuteProgram returns to the engine
2934 // (technically each PRVM_ExecuteProgram call saves the cursize value and
2935 // restores it on return, so multiple recursive calls can share the same
2937 // the buffer size is automatically grown as needed
2939 int PRVM_SetTempString(const char *s)
2945 size = (int)strlen(s) + 1;
2946 if (developer_insane.integer)
2947 Con_DPrintf("PRVM_SetTempString: cursize %i, size %i\n", vm_tempstringsbuf.cursize, size);
2948 if (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
2950 sizebuf_t old = vm_tempstringsbuf;
2951 if (vm_tempstringsbuf.cursize + size >= 1<<28)
2952 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);
2953 vm_tempstringsbuf.maxsize = max(vm_tempstringsbuf.maxsize, 65536);
2954 while (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
2955 vm_tempstringsbuf.maxsize *= 2;
2956 if (vm_tempstringsbuf.maxsize != old.maxsize || vm_tempstringsbuf.data == NULL)
2958 Con_DPrintf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, vm_tempstringsbuf.maxsize/1024);
2959 vm_tempstringsbuf.data = (unsigned char *) Mem_Alloc(sv_mempool, vm_tempstringsbuf.maxsize);
2961 memcpy(vm_tempstringsbuf.data, old.data, old.cursize);
2966 t = (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.cursize;
2968 vm_tempstringsbuf.cursize += size;
2969 return PRVM_SetEngineString(t);
2972 int PRVM_AllocString(size_t bufferlength, char **pointer)
2977 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2978 if (!prog->knownstrings[i])
2980 if (i >= prog->numknownstrings)
2982 if (i >= prog->maxknownstrings)
2984 const char **oldstrings = prog->knownstrings;
2985 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2986 const char **oldstrings_origin = prog->knownstrings_origin;
2987 prog->maxknownstrings += 128;
2988 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2989 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2990 if(prog->leaktest_active)
2991 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2992 if (prog->numknownstrings)
2994 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2995 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2996 if(prog->leaktest_active)
2997 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
3000 Mem_Free((char **)oldstrings);
3001 if (oldstrings_freeable)
3002 Mem_Free((unsigned char *)oldstrings_freeable);
3003 if (oldstrings_origin)
3004 Mem_Free((char **)oldstrings_origin);
3006 prog->numknownstrings++;
3008 prog->firstfreeknownstring = i + 1;
3009 prog->knownstrings[i] = (char *)PRVM_Alloc(bufferlength);
3010 prog->knownstrings_freeable[i] = true;
3011 if(prog->leaktest_active)
3012 prog->knownstrings_origin[i] = PRVM_AllocationOrigin();
3014 *pointer = (char *)(prog->knownstrings[i]);
3015 return PRVM_KNOWNSTRINGBASE + i;
3018 void PRVM_FreeString(int num)
3021 PRVM_ERROR("PRVM_FreeString: attempt to free a NULL string");
3022 else if (num >= 0 && num < prog->stringssize)
3023 PRVM_ERROR("PRVM_FreeString: attempt to free a constant string");
3024 else if (num >= PRVM_KNOWNSTRINGBASE && num < PRVM_KNOWNSTRINGBASE + prog->numknownstrings)
3026 num = num - PRVM_KNOWNSTRINGBASE;
3027 if (!prog->knownstrings[num])
3028 PRVM_ERROR("PRVM_FreeString: attempt to free a non-existent or already freed string");
3029 if (!prog->knownstrings_freeable[num])
3030 PRVM_ERROR("PRVM_FreeString: attempt to free a string owned by the engine");
3031 PRVM_Free((char *)prog->knownstrings[num]);
3032 if(prog->leaktest_active)
3033 if(prog->knownstrings_origin[num])
3034 PRVM_Free((char *)prog->knownstrings_origin[num]);
3035 prog->knownstrings[num] = NULL;
3036 prog->knownstrings_freeable[num] = false;
3037 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
3040 PRVM_ERROR("PRVM_FreeString: invalid string offset %i", num);
3043 static qboolean PRVM_IsStringReferenced(string_t string)
3047 for (i = 0;i < prog->progs->numglobaldefs;i++)
3049 ddef_t *d = &prog->globaldefs[i];
3050 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3052 if(string == ((prvm_eval_t *) &prog->globals.generic[d->ofs])->string)
3056 for(j = 0; j < prog->num_edicts; ++j)
3058 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3059 if (ed->priv.required->free)
3061 for (i=0; i<prog->progs->numfielddefs; ++i)
3063 ddef_t *d = &prog->fielddefs[i];
3064 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3066 if(string == ((prvm_eval_t *) &ed->fields.vp[d->ofs])->string)
3074 static qboolean PRVM_IsEdictRelevant(prvm_edict_t *edict)
3076 if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
3077 return true; // world or clients
3078 switch(prog - prog_list)
3080 case PRVM_SERVERPROG:
3082 entvars_t *ev = edict->fields.server;
3083 if(ev->solid) // can block other stuff, or is a trigger?
3085 if(ev->modelindex) // visible ent?
3087 if(ev->effects) // particle effect?
3089 if(ev->think) // has a think function?
3090 if(ev->nextthink > 0) // that actually will eventually run?
3094 if(*prvm_leaktest_ignore_classnames.string)
3096 if(strstr(va(" %s ", prvm_leaktest_ignore_classnames.string), va(" %s ", PRVM_GetString(ev->classname))))
3101 case PRVM_CLIENTPROG:
3103 // TODO someone add more stuff here
3104 cl_entvars_t *ev = edict->fields.client;
3105 if(ev->entnum) // csqc networked
3107 if(ev->modelindex) // visible ent?
3109 if(ev->effects) // particle effect?
3111 if(ev->think) // has a think function?
3112 if(ev->nextthink > 0) // that actually will eventually run?
3114 if(*prvm_leaktest_ignore_classnames.string)
3116 if(strstr(va(" %s ", prvm_leaktest_ignore_classnames.string), va(" %s ", PRVM_GetString(ev->classname))))
3122 // menu prog does not have classnames
3128 static qboolean PRVM_IsEdictReferenced(prvm_edict_t *edict, int mark)
3131 int edictnum = PRVM_NUM_FOR_EDICT(edict);
3132 const char *targetname = NULL;
3134 switch(prog - prog_list)
3136 case PRVM_SERVERPROG:
3137 targetname = PRVM_GetString(edict->fields.server->targetname);
3142 if(!*targetname) // ""
3145 for (i = 0;i < prog->progs->numglobaldefs;i++)
3147 ddef_t *d = &prog->globaldefs[i];
3148 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3150 if(edictnum == ((prvm_eval_t *) &prog->globals.generic[d->ofs])->edict)
3154 for(j = 0; j < prog->num_edicts; ++j)
3156 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3157 if (ed->priv.required->mark < mark)
3163 const char *target = PRVM_GetString(ed->fields.server->target);
3165 if(!strcmp(target, targetname))
3168 for (i=0; i<prog->progs->numfielddefs; ++i)
3170 ddef_t *d = &prog->fielddefs[i];
3171 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3173 if(edictnum == ((prvm_eval_t *) &ed->fields.vp[d->ofs])->edict)
3181 static void PRVM_MarkReferencedEdicts(void)
3187 for(j = 0; j < prog->num_edicts; ++j)
3189 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3190 if(ed->priv.required->free)
3192 ed->priv.required->mark = PRVM_IsEdictRelevant(ed) ? 1 : 0;
3199 for(j = 0; j < prog->num_edicts; ++j)
3201 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3202 if(ed->priv.required->free)
3204 if(ed->priv.required->mark)
3206 if(PRVM_IsEdictReferenced(ed, stage))
3208 ed->priv.required->mark = stage + 1;
3215 Con_DPrintf("leak check used %d stages to find all references\n", stage);
3218 void PRVM_LeakTest(void)
3221 qboolean leaked = false;
3223 if(!prog->leaktest_active)
3227 for (i = 0; i < prog->numknownstrings; ++i)
3229 if(prog->knownstrings[i])
3230 if(prog->knownstrings_freeable[i])
3231 if(prog->knownstrings_origin[i])
3232 if(!PRVM_IsStringReferenced(-1 - i))
3234 Con_Printf("Unreferenced string found!\n Value: %s\n Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
3240 PRVM_MarkReferencedEdicts();
3241 for(j = 0; j < prog->num_edicts; ++j)
3243 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3244 if(ed->priv.required->free)
3246 if(!ed->priv.required->mark)
3247 if(ed->priv.required->allocation_origin)
3249 Con_Printf("Unreferenced edict found!\n Allocated at: %s\n", ed->priv.required->allocation_origin);
3250 PRVM_ED_Print(ed, NULL);
3256 for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
3258 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
3260 if(stringbuffer->origin)
3262 Con_Printf("Open string buffer handle found!\n Allocated at: %s\n", stringbuffer->origin);
3267 for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
3269 if(prog->openfiles[i])
3270 if(prog->openfiles_origin[i])
3272 Con_Printf("Open file handle found!\n Allocated at: %s\n", prog->openfiles_origin[i]);
3277 for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
3279 if(prog->opensearches[i])
3280 if(prog->opensearches_origin[i])
3282 Con_Printf("Open search handle found!\n Allocated at: %s\n", prog->opensearches_origin[i]);
3288 Con_Printf("Congratulations. No leaks found.\n");