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 = {CVAR_SAVE, "prvm_language", "", "when set, loads progs.dat.LANGUAGENAME.po for string translations; when set to dump, progs.dat.pot 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_timeprofiling = {0, "prvm_timeprofiling", "0", "counts how long each function has been executed, these counts are displayed in prvm_profile output (if enabled)"};
40 cvar_t prvm_backtraceforwarnings = {0, "prvm_backtraceforwarnings", "0", "print a backtrace for warnings too"};
41 cvar_t prvm_leaktest = {0, "prvm_leaktest", "0", "try to detect memory leaks in strings or entities"};
42 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)"};
43 cvar_t prvm_errordump = {0, "prvm_errordump", "0", "write a savegame on crash to crash-server.dmp"};
44 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)"};
45 cvar_t prvm_reuseedicts_neverinsameframe = {0, "prvm_reuseedicts_neverinsameframe", "1", "never allows re-use of freed entity slots during same frame"};
47 static double prvm_reuseedicts_always_allow = 0;
48 qboolean prvm_runawaycheck = true;
50 extern sizebuf_t vm_tempstringsbuf;
52 //============================================================================
60 void PRVM_MEM_Alloc(void)
64 // reserve space for the null entity aka world
65 // check bound of max_edicts
66 prog->max_edicts = bound(1 + prog->reserved_edicts, prog->max_edicts, prog->limit_edicts);
67 prog->num_edicts = bound(1 + prog->reserved_edicts, prog->num_edicts, prog->max_edicts);
69 // edictprivate_size has to be min as big prvm_edict_private_t
70 prog->edictprivate_size = max(prog->edictprivate_size,(int)sizeof(prvm_edict_private_t));
73 prog->edicts = (prvm_edict_t *)Mem_Alloc(prog->progs_mempool,prog->limit_edicts * sizeof(prvm_edict_t));
75 // alloc edict private space
76 prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size);
79 prog->entityfieldsarea = prog->entityfields * prog->max_edicts;
80 prog->edictsfields = (vec_t *)Mem_Alloc(prog->progs_mempool, prog->entityfieldsarea * sizeof(vec_t));
83 for(i = 0; i < prog->max_edicts; i++)
85 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
86 prog->edicts[i].fields.vp = prog->edictsfields + i * prog->entityfields;
92 PRVM_MEM_IncreaseEdicts
95 void PRVM_MEM_IncreaseEdicts(void)
99 if(prog->max_edicts >= prog->limit_edicts)
102 PRVM_GCALL(begin_increase_edicts)();
105 prog->max_edicts = min(prog->max_edicts + 256, prog->limit_edicts);
107 prog->entityfieldsarea = prog->entityfields * prog->max_edicts;
108 prog->edictsfields = (vec_t*)Mem_Realloc(prog->progs_mempool, (void *)prog->edictsfields, prog->entityfieldsarea * sizeof(vec_t));
109 prog->edictprivate = (void *)Mem_Realloc(prog->progs_mempool, (void *)prog->edictprivate, prog->max_edicts * prog->edictprivate_size);
111 //set e and v pointers
112 for(i = 0; i < prog->max_edicts; i++)
114 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
115 prog->edicts[i].fields.vp = prog->edictsfields + i * prog->entityfields;
118 PRVM_GCALL(end_increase_edicts)();
121 //============================================================================
124 int PRVM_ED_FindFieldOffset(const char *field)
127 d = PRVM_ED_FindField(field);
133 int PRVM_ED_FindGlobalOffset(const char *global)
136 d = PRVM_ED_FindGlobal(global);
142 func_t PRVM_ED_FindFunctionOffset(const char *function)
145 f = PRVM_ED_FindFunction(function);
148 return (func_t)(f - prog->functions);
151 qboolean PRVM_ProgLoaded(int prognr)
153 if(prognr < 0 || prognr >= PRVM_MAXPROGS)
156 return (prog_list[prognr].loaded ? TRUE : FALSE);
161 PRVM_SetProgFromString
164 // perhaps add a return value when the str doesnt exist
165 qboolean PRVM_SetProgFromString(const char *str)
168 for(; i < PRVM_MAXPROGS ; i++)
169 if(prog_list[i].name && !strcmp(prog_list[i].name,str))
171 if(prog_list[i].loaded)
173 prog = &prog_list[i];
178 Con_Printf("%s not loaded !\n",PRVM_NAME);
183 Con_Printf("Invalid program name %s !\n", str);
192 void PRVM_SetProg(int prognr)
194 if(0 <= prognr && prognr < PRVM_MAXPROGS)
196 if(prog_list[prognr].loaded)
197 prog = &prog_list[prognr];
199 PRVM_ERROR("%i not loaded !", prognr);
202 PRVM_ERROR("Invalid program number %i", prognr);
209 Sets everything to NULL
212 void PRVM_ED_ClearEdict (prvm_edict_t *e)
214 memset (e->fields.vp, 0, prog->progs->entityfields * 4);
215 e->priv.required->free = false;
217 // AK: Let the init_edict function determine if something needs to be initialized
218 PRVM_GCALL(init_edict)(e);
221 const char *PRVM_AllocationOrigin(void)
224 if(prog->leaktest_active)
225 if(prog->depth > 0) // actually in QC code and not just parsing the entities block of a map/savegame
227 buf = (char *)PRVM_Alloc(128);
228 PRVM_ShortStackTrace(buf, 128);
237 Returns if this particular edict could get allocated by PRVM_ED_Alloc
240 qboolean PRVM_ED_CanAlloc(prvm_edict_t *e)
242 if(!e->priv.required->free)
244 if(prvm_reuseedicts_always_allow == realtime)
246 if(realtime <= e->priv.required->freetime && prvm_reuseedicts_neverinsameframe.integer)
247 return false; // never allow reuse in same frame (causes networking trouble)
248 if(e->priv.required->freetime < prog->starttime + prvm_reuseedicts_startuptime.value)
250 if(realtime > e->priv.required->freetime + 1)
252 return false; // entity slot still blocked because the entity was freed less than one second ago
259 Either finds a free edict, or allocates a new one.
260 Try to avoid reusing an entity that was recently freed, because it
261 can cause the client to think the entity morphed into something else
262 instead of being removed and recreated, which can cause interpolated
263 angles and bad trails.
266 prvm_edict_t *PRVM_ED_Alloc (void)
271 // the client qc dont need maxclients
272 // thus it doesnt need to use svs.maxclients
273 // AK: changed i=svs.maxclients+1
274 // AK: changed so the edict 0 wont spawn -> used as reserved/world entity
275 // although the menu/client has no world
276 for (i = prog->reserved_edicts + 1;i < prog->num_edicts;i++)
278 e = PRVM_EDICT_NUM(i);
279 if(PRVM_ED_CanAlloc(e))
281 PRVM_ED_ClearEdict (e);
282 e->priv.required->allocation_origin = PRVM_AllocationOrigin();
287 if (i == prog->limit_edicts)
288 PRVM_ERROR ("%s: PRVM_ED_Alloc: no free edicts",PRVM_NAME);
291 if (prog->num_edicts >= prog->max_edicts)
292 PRVM_MEM_IncreaseEdicts();
294 e = PRVM_EDICT_NUM(i);
295 PRVM_ED_ClearEdict (e);
297 e->priv.required->allocation_origin = PRVM_AllocationOrigin();
306 Marks the edict as free
307 FIXME: walk all entities and NULL out references to this entity
310 void PRVM_ED_Free (prvm_edict_t *ed)
312 // dont delete the null entity (world) or reserved edicts
313 if(PRVM_NUM_FOR_EDICT(ed) <= prog->reserved_edicts )
316 PRVM_GCALL(free_edict)(ed);
318 ed->priv.required->free = true;
319 ed->priv.required->freetime = realtime;
320 if(ed->priv.required->allocation_origin)
322 PRVM_Free((char *)ed->priv.required->allocation_origin);
323 ed->priv.required->allocation_origin = NULL;
327 //===========================================================================
334 ddef_t *PRVM_ED_GlobalAtOfs (int ofs)
339 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
341 def = &prog->globaldefs[i];
353 ddef_t *PRVM_ED_FieldAtOfs (int ofs)
358 for (i=0 ; i<prog->progs->numfielddefs ; i++)
360 def = &prog->fielddefs[i];
372 ddef_t *PRVM_ED_FindField (const char *name)
377 for (i=0 ; i<prog->progs->numfielddefs ; i++)
379 def = &prog->fielddefs[i];
380 if (!strcmp(PRVM_GetString(def->s_name), name))
391 ddef_t *PRVM_ED_FindGlobal (const char *name)
396 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
398 def = &prog->globaldefs[i];
399 if (!strcmp(PRVM_GetString(def->s_name), name))
411 mfunction_t *PRVM_ED_FindFunction (const char *name)
416 for (i=0 ; i<prog->progs->numfunctions ; i++)
418 func = &prog->functions[i];
419 if (!strcmp(PRVM_GetString(func->s_name), name))
430 Returns a string describing *data in a type specific manner
433 char *PRVM_ValueString (etype_t type, prvm_eval_t *val)
435 static char line[MAX_INPUTLINE];
440 type = (etype_t)((int) type & ~DEF_SAVEGLOBAL);
445 strlcpy (line, PRVM_GetString (val->string), sizeof (line));
449 if (n < 0 || n >= prog->max_edicts)
450 dpsnprintf (line, sizeof(line), "entity %i (invalid!)", n);
452 dpsnprintf (line, sizeof(line), "entity %i", n);
455 f = prog->functions + val->function;
456 dpsnprintf (line, sizeof(line), "%s()", PRVM_GetString(f->s_name));
459 def = PRVM_ED_FieldAtOfs ( val->_int );
460 dpsnprintf (line, sizeof(line), ".%s", PRVM_GetString(def->s_name));
463 dpsnprintf (line, sizeof(line), "void");
466 // LordHavoc: changed from %5.1f to %10.4f
467 dpsnprintf (line, sizeof(line), "%10.4f", val->_float);
470 // LordHavoc: changed from %5.1f to %10.4f
471 dpsnprintf (line, sizeof(line), "'%10.4f %10.4f %10.4f'", val->vector[0], val->vector[1], val->vector[2]);
474 dpsnprintf (line, sizeof(line), "pointer");
477 dpsnprintf (line, sizeof(line), "bad type %i", (int) type);
488 Returns a string describing *data in a type specific manner
489 Easier to parse than PR_ValueString
492 char *PRVM_UglyValueString (etype_t type, prvm_eval_t *val)
494 static char line[MAX_INPUTLINE];
500 type = (etype_t)((int)type & ~DEF_SAVEGLOBAL);
505 // Parse the string a bit to turn special characters
506 // (like newline, specifically) into escape codes,
507 // this fixes saving games from various mods
508 s = PRVM_GetString (val->string);
509 for (i = 0;i < (int)sizeof(line) - 2 && *s;)
538 dpsnprintf (line, sizeof (line), "%i", PRVM_NUM_FOR_EDICT(PRVM_PROG_TO_EDICT(val->edict)));
541 f = prog->functions + val->function;
542 strlcpy (line, PRVM_GetString (f->s_name), sizeof (line));
545 def = PRVM_ED_FieldAtOfs ( val->_int );
546 dpsnprintf (line, sizeof (line), ".%s", PRVM_GetString(def->s_name));
549 dpsnprintf (line, sizeof (line), "void");
552 dpsnprintf (line, sizeof (line), "%.9g", val->_float);
555 dpsnprintf (line, sizeof (line), "%.9g %.9g %.9g", val->vector[0], val->vector[1], val->vector[2]);
558 dpsnprintf (line, sizeof (line), "bad type %i", type);
569 Returns a string with a description and the contents of a global,
570 padded to 20 field width
573 char *PRVM_GlobalString (int ofs)
579 static char line[128];
581 val = (void *)&prog->globals.generic[ofs];
582 def = PRVM_ED_GlobalAtOfs(ofs);
584 dpsnprintf (line, sizeof(line), "GLOBAL%i", ofs);
587 s = PRVM_ValueString ((etype_t)def->type, (prvm_eval_t *)val);
588 dpsnprintf (line, sizeof(line), "%s (=%s)", PRVM_GetString(def->s_name), s);
592 //for ( ; i<20 ; i++)
593 // strcat (line," ");
599 char *PRVM_GlobalStringNoContents (int ofs)
603 static char line[128];
605 def = PRVM_ED_GlobalAtOfs(ofs);
607 dpsnprintf (line, sizeof(line), "GLOBAL%i", ofs);
609 dpsnprintf (line, sizeof(line), "%s", PRVM_GetString(def->s_name));
612 //for ( ; i<20 ; i++)
613 // strcat (line," ");
627 // LordHavoc: optimized this to print out much more quickly (tempstring)
628 // LordHavoc: changed to print out every 4096 characters (incase there are a lot of fields to print)
629 void PRVM_ED_Print(prvm_edict_t *ed, const char *wildcard_fieldname)
637 char tempstring[MAX_INPUTLINE], tempstring2[260]; // temporary string buffers
639 if (ed->priv.required->free)
641 Con_Printf("%s: FREE\n",PRVM_NAME);
646 dpsnprintf(tempstring, sizeof(tempstring), "\n%s EDICT %i:\n", PRVM_NAME, PRVM_NUM_FOR_EDICT(ed));
647 for (i=1 ; i<prog->progs->numfielddefs ; i++)
649 d = &prog->fielddefs[i];
650 name = PRVM_GetString(d->s_name);
651 if(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
652 continue; // skip _x, _y, _z vars
654 // Check Field Name Wildcard
655 if(wildcard_fieldname)
656 if( !matchpattern(name, wildcard_fieldname, 1) )
657 // Didn't match; skip
660 v = (int *)(ed->fields.vp + d->ofs);
662 // if the value is still all 0, skip the field
663 type = d->type & ~DEF_SAVEGLOBAL;
665 for (j=0 ; j<prvm_type_size[type] ; j++)
668 if (j == prvm_type_size[type])
671 if (strlen(name) > sizeof(tempstring2)-4)
673 memcpy (tempstring2, name, sizeof(tempstring2)-4);
674 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
675 tempstring2[sizeof(tempstring2)-1] = 0;
678 strlcat(tempstring, name, sizeof(tempstring));
679 for (l = strlen(name);l < 14;l++)
680 strlcat(tempstring, " ", sizeof(tempstring));
681 strlcat(tempstring, " ", sizeof(tempstring));
683 name = PRVM_ValueString((etype_t)d->type, (prvm_eval_t *)v);
684 if (strlen(name) > sizeof(tempstring2)-4)
686 memcpy (tempstring2, name, sizeof(tempstring2)-4);
687 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
688 tempstring2[sizeof(tempstring2)-1] = 0;
691 strlcat(tempstring, name, sizeof(tempstring));
692 strlcat(tempstring, "\n", sizeof(tempstring));
693 if (strlen(tempstring) >= sizeof(tempstring)/2)
695 Con_Print(tempstring);
700 Con_Print(tempstring);
710 extern cvar_t developer_entityparsing;
711 void PRVM_ED_Write (qfile_t *f, prvm_edict_t *ed)
721 if (ed->priv.required->free)
727 for (i=1 ; i<prog->progs->numfielddefs ; i++)
729 d = &prog->fielddefs[i];
730 name = PRVM_GetString(d->s_name);
732 if(developer_entityparsing.integer)
733 Con_Printf("PRVM_ED_Write: at entity %d field %s\n", PRVM_NUM_FOR_EDICT(ed), name);
735 //if(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
736 if(strlen(name) > 1 && name[strlen(name)-2] == '_')
737 continue; // skip _x, _y, _z vars, and ALSO other _? vars as some mods expect them to be never saved (TODO: a gameplayfix for using the "more precise" condition above?)
739 v = (int *)(ed->fields.vp + d->ofs);
741 // if the value is still all 0, skip the field
742 type = d->type & ~DEF_SAVEGLOBAL;
743 for (j=0 ; j<prvm_type_size[type] ; j++)
746 if (j == prvm_type_size[type])
749 FS_Printf(f,"\"%s\" ",name);
750 prog->statestring = va("PRVM_ED_Write, ent=%d, name=%s", i, name);
751 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString((etype_t)d->type, (prvm_eval_t *)v));
752 prog->statestring = NULL;
758 void PRVM_ED_PrintNum (int ent, const char *wildcard_fieldname)
760 PRVM_ED_Print(PRVM_EDICT_NUM(ent), wildcard_fieldname);
765 PRVM_ED_PrintEdicts_f
767 For debugging, prints all the entities in the current server
770 void PRVM_ED_PrintEdicts_f (void)
773 const char *wildcard_fieldname;
775 if(Cmd_Argc() < 2 || Cmd_Argc() > 3)
777 Con_Print("prvm_edicts <program name> <optional field name wildcard>\n");
782 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
786 wildcard_fieldname = Cmd_Argv(2);
788 wildcard_fieldname = NULL;
790 Con_Printf("%s: %i entities\n", PRVM_NAME, prog->num_edicts);
791 for (i=0 ; i<prog->num_edicts ; i++)
792 PRVM_ED_PrintNum (i, wildcard_fieldname);
801 For debugging, prints a single edict
804 void PRVM_ED_PrintEdict_f (void)
807 const char *wildcard_fieldname;
809 if(Cmd_Argc() < 3 || Cmd_Argc() > 4)
811 Con_Print("prvm_edict <program name> <edict number> <optional field name wildcard>\n");
816 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
819 i = atoi (Cmd_Argv(2));
820 if (i >= prog->num_edicts)
822 Con_Print("Bad edict number\n");
827 // Optional Wildcard Provided
828 wildcard_fieldname = Cmd_Argv(3);
831 wildcard_fieldname = NULL;
832 PRVM_ED_PrintNum (i, wildcard_fieldname);
844 // 2 possibilities : 1. just displaying the active edict count
845 // 2. making a function pointer [x]
846 void PRVM_ED_Count_f (void)
854 Con_Print("prvm_count <program name>\n");
859 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
862 if(prog->count_edicts)
863 prog->count_edicts();
867 for (i=0 ; i<prog->num_edicts ; i++)
869 ent = PRVM_EDICT_NUM(i);
870 if (ent->priv.required->free)
875 Con_Printf("num_edicts:%3i\n", prog->num_edicts);
876 Con_Printf("active :%3i\n", active);
883 ==============================================================================
887 FIXME: need to tag constants, doesn't really work
888 ==============================================================================
896 void PRVM_ED_WriteGlobals (qfile_t *f)
904 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
906 def = &prog->globaldefs[i];
908 if ( !(def->type & DEF_SAVEGLOBAL) )
910 type &= ~DEF_SAVEGLOBAL;
912 if (type != ev_string && type != ev_float && type != ev_entity)
915 name = PRVM_GetString(def->s_name);
917 if(developer_entityparsing.integer)
918 Con_Printf("PRVM_ED_WriteGlobals: at global %s\n", name);
920 prog->statestring = va("PRVM_ED_WriteGlobals, name=%s", name);
921 FS_Printf(f,"\"%s\" ", name);
922 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString((etype_t)type, (prvm_eval_t *)&prog->globals.generic[def->ofs]));
923 prog->statestring = NULL;
933 void PRVM_ED_ParseGlobals (const char *data)
935 char keyname[MAX_INPUTLINE];
941 if (!COM_ParseToken_Simple(&data, false, false))
942 PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace");
943 if (com_token[0] == '}')
946 if (developer_entityparsing.integer)
947 Con_Printf("Key: \"%s\"", com_token);
949 strlcpy (keyname, com_token, sizeof(keyname));
952 if (!COM_ParseToken_Simple(&data, false, true))
953 PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace");
955 if (developer_entityparsing.integer)
956 Con_Printf(" \"%s\"\n", com_token);
958 if (com_token[0] == '}')
959 PRVM_ERROR ("PRVM_ED_ParseGlobals: closing brace without data");
961 key = PRVM_ED_FindGlobal (keyname);
964 Con_DPrintf("'%s' is not a global on %s\n", keyname, PRVM_NAME);
968 if (!PRVM_ED_ParseEpair(NULL, key, com_token, true))
969 PRVM_ERROR ("PRVM_ED_ParseGlobals: parse error");
973 //============================================================================
980 Can parse either fields or globals
981 returns false if error
984 qboolean PRVM_ED_ParseEpair(prvm_edict_t *ent, ddef_t *key, const char *s, qboolean parsebackslash)
993 val = (prvm_eval_t *)(ent->fields.vp + key->ofs);
995 val = (prvm_eval_t *)(prog->globals.generic + key->ofs);
996 switch (key->type & ~DEF_SAVEGLOBAL)
999 l = (int)strlen(s) + 1;
1000 val->string = PRVM_AllocString(l, &new_p);
1001 for (i = 0;i < l;i++)
1003 if (s[i] == '\\' && s[i+1] && parsebackslash)
1008 else if (s[i] == 'r')
1019 while (*s && ISWHITESPACE(*s))
1021 val->_float = atof(s);
1025 for (i = 0;i < 3;i++)
1027 while (*s && ISWHITESPACE(*s))
1031 val->vector[i] = atof(s);
1032 while (!ISWHITESPACE(*s))
1040 while (*s && ISWHITESPACE(*s))
1043 if (i >= prog->limit_edicts)
1044 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);
1045 while (i >= prog->max_edicts)
1046 PRVM_MEM_IncreaseEdicts();
1047 // if IncreaseEdicts was called the base pointer needs to be updated
1049 val = (prvm_eval_t *)(ent->fields.vp + key->ofs);
1050 val->edict = PRVM_EDICT_TO_PROG(PRVM_EDICT_NUM((int)i));
1056 Con_DPrintf("PRVM_ED_ParseEpair: Bogus field name %s in %s\n", s, PRVM_NAME);
1059 def = PRVM_ED_FindField(s + 1);
1062 Con_DPrintf("PRVM_ED_ParseEpair: Can't find field %s in %s\n", s, PRVM_NAME);
1065 val->_int = def->ofs;
1069 func = PRVM_ED_FindFunction(s);
1072 Con_Printf("PRVM_ED_ParseEpair: Can't find function %s in %s\n", s, PRVM_NAME);
1075 val->function = func - prog->functions;
1079 Con_Printf("PRVM_ED_ParseEpair: Unknown key->type %i for key \"%s\" on %s\n", key->type, PRVM_GetString(key->s_name), PRVM_NAME);
1089 Console command to send a string to QC function GameCommand of the
1093 sv_cmd adminmsg 3 "do not teamkill"
1094 cl_cmd someclientcommand
1095 menu_cmd somemenucommand
1097 All progs can support this extension; sg calls it in server QC, cg in client
1101 void PRVM_GameCommand(const char *whichprogs, const char *whichcmd)
1105 Con_Printf("%s text...\n", whichcmd);
1110 if(!PRVM_SetProgFromString(whichprogs))
1111 // note: this is not PRVM_SetProg because that one aborts "hard" using PRVM_Error
1112 // also, it makes printing error messages easier!
1114 Con_Printf("%s program not loaded.\n", whichprogs);
1118 if(!prog->funcoffsets.GameCommand)
1120 Con_Printf("%s program do not support GameCommand!\n", whichprogs);
1124 int restorevm_tempstringsbuf_cursize;
1129 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
1130 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(s ? s : "");
1131 PRVM_ExecuteProgram (prog->funcoffsets.GameCommand, "QC function GameCommand is missing");
1132 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1137 void PRVM_GameCommand_Server_f(void)
1139 PRVM_GameCommand("server", "sv_cmd");
1141 void PRVM_GameCommand_Client_f(void)
1143 PRVM_GameCommand("client", "cl_cmd");
1145 void PRVM_GameCommand_Menu_f(void)
1147 PRVM_GameCommand("menu", "menu_cmd");
1154 Console command to load a field of a specified edict
1157 void PRVM_ED_EdictGet_f(void)
1164 if(Cmd_Argc() != 4 && Cmd_Argc() != 5)
1166 Con_Print("prvm_edictget <program name> <edict number> <field> [<cvar>]\n");
1171 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1173 Con_Printf("Wrong program name %s !\n", Cmd_Argv(1));
1177 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1179 if((key = PRVM_ED_FindField(Cmd_Argv(3))) == 0)
1181 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1185 v = (prvm_eval_t *)(ed->fields.vp + key->ofs);
1186 s = PRVM_UglyValueString((etype_t)key->type, v);
1189 cvar_t *cvar = Cvar_FindVar(Cmd_Argv(4));
1190 if (cvar && cvar->flags & CVAR_READONLY)
1192 Con_Printf("prvm_edictget: %s is read-only\n", cvar->name);
1195 Cvar_Get(Cmd_Argv(4), s, 0, NULL);
1198 Con_Printf("%s\n", s);
1204 void PRVM_ED_GlobalGet_f(void)
1210 if(Cmd_Argc() != 3 && Cmd_Argc() != 4)
1212 Con_Print("prvm_globalget <program name> <global> [<cvar>]\n");
1217 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1219 Con_Printf("Wrong program name %s !\n", Cmd_Argv(1));
1223 key = PRVM_ED_FindGlobal(Cmd_Argv(2));
1226 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
1230 v = (prvm_eval_t *) &prog->globals.generic[key->ofs];
1231 s = PRVM_UglyValueString((etype_t)key->type, v);
1234 cvar_t *cvar = Cvar_FindVar(Cmd_Argv(3));
1235 if (cvar && cvar->flags & CVAR_READONLY)
1237 Con_Printf("prvm_globalget: %s is read-only\n", cvar->name);
1240 Cvar_Get(Cmd_Argv(3), s, 0, NULL);
1243 Con_Printf("%s\n", s);
1253 Console command to set a field of a specified edict
1256 void PRVM_ED_EdictSet_f(void)
1263 Con_Print("prvm_edictset <program name> <edict number> <field> <value>\n");
1268 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1270 Con_Printf("Wrong program name %s !\n", Cmd_Argv(1));
1274 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1276 if((key = PRVM_ED_FindField(Cmd_Argv(3))) == 0)
1277 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1279 PRVM_ED_ParseEpair(ed, key, Cmd_Argv(4), true);
1285 ====================
1288 Parses an edict out of the given string, returning the new position
1289 ed should be a properly initialized empty edict.
1290 Used for initial level load and for savegames.
1291 ====================
1293 const char *PRVM_ED_ParseEdict (const char *data, prvm_edict_t *ent)
1303 // go through all the dictionary pairs
1307 if (!COM_ParseToken_Simple(&data, false, false))
1308 PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1309 if (developer_entityparsing.integer)
1310 Con_Printf("Key: \"%s\"", com_token);
1311 if (com_token[0] == '}')
1314 // anglehack is to allow QuakeEd to write single scalar angles
1315 // and allow them to be turned into vectors. (FIXME...)
1316 if (!strcmp(com_token, "angle"))
1318 strlcpy (com_token, "angles", sizeof(com_token));
1324 // FIXME: change light to _light to get rid of this hack
1325 if (!strcmp(com_token, "light"))
1326 strlcpy (com_token, "light_lev", sizeof(com_token)); // hack for single light def
1328 strlcpy (keyname, com_token, sizeof(keyname));
1330 // another hack to fix keynames with trailing spaces
1331 n = strlen(keyname);
1332 while (n && keyname[n-1] == ' ')
1339 if (!COM_ParseToken_Simple(&data, false, false))
1340 PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1341 if (developer_entityparsing.integer)
1342 Con_Printf(" \"%s\"\n", com_token);
1344 if (com_token[0] == '}')
1345 PRVM_ERROR ("PRVM_ED_ParseEdict: closing brace without data");
1349 // ignore attempts to set key "" (this problem occurs in nehahra neh1m8.bsp)
1353 // keynames with a leading underscore are used for utility comments,
1354 // and are immediately discarded by quake
1355 if (keyname[0] == '_')
1358 key = PRVM_ED_FindField (keyname);
1361 Con_DPrintf("%s: '%s' is not a field\n", PRVM_NAME, keyname);
1368 strlcpy (temp, com_token, sizeof(temp));
1369 dpsnprintf (com_token, sizeof(com_token), "0 %s 0", temp);
1372 if (!PRVM_ED_ParseEpair(ent, key, com_token, strcmp(keyname, "wad") != 0))
1373 PRVM_ERROR ("PRVM_ED_ParseEdict: parse error");
1377 ent->priv.required->free = true;
1385 PRVM_ED_LoadFromFile
1387 The entities are directly placed in the array, rather than allocated with
1388 PRVM_ED_Alloc, because otherwise an error loading the map would have entity
1389 number references out of order.
1391 Creates a server's entity / program execution context by
1392 parsing textual entity definitions out of an ent file.
1394 Used for both fresh maps and savegame loads. A fresh map would also need
1395 to call PRVM_ED_CallSpawnFunctions () to let the objects initialize themselves.
1398 void PRVM_ED_LoadFromFile (const char *data)
1401 int parsed, inhibited, spawned, died;
1402 const char *funcname;
1410 prvm_reuseedicts_always_allow = realtime;
1415 // parse the opening brace
1416 if (!COM_ParseToken_Simple(&data, false, false))
1418 if (com_token[0] != '{')
1419 PRVM_ERROR ("PRVM_ED_LoadFromFile: %s: found %s when expecting {", PRVM_NAME, com_token);
1421 // CHANGED: this is not conform to PR_LoadFromFile
1422 if(prog->loadintoworld)
1424 prog->loadintoworld = false;
1425 ent = PRVM_EDICT_NUM(0);
1428 ent = PRVM_ED_Alloc();
1431 if (ent != prog->edicts) // hack
1432 memset (ent->fields.vp, 0, prog->progs->entityfields * 4);
1434 data = PRVM_ED_ParseEdict (data, ent);
1437 // remove the entity ?
1438 if(prog->load_edict && !prog->load_edict(ent))
1445 if (prog->funcoffsets.SV_OnEntityPreSpawnFunction)
1448 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1449 PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityPreSpawnFunction, "QC function SV_OnEntityPreSpawnFunction is missing");
1452 if(ent->priv.required->free)
1459 // immediately call spawn function, but only if there is a self global and a classname
1461 if(!ent->priv.required->free)
1462 if(prog->globaloffsets.self >= 0 && prog->fieldoffsets.classname >= 0)
1464 string_t handle = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.classname)->string;
1467 Con_Print("No classname for:\n");
1468 PRVM_ED_Print(ent, NULL);
1473 // look for the spawn function
1474 funcname = PRVM_GetString(handle);
1475 func = PRVM_ED_FindFunction (va("spawnfunc_%s", funcname));
1477 if(prog->globaloffsets.require_spawnfunc_prefix < 0)
1478 func = PRVM_ED_FindFunction (funcname);
1482 // check for OnEntityNoSpawnFunction
1483 if (prog->funcoffsets.SV_OnEntityNoSpawnFunction)
1486 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1487 PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityNoSpawnFunction, "QC function SV_OnEntityNoSpawnFunction is missing");
1491 if (developer.integer > 0) // don't confuse non-developers with errors
1493 Con_Print("No spawn function for:\n");
1494 PRVM_ED_Print(ent, NULL);
1497 continue; // not included in "inhibited" count
1503 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1504 PRVM_ExecuteProgram (func - prog->functions, "");
1508 if(!ent->priv.required->free)
1509 if (prog->funcoffsets.SV_OnEntityPostSpawnFunction)
1512 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1513 PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityPostSpawnFunction, "QC function SV_OnEntityPostSpawnFunction is missing");
1517 if (ent->priv.required->free)
1521 Con_DPrintf("%s: %i new entities parsed, %i new inhibited, %i (%i new) spawned (whereas %i removed self, %i stayed)\n", PRVM_NAME, parsed, inhibited, prog->num_edicts, spawned, died, spawned - died);
1523 prvm_reuseedicts_always_allow = 0;
1526 void PRVM_FindOffsets(void)
1528 // field and global searches use -1 for NULL
1529 memset(&prog->fieldoffsets, -1, sizeof(prog->fieldoffsets));
1530 memset(&prog->globaloffsets, -1, sizeof(prog->globaloffsets));
1531 // functions use 0 for NULL
1532 memset(&prog->funcoffsets, 0, sizeof(prog->funcoffsets));
1534 // server and client qc use a lot of similar fields, so this is combined
1535 prog->fieldoffsets.SendEntity = PRVM_ED_FindFieldOffset("SendEntity");
1536 prog->fieldoffsets.SendFlags = PRVM_ED_FindFieldOffset("SendFlags");
1537 prog->fieldoffsets.Version = PRVM_ED_FindFieldOffset("Version");
1538 prog->fieldoffsets.alpha = PRVM_ED_FindFieldOffset("alpha");
1539 prog->fieldoffsets.ammo_cells1 = PRVM_ED_FindFieldOffset("ammo_cells1");
1540 prog->fieldoffsets.ammo_lava_nails = PRVM_ED_FindFieldOffset("ammo_lava_nails");
1541 prog->fieldoffsets.ammo_multi_rockets = PRVM_ED_FindFieldOffset("ammo_multi_rockets");
1542 prog->fieldoffsets.ammo_nails1 = PRVM_ED_FindFieldOffset("ammo_nails1");
1543 prog->fieldoffsets.ammo_plasma = PRVM_ED_FindFieldOffset("ammo_plasma");
1544 prog->fieldoffsets.ammo_rockets1 = PRVM_ED_FindFieldOffset("ammo_rockets1");
1545 prog->fieldoffsets.ammo_shells1 = PRVM_ED_FindFieldOffset("ammo_shells1");
1546 prog->fieldoffsets.angles = PRVM_ED_FindFieldOffset("angles");
1547 prog->fieldoffsets.button3 = PRVM_ED_FindFieldOffset("button3");
1548 prog->fieldoffsets.button4 = PRVM_ED_FindFieldOffset("button4");
1549 prog->fieldoffsets.button5 = PRVM_ED_FindFieldOffset("button5");
1550 prog->fieldoffsets.button6 = PRVM_ED_FindFieldOffset("button6");
1551 prog->fieldoffsets.button7 = PRVM_ED_FindFieldOffset("button7");
1552 prog->fieldoffsets.button8 = PRVM_ED_FindFieldOffset("button8");
1553 prog->fieldoffsets.button9 = PRVM_ED_FindFieldOffset("button9");
1554 prog->fieldoffsets.button10 = PRVM_ED_FindFieldOffset("button10");
1555 prog->fieldoffsets.button11 = PRVM_ED_FindFieldOffset("button11");
1556 prog->fieldoffsets.button12 = PRVM_ED_FindFieldOffset("button12");
1557 prog->fieldoffsets.button13 = PRVM_ED_FindFieldOffset("button13");
1558 prog->fieldoffsets.button14 = PRVM_ED_FindFieldOffset("button14");
1559 prog->fieldoffsets.button15 = PRVM_ED_FindFieldOffset("button15");
1560 prog->fieldoffsets.button16 = PRVM_ED_FindFieldOffset("button16");
1561 prog->fieldoffsets.buttonchat = PRVM_ED_FindFieldOffset("buttonchat");
1562 prog->fieldoffsets.buttonuse = PRVM_ED_FindFieldOffset("buttonuse");
1563 prog->fieldoffsets.chain = PRVM_ED_FindFieldOffset("chain");
1564 prog->fieldoffsets.classname = PRVM_ED_FindFieldOffset("classname");
1565 prog->fieldoffsets.clientcamera = PRVM_ED_FindFieldOffset("clientcamera");
1566 prog->fieldoffsets.clientcolors = PRVM_ED_FindFieldOffset("clientcolors");
1567 prog->fieldoffsets.clientstatus = PRVM_ED_FindFieldOffset("clientstatus");
1568 prog->fieldoffsets.color = PRVM_ED_FindFieldOffset("color");
1569 prog->fieldoffsets.colormod = PRVM_ED_FindFieldOffset("colormod");
1570 prog->fieldoffsets.contentstransition = PRVM_ED_FindFieldOffset("contentstransition");
1571 prog->fieldoffsets.cursor_active = PRVM_ED_FindFieldOffset("cursor_active");
1572 prog->fieldoffsets.cursor_screen = PRVM_ED_FindFieldOffset("cursor_screen");
1573 prog->fieldoffsets.cursor_trace_endpos = PRVM_ED_FindFieldOffset("cursor_trace_endpos");
1574 prog->fieldoffsets.cursor_trace_ent = PRVM_ED_FindFieldOffset("cursor_trace_ent");
1575 prog->fieldoffsets.cursor_trace_start = PRVM_ED_FindFieldOffset("cursor_trace_start");
1576 prog->fieldoffsets.customizeentityforclient = PRVM_ED_FindFieldOffset("customizeentityforclient");
1577 prog->fieldoffsets.dimension_hit = PRVM_ED_FindFieldOffset("dimension_hit");
1578 prog->fieldoffsets.dimension_solid = PRVM_ED_FindFieldOffset("dimension_solid");
1579 prog->fieldoffsets.disableclientprediction = PRVM_ED_FindFieldOffset("disableclientprediction");
1580 prog->fieldoffsets.discardabledemo = PRVM_ED_FindFieldOffset("discardabledemo");
1581 prog->fieldoffsets.dphitcontentsmask = PRVM_ED_FindFieldOffset("dphitcontentsmask");
1582 prog->fieldoffsets.drawonlytoclient = PRVM_ED_FindFieldOffset("drawonlytoclient");
1583 prog->fieldoffsets.exteriormodeltoclient = PRVM_ED_FindFieldOffset("exteriormodeltoclient");
1584 prog->fieldoffsets.fatness = PRVM_ED_FindFieldOffset("fatness");
1585 prog->fieldoffsets.forceshader = PRVM_ED_FindFieldOffset("forceshader");
1586 prog->fieldoffsets.frame = PRVM_ED_FindFieldOffset("frame");
1587 prog->fieldoffsets.frame1time = PRVM_ED_FindFieldOffset("frame1time");
1588 prog->fieldoffsets.frame2 = PRVM_ED_FindFieldOffset("frame2");
1589 prog->fieldoffsets.frame2time = PRVM_ED_FindFieldOffset("frame2time");
1590 prog->fieldoffsets.frame3 = PRVM_ED_FindFieldOffset("frame3");
1591 prog->fieldoffsets.frame3time = PRVM_ED_FindFieldOffset("frame3time");
1592 prog->fieldoffsets.frame4 = PRVM_ED_FindFieldOffset("frame4");
1593 prog->fieldoffsets.frame4time = PRVM_ED_FindFieldOffset("frame4time");
1594 prog->fieldoffsets.fullbright = PRVM_ED_FindFieldOffset("fullbright");
1595 prog->fieldoffsets.glow_color = PRVM_ED_FindFieldOffset("glow_color");
1596 prog->fieldoffsets.glow_size = PRVM_ED_FindFieldOffset("glow_size");
1597 prog->fieldoffsets.glow_trail = PRVM_ED_FindFieldOffset("glow_trail");
1598 prog->fieldoffsets.glowmod = PRVM_ED_FindFieldOffset("glowmod");
1599 prog->fieldoffsets.gravity = PRVM_ED_FindFieldOffset("gravity");
1600 prog->fieldoffsets.groundentity = PRVM_ED_FindFieldOffset("groundentity");
1601 prog->fieldoffsets.hull = PRVM_ED_FindFieldOffset("hull");
1602 prog->fieldoffsets.ideal_yaw = PRVM_ED_FindFieldOffset("ideal_yaw");
1603 prog->fieldoffsets.idealpitch = PRVM_ED_FindFieldOffset("idealpitch");
1604 prog->fieldoffsets.items2 = PRVM_ED_FindFieldOffset("items2");
1605 prog->fieldoffsets.lerpfrac = PRVM_ED_FindFieldOffset("lerpfrac");
1606 prog->fieldoffsets.lerpfrac3 = PRVM_ED_FindFieldOffset("lerpfrac3");
1607 prog->fieldoffsets.lerpfrac4 = PRVM_ED_FindFieldOffset("lerpfrac4");
1608 prog->fieldoffsets.light_lev = PRVM_ED_FindFieldOffset("light_lev");
1609 prog->fieldoffsets.message = PRVM_ED_FindFieldOffset("message");
1610 prog->fieldoffsets.modelflags = PRVM_ED_FindFieldOffset("modelflags");
1611 prog->fieldoffsets.movement = PRVM_ED_FindFieldOffset("movement");
1612 prog->fieldoffsets.movetypesteplandevent = PRVM_ED_FindFieldOffset("movetypesteplandevent");
1613 prog->fieldoffsets.netaddress = PRVM_ED_FindFieldOffset("netaddress");
1614 prog->fieldoffsets.nextthink = PRVM_ED_FindFieldOffset("nextthink");
1615 prog->fieldoffsets.nodrawtoclient = PRVM_ED_FindFieldOffset("nodrawtoclient");
1616 prog->fieldoffsets.pflags = PRVM_ED_FindFieldOffset("pflags");
1617 prog->fieldoffsets.ping = PRVM_ED_FindFieldOffset("ping");
1618 prog->fieldoffsets.packetloss = PRVM_ED_FindFieldOffset("ping_packetloss");
1619 prog->fieldoffsets.movementloss = PRVM_ED_FindFieldOffset("ping_movementloss");
1620 prog->fieldoffsets.pitch_speed = PRVM_ED_FindFieldOffset("pitch_speed");
1621 prog->fieldoffsets.playermodel = PRVM_ED_FindFieldOffset("playermodel");
1622 prog->fieldoffsets.playerskin = PRVM_ED_FindFieldOffset("playerskin");
1623 prog->fieldoffsets.pmodel = PRVM_ED_FindFieldOffset("pmodel");
1624 prog->fieldoffsets.punchvector = PRVM_ED_FindFieldOffset("punchvector");
1625 prog->fieldoffsets.renderamt = PRVM_ED_FindFieldOffset("renderamt"); // HalfLife support
1626 prog->fieldoffsets.renderflags = PRVM_ED_FindFieldOffset("renderflags");
1627 prog->fieldoffsets.rendermode = PRVM_ED_FindFieldOffset("rendermode"); // HalfLife support
1628 prog->fieldoffsets.scale = PRVM_ED_FindFieldOffset("scale");
1629 prog->fieldoffsets.shadertime = PRVM_ED_FindFieldOffset("shadertime");
1630 prog->fieldoffsets.skeletonindex = PRVM_ED_FindFieldOffset("skeletonindex");
1631 prog->fieldoffsets.style = PRVM_ED_FindFieldOffset("style");
1632 prog->fieldoffsets.tag_entity = PRVM_ED_FindFieldOffset("tag_entity");
1633 prog->fieldoffsets.tag_index = PRVM_ED_FindFieldOffset("tag_index");
1634 prog->fieldoffsets.think = PRVM_ED_FindFieldOffset("think");
1635 prog->fieldoffsets.viewmodelforclient = PRVM_ED_FindFieldOffset("viewmodelforclient");
1636 prog->fieldoffsets.viewzoom = PRVM_ED_FindFieldOffset("viewzoom");
1637 prog->fieldoffsets.yaw_speed = PRVM_ED_FindFieldOffset("yaw_speed");
1638 prog->fieldoffsets.bouncefactor = PRVM_ED_FindFieldOffset("bouncefactor");
1639 prog->fieldoffsets.bouncestop = PRVM_ED_FindFieldOffset("bouncestop");
1641 prog->fieldoffsets.solid = PRVM_ED_FindFieldOffset("solid");
1642 prog->fieldoffsets.movetype = PRVM_ED_FindFieldOffset("movetype");
1643 prog->fieldoffsets.modelindex = PRVM_ED_FindFieldOffset("modelindex");
1644 prog->fieldoffsets.mins = PRVM_ED_FindFieldOffset("mins");
1645 prog->fieldoffsets.maxs = PRVM_ED_FindFieldOffset("maxs");
1646 prog->fieldoffsets.mass = PRVM_ED_FindFieldOffset("mass");
1647 prog->fieldoffsets.origin = PRVM_ED_FindFieldOffset("origin");
1648 prog->fieldoffsets.velocity = PRVM_ED_FindFieldOffset("velocity");
1649 //prog->fieldoffsets.axis_forward = PRVM_ED_FindFieldOffset("axis_forward");
1650 //prog->fieldoffsets.axis_left = PRVM_ED_FindFieldOffset("axis_left");
1651 //prog->fieldoffsets.axis_up = PRVM_ED_FindFieldOffset("axis_up");
1652 //prog->fieldoffsets.spinvelocity = PRVM_ED_FindFieldOffset("spinvelocity");
1653 prog->fieldoffsets.angles = PRVM_ED_FindFieldOffset("angles");
1654 prog->fieldoffsets.avelocity = PRVM_ED_FindFieldOffset("avelocity");
1655 prog->fieldoffsets.aiment = PRVM_ED_FindFieldOffset("aiment");
1656 prog->fieldoffsets.enemy = PRVM_ED_FindFieldOffset("enemy");
1657 prog->fieldoffsets.jointtype = PRVM_ED_FindFieldOffset("jointtype");
1658 prog->fieldoffsets.movedir = PRVM_ED_FindFieldOffset("movedir");
1660 prog->fieldoffsets.camera_transform = PRVM_ED_FindFieldOffset("camera_transform");
1661 prog->fieldoffsets.userwavefunc_param0 = PRVM_ED_FindFieldOffset("userwavefunc_param0");
1662 prog->fieldoffsets.userwavefunc_param1 = PRVM_ED_FindFieldOffset("userwavefunc_param1");
1663 prog->fieldoffsets.userwavefunc_param2 = PRVM_ED_FindFieldOffset("userwavefunc_param2");
1664 prog->fieldoffsets.userwavefunc_param3 = PRVM_ED_FindFieldOffset("userwavefunc_param3");
1666 prog->fieldoffsets.crypto_keyfp = PRVM_ED_FindFieldOffset("crypto_keyfp");
1667 prog->fieldoffsets.crypto_mykeyfp = PRVM_ED_FindFieldOffset("crypto_mykeyfp");
1668 prog->fieldoffsets.crypto_idfp = PRVM_ED_FindFieldOffset("crypto_idfp");
1669 prog->fieldoffsets.crypto_encryptmethod = PRVM_ED_FindFieldOffset("crypto_encryptmethod");
1670 prog->fieldoffsets.crypto_signmethod = PRVM_ED_FindFieldOffset("crypto_signmethod");
1672 prog->funcoffsets.CSQC_ConsoleCommand = PRVM_ED_FindFunctionOffset("CSQC_ConsoleCommand");
1673 prog->funcoffsets.CSQC_Ent_Remove = PRVM_ED_FindFunctionOffset("CSQC_Ent_Remove");
1674 prog->funcoffsets.CSQC_Ent_Spawn = PRVM_ED_FindFunctionOffset("CSQC_Ent_Spawn");
1675 prog->funcoffsets.CSQC_Ent_Update = PRVM_ED_FindFunctionOffset("CSQC_Ent_Update");
1676 prog->funcoffsets.CSQC_Event = PRVM_ED_FindFunctionOffset("CSQC_Event");
1677 prog->funcoffsets.CSQC_Event_Sound = PRVM_ED_FindFunctionOffset("CSQC_Event_Sound");
1678 prog->funcoffsets.CSQC_Init = PRVM_ED_FindFunctionOffset("CSQC_Init");
1679 prog->funcoffsets.CSQC_InputEvent = PRVM_ED_FindFunctionOffset("CSQC_InputEvent");
1680 prog->funcoffsets.CSQC_Parse_CenterPrint = PRVM_ED_FindFunctionOffset("CSQC_Parse_CenterPrint");
1681 prog->funcoffsets.CSQC_Parse_Print = PRVM_ED_FindFunctionOffset("CSQC_Parse_Print");
1682 prog->funcoffsets.CSQC_Parse_StuffCmd = PRVM_ED_FindFunctionOffset("CSQC_Parse_StuffCmd");
1683 prog->funcoffsets.CSQC_Parse_TempEntity = PRVM_ED_FindFunctionOffset("CSQC_Parse_TempEntity");
1684 prog->funcoffsets.CSQC_Shutdown = PRVM_ED_FindFunctionOffset("CSQC_Shutdown");
1685 prog->funcoffsets.CSQC_UpdateView = PRVM_ED_FindFunctionOffset("CSQC_UpdateView");
1686 prog->funcoffsets.EndFrame = PRVM_ED_FindFunctionOffset("EndFrame");
1687 prog->funcoffsets.GameCommand = PRVM_ED_FindFunctionOffset("GameCommand");
1688 prog->funcoffsets.Gecko_Query = PRVM_ED_FindFunctionOffset("Gecko_Query");
1689 prog->funcoffsets.RestoreGame = PRVM_ED_FindFunctionOffset("RestoreGame");
1690 prog->funcoffsets.SV_ChangeTeam = PRVM_ED_FindFunctionOffset("SV_ChangeTeam");
1691 prog->funcoffsets.SV_OnEntityNoSpawnFunction = PRVM_ED_FindFunctionOffset("SV_OnEntityNoSpawnFunction");
1692 prog->funcoffsets.SV_OnEntityPostSpawnFunction = PRVM_ED_FindFunctionOffset("SV_OnEntityPostSpawnFunction");
1693 prog->funcoffsets.SV_OnEntityPreSpawnFunction = PRVM_ED_FindFunctionOffset("SV_OnEntityPreSpawnFunction");
1694 prog->funcoffsets.SV_ParseClientCommand = PRVM_ED_FindFunctionOffset("SV_ParseClientCommand");
1695 prog->funcoffsets.SV_PausedTic = PRVM_ED_FindFunctionOffset("SV_PausedTic");
1696 prog->funcoffsets.SV_PlayerPhysics = PRVM_ED_FindFunctionOffset("SV_PlayerPhysics");
1697 prog->funcoffsets.SV_Shutdown = PRVM_ED_FindFunctionOffset("SV_Shutdown");
1698 prog->funcoffsets.URI_Get_Callback = PRVM_ED_FindFunctionOffset("URI_Get_Callback");
1699 prog->globaloffsets.SV_InitCmd = PRVM_ED_FindGlobalOffset("SV_InitCmd");
1700 prog->globaloffsets.coop = PRVM_ED_FindGlobalOffset("coop");
1701 prog->globaloffsets.deathmatch = PRVM_ED_FindGlobalOffset("deathmatch");
1702 prog->globaloffsets.dmg_origin = PRVM_ED_FindGlobalOffset("dmg_origin");
1703 prog->globaloffsets.dmg_save = PRVM_ED_FindGlobalOffset("dmg_save");
1704 prog->globaloffsets.dmg_take = PRVM_ED_FindGlobalOffset("dmg_take");
1705 prog->globaloffsets.drawfont = PRVM_ED_FindGlobalOffset("drawfont");
1706 prog->globaloffsets.drawfontscale = PRVM_ED_FindGlobalOffset("drawfontscale");
1707 prog->globaloffsets.gettaginfo_forward = PRVM_ED_FindGlobalOffset("gettaginfo_forward");
1708 prog->globaloffsets.gettaginfo_name = PRVM_ED_FindGlobalOffset("gettaginfo_name");
1709 prog->globaloffsets.gettaginfo_offset = PRVM_ED_FindGlobalOffset("gettaginfo_offset");
1710 prog->globaloffsets.gettaginfo_parent = PRVM_ED_FindGlobalOffset("gettaginfo_parent");
1711 prog->globaloffsets.gettaginfo_right = PRVM_ED_FindGlobalOffset("gettaginfo_right");
1712 prog->globaloffsets.gettaginfo_up = PRVM_ED_FindGlobalOffset("gettaginfo_up");
1713 prog->globaloffsets.transparent_offset = PRVM_ED_FindGlobalOffset("transparent_offset");
1714 prog->globaloffsets.intermission = PRVM_ED_FindGlobalOffset("intermission");
1715 prog->globaloffsets.require_spawnfunc_prefix = PRVM_ED_FindGlobalOffset("require_spawnfunc_prefix");
1716 prog->globaloffsets.sb_showscores = PRVM_ED_FindGlobalOffset("sb_showscores");
1717 prog->globaloffsets.self = PRVM_ED_FindGlobalOffset("self");
1718 prog->globaloffsets.serverdeltatime = PRVM_ED_FindGlobalOffset("serverdeltatime");
1719 prog->globaloffsets.serverprevtime = PRVM_ED_FindGlobalOffset("serverprevtime");
1720 prog->globaloffsets.servertime = PRVM_ED_FindGlobalOffset("servertime");
1721 prog->globaloffsets.time = PRVM_ED_FindGlobalOffset("time");
1722 prog->globaloffsets.trace_allsolid = PRVM_ED_FindGlobalOffset("trace_allsolid");
1723 prog->globaloffsets.trace_dphitcontents = PRVM_ED_FindGlobalOffset("trace_dphitcontents");
1724 prog->globaloffsets.trace_dphitq3surfaceflags = PRVM_ED_FindGlobalOffset("trace_dphitq3surfaceflags");
1725 prog->globaloffsets.trace_dphittexturename = PRVM_ED_FindGlobalOffset("trace_dphittexturename");
1726 prog->globaloffsets.trace_dpstartcontents = PRVM_ED_FindGlobalOffset("trace_dpstartcontents");
1727 prog->globaloffsets.trace_endpos = PRVM_ED_FindGlobalOffset("trace_endpos");
1728 prog->globaloffsets.trace_ent = PRVM_ED_FindGlobalOffset("trace_ent");
1729 prog->globaloffsets.trace_fraction = PRVM_ED_FindGlobalOffset("trace_fraction");
1730 prog->globaloffsets.trace_inopen = PRVM_ED_FindGlobalOffset("trace_inopen");
1731 prog->globaloffsets.trace_inwater = PRVM_ED_FindGlobalOffset("trace_inwater");
1732 prog->globaloffsets.trace_networkentity = PRVM_ED_FindGlobalOffset("trace_networkentity");
1733 prog->globaloffsets.trace_plane_dist = PRVM_ED_FindGlobalOffset("trace_plane_dist");
1734 prog->globaloffsets.trace_plane_normal = PRVM_ED_FindGlobalOffset("trace_plane_normal");
1735 prog->globaloffsets.trace_startsolid = PRVM_ED_FindGlobalOffset("trace_startsolid");
1736 prog->globaloffsets.v_forward = PRVM_ED_FindGlobalOffset("v_forward");
1737 prog->globaloffsets.v_right = PRVM_ED_FindGlobalOffset("v_right");
1738 prog->globaloffsets.v_up = PRVM_ED_FindGlobalOffset("v_up");
1739 prog->globaloffsets.view_angles = PRVM_ED_FindGlobalOffset("view_angles");
1740 prog->globaloffsets.view_punchangle = PRVM_ED_FindGlobalOffset("view_punchangle");
1741 prog->globaloffsets.view_punchvector = PRVM_ED_FindGlobalOffset("view_punchvector");
1742 prog->globaloffsets.worldstatus = PRVM_ED_FindGlobalOffset("worldstatus");
1743 prog->globaloffsets.particles_alphamin = PRVM_ED_FindGlobalOffset("particles_alphamin");
1744 prog->globaloffsets.particles_alphamax = PRVM_ED_FindGlobalOffset("particles_alphamax");
1745 prog->globaloffsets.particles_colormin = PRVM_ED_FindGlobalOffset("particles_colormin");
1746 prog->globaloffsets.particles_colormax = PRVM_ED_FindGlobalOffset("particles_colormax");
1748 // menu qc only uses some functions, nothing else
1749 prog->funcoffsets.m_draw = PRVM_ED_FindFunctionOffset("m_draw");
1750 prog->funcoffsets.m_init = PRVM_ED_FindFunctionOffset("m_init");
1751 prog->funcoffsets.m_keydown = PRVM_ED_FindFunctionOffset("m_keydown");
1752 prog->funcoffsets.m_keyup = PRVM_ED_FindFunctionOffset("m_keyup");
1753 prog->funcoffsets.m_shutdown = PRVM_ED_FindFunctionOffset("m_shutdown");
1754 prog->funcoffsets.m_toggle = PRVM_ED_FindFunctionOffset("m_toggle");
1755 prog->funcoffsets.m_newmap = PRVM_ED_FindFunctionOffset("m_newmap");
1760 typedef struct dpfield_s
1767 #define DPFIELDS (sizeof(dpfields) / sizeof(dpfield_t))
1769 dpfield_t dpfields[] =
1780 #define PO_HASHSIZE 16384
1781 typedef struct po_string_s
1784 struct po_string_s *nextonhashchain;
1789 po_string_t *hashtable[PO_HASHSIZE];
1792 void PRVM_PO_UnparseString(char *out, const char *in, size_t outsize)
1801 case '\a': if(outsize >= 2) { *out++ = '\\'; *out++ = 'a'; outsize -= 2; } break;
1802 case '\b': if(outsize >= 2) { *out++ = '\\'; *out++ = 'b'; outsize -= 2; } break;
1803 case '\t': if(outsize >= 2) { *out++ = '\\'; *out++ = 't'; outsize -= 2; } break;
1804 case '\r': if(outsize >= 2) { *out++ = '\\'; *out++ = 'r'; outsize -= 2; } break;
1805 case '\n': if(outsize >= 2) { *out++ = '\\'; *out++ = 'n'; outsize -= 2; } break;
1806 case '\\': if(outsize >= 2) { *out++ = '\\'; *out++ = '\\'; outsize -= 2; } break;
1807 case '"': if(outsize >= 2) { *out++ = '\\'; *out++ = '"'; outsize -= 2; } break;
1809 if(*in >= 0 && *in <= 0x1F)
1814 *out++ = '0' + ((*in & 0700) >> 6);
1815 *out++ = '0' + ((*in & 0070) >> 3);
1816 *out++ = '0' + ((*in & 0007));
1833 void PRVM_PO_ParseString(char *out, const char *in, size_t outsize)
1846 case 'a': if(outsize > 0) { *out++ = '\a'; --outsize; } break;
1847 case 'b': if(outsize > 0) { *out++ = '\b'; --outsize; } break;
1848 case 't': if(outsize > 0) { *out++ = '\t'; --outsize; } break;
1849 case 'r': if(outsize > 0) { *out++ = '\r'; --outsize; } break;
1850 case 'n': if(outsize > 0) { *out++ = '\n'; --outsize; } break;
1851 case '\\': if(outsize > 0) { *out++ = '\\'; --outsize; } break;
1852 case '"': if(outsize > 0) { *out++ = '"'; --outsize; } break;
1853 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
1857 if(*in >= '0' && *in <= '7')
1860 *out = (*out << 3) | (*in - '0');
1863 if(*in >= '0' && *in <= '7')
1866 *out = (*out << 3) | (*in - '0');
1877 if(outsize > 0) { *out++ = *in; --outsize; }
1892 po_t *PRVM_PO_Load(const char *filename, mempool_t *pool)
1897 char inbuf[MAX_INPUTLINE];
1898 char decodedbuf[MAX_INPUTLINE];
1901 po_string_t thisstr;
1902 const char *buf = (const char *) FS_LoadFile(filename, pool, true, NULL);
1907 memset(&thisstr, 0, sizeof(thisstr)); // hush compiler warning
1909 po = (po_t *)Mem_Alloc(pool, sizeof(*po));
1910 memset(po, 0, sizeof(*po));
1918 p = strchr(p, '\n');
1924 if(*p == '\r' || *p == '\n')
1929 if(!strncmp(p, "msgid \"", 7))
1934 else if(!strncmp(p, "msgstr \"", 8))
1941 p = strchr(p, '\n');
1951 q = strchr(p, '\n');
1958 if((size_t)(q - p) >= (size_t) sizeof(inbuf))
1960 strlcpy(inbuf, p, q - p); // not - 1, because this adds a NUL
1961 PRVM_PO_ParseString(decodedbuf + decodedpos, inbuf, sizeof(decodedbuf) - decodedpos);
1962 decodedpos += strlen(decodedbuf + decodedpos);
1972 Mem_Free(thisstr.key);
1973 thisstr.key = (char *)Mem_Alloc(pool, decodedpos + 1);
1974 memcpy(thisstr.key, decodedbuf, decodedpos + 1);
1976 else if(decodedpos > 0 && thisstr.key) // skip empty translation results
1978 thisstr.value = (char *)Mem_Alloc(pool, decodedpos + 1);
1979 memcpy(thisstr.value, decodedbuf, decodedpos + 1);
1980 hashindex = CRC_Block((const unsigned char *) thisstr.key, strlen(thisstr.key)) % PO_HASHSIZE;
1981 thisstr.nextonhashchain = po->hashtable[hashindex];
1982 po->hashtable[hashindex] = (po_string_t *)Mem_Alloc(pool, sizeof(thisstr));
1983 memcpy(po->hashtable[hashindex], &thisstr, sizeof(thisstr));
1984 memset(&thisstr, 0, sizeof(thisstr));
1988 Mem_Free((char *) buf);
1991 const char *PRVM_PO_Lookup(po_t *po, const char *str)
1993 int hashindex = CRC_Block((const unsigned char *) str, strlen(str)) % PO_HASHSIZE;
1994 po_string_t *p = po->hashtable[hashindex];
1997 if(!strcmp(str, p->key))
1999 p = p->nextonhashchain;
2003 void PRVM_PO_Destroy(po_t *po)
2006 for(i = 0; i < PO_HASHSIZE; ++i)
2008 po_string_t *p = po->hashtable[i];
2012 p = p->nextonhashchain;
2021 void PRVM_LeakTest(void);
2022 void PRVM_ResetProg(void)
2025 PRVM_GCALL(reset_cmd)();
2026 Mem_FreePool(&prog->progs_mempool);
2028 PRVM_PO_Destroy((po_t *) prog->po);
2029 memset(prog,0,sizeof(prvm_prog_t));
2030 prog->starttime = Sys_DoubleTime();
2038 void PRVM_LoadLNO( const char *progname ) {
2039 fs_offset_t filesize;
2041 unsigned int *header;
2044 FS_StripExtension( progname, filename, sizeof( filename ) );
2045 strlcat( filename, ".lno", sizeof( filename ) );
2047 lno = FS_LoadFile( filename, tempmempool, false, &filesize );
2053 <Spike> SafeWrite (h, &lnotype, sizeof(int));
2054 <Spike> SafeWrite (h, &version, sizeof(int));
2055 <Spike> SafeWrite (h, &numglobaldefs, sizeof(int));
2056 <Spike> SafeWrite (h, &numpr_globals, sizeof(int));
2057 <Spike> SafeWrite (h, &numfielddefs, sizeof(int));
2058 <Spike> SafeWrite (h, &numstatements, sizeof(int));
2059 <Spike> SafeWrite (h, statement_linenums, numstatements*sizeof(int));
2061 if( (unsigned) filesize < (6 + prog->progs->numstatements) * sizeof( int ) ) {
2066 header = (unsigned int *) lno;
2067 if( header[ 0 ] == *(unsigned int *) "LNOF" &&
2068 LittleLong( header[ 1 ] ) == 1 &&
2069 (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs->numglobaldefs &&
2070 (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs->numglobals &&
2071 (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs->numfielddefs &&
2072 (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs->numstatements )
2074 prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs->numstatements * sizeof( int ) );
2075 memcpy( prog->statement_linenums, (int *) lno + 6, prog->progs->numstatements * sizeof( int ) );
2085 void PRVM_LoadProgs (const char * filename, int numrequiredfunc, const char **required_func, int numrequiredfields, prvm_required_field_t *required_field, int numrequiredglobals, char **required_global)
2089 ddef_t *infielddefs;
2090 dfunction_t *dfunctions;
2091 fs_offset_t filesize;
2093 if( prog->loaded ) {
2094 PRVM_ERROR ("PRVM_LoadProgs: there is already a %s program loaded!", PRVM_NAME );
2097 prog->progs = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
2098 if (prog->progs == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
2099 PRVM_ERROR ("PRVM_LoadProgs: couldn't load %s for %s", filename, PRVM_NAME);
2100 // TODO bounds check header fields (e.g. numstatements), they must never go behind end of file
2102 Con_DPrintf("%s programs occupy %iK.\n", PRVM_NAME, (int)(filesize/1024));
2104 prog->filecrc = CRC_Block((unsigned char *)prog->progs, filesize);
2106 // byte swap the header
2107 for (i = 0;i < (int) sizeof(*prog->progs) / 4;i++)
2108 ((int *)prog->progs)[i] = LittleLong ( ((int *)prog->progs)[i] );
2110 if (prog->progs->version != PROG_VERSION)
2111 PRVM_ERROR ("%s: %s has wrong version number (%i should be %i)", PRVM_NAME, filename, prog->progs->version, PROG_VERSION);
2112 if (prog->progs->crc != prog->headercrc && prog->progs->crc != prog->headercrc2)
2113 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);
2115 //prog->functions = (dfunction_t *)((unsigned char *)progs + progs->ofs_functions);
2116 dfunctions = (dfunction_t *)((unsigned char *)prog->progs + prog->progs->ofs_functions);
2118 if (prog->progs->ofs_strings + prog->progs->numstrings >= (int)filesize)
2119 PRVM_ERROR ("%s: %s strings go past end of file", PRVM_NAME, filename);
2120 prog->strings = (char *)prog->progs + prog->progs->ofs_strings;
2121 prog->stringssize = prog->progs->numstrings;
2123 prog->numknownstrings = 0;
2124 prog->maxknownstrings = 0;
2125 prog->knownstrings = NULL;
2126 prog->knownstrings_freeable = NULL;
2128 Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
2130 prog->globaldefs = (ddef_t *)((unsigned char *)prog->progs + prog->progs->ofs_globaldefs);
2132 // we need to expand the fielddefs list to include all the engine fields,
2133 // so allocate a new place for it
2134 infielddefs = (ddef_t *)((unsigned char *)prog->progs + prog->progs->ofs_fielddefs);
2136 prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs->numfielddefs + numrequiredfields) * sizeof(ddef_t));
2138 prog->statements = (dstatement_t *)((unsigned char *)prog->progs + prog->progs->ofs_statements);
2140 prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs->numstatements * sizeof(*prog->statement_profile));
2142 //pr_global_struct = (globalvars_t *)((unsigned char *)progs + progs->ofs_globals);
2143 prog->globals.generic = (float *)((unsigned char *)prog->progs + prog->progs->ofs_globals);
2145 // byte swap the lumps
2146 for (i=0 ; i<prog->progs->numstatements ; i++)
2148 prog->statements[i].op = LittleShort(prog->statements[i].op);
2149 prog->statements[i].a = LittleShort(prog->statements[i].a);
2150 prog->statements[i].b = LittleShort(prog->statements[i].b);
2151 prog->statements[i].c = LittleShort(prog->statements[i].c);
2154 prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs->numfunctions);
2155 for (i = 0;i < prog->progs->numfunctions;i++)
2157 prog->functions[i].first_statement = LittleLong (dfunctions[i].first_statement);
2158 prog->functions[i].parm_start = LittleLong (dfunctions[i].parm_start);
2159 prog->functions[i].s_name = LittleLong (dfunctions[i].s_name);
2160 prog->functions[i].s_file = LittleLong (dfunctions[i].s_file);
2161 prog->functions[i].numparms = LittleLong (dfunctions[i].numparms);
2162 prog->functions[i].locals = LittleLong (dfunctions[i].locals);
2163 memcpy(prog->functions[i].parm_size, dfunctions[i].parm_size, sizeof(dfunctions[i].parm_size));
2164 if(prog->functions[i].first_statement >= prog->progs->numstatements)
2165 PRVM_ERROR("PRVM_LoadProgs: out of bounds function statement (function %d) in %s", i, PRVM_NAME);
2166 // TODO bounds check parm_start, s_name, s_file, numparms, locals, parm_size
2169 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
2171 prog->globaldefs[i].type = LittleShort (prog->globaldefs[i].type);
2172 prog->globaldefs[i].ofs = LittleShort (prog->globaldefs[i].ofs);
2173 prog->globaldefs[i].s_name = LittleLong (prog->globaldefs[i].s_name);
2174 // TODO bounds check ofs, s_name
2177 // copy the progs fields to the new fields list
2178 for (i = 0;i < prog->progs->numfielddefs;i++)
2180 prog->fielddefs[i].type = LittleShort (infielddefs[i].type);
2181 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
2182 PRVM_ERROR ("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", PRVM_NAME);
2183 prog->fielddefs[i].ofs = LittleShort (infielddefs[i].ofs);
2184 prog->fielddefs[i].s_name = LittleLong (infielddefs[i].s_name);
2185 // TODO bounds check ofs, s_name
2188 // append the required fields
2189 for (i = 0;i < (int) numrequiredfields;i++)
2191 prog->fielddefs[prog->progs->numfielddefs].type = required_field[i].type;
2192 prog->fielddefs[prog->progs->numfielddefs].ofs = prog->progs->entityfields;
2193 prog->fielddefs[prog->progs->numfielddefs].s_name = PRVM_SetEngineString(required_field[i].name);
2194 // TODO bounds check ofs, s_name
2195 if (prog->fielddefs[prog->progs->numfielddefs].type == ev_vector)
2196 prog->progs->entityfields += 3;
2198 prog->progs->entityfields++;
2199 prog->progs->numfielddefs++;
2201 prog->entityfields = prog->progs->entityfields;
2203 // check required functions
2204 for(i=0 ; i < numrequiredfunc ; i++)
2205 if(PRVM_ED_FindFunction(required_func[i]) == 0)
2206 PRVM_ERROR("%s: %s not found in %s",PRVM_NAME, required_func[i], filename);
2208 // check required globals
2209 for(i=0 ; i < numrequiredglobals ; i++)
2210 if(PRVM_ED_FindGlobal(required_global[i]) == 0)
2211 PRVM_ERROR("%s: %s not found in %s",PRVM_NAME, required_global[i], filename);
2213 for (i=0 ; i<prog->progs->numglobals ; i++)
2214 ((int *)prog->globals.generic)[i] = LittleLong (((int *)prog->globals.generic)[i]);
2216 // LordHavoc: bounds check anything static
2217 for (i = 0,st = prog->statements;i < prog->progs->numstatements;i++,st++)
2223 if ((unsigned short) st->a >= prog->progs->numglobals || st->b + i < 0 || st->b + i >= prog->progs->numstatements)
2224 PRVM_ERROR("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", i, PRVM_NAME);
2227 if (st->a + i < 0 || st->a + i >= prog->progs->numstatements)
2228 PRVM_ERROR("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, PRVM_NAME);
2230 // global global global
2265 if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->b >= prog->progs->numglobals || (unsigned short) st->c >= prog->progs->numglobals)
2266 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
2268 // global none global
2274 if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->c >= prog->progs->numglobals)
2275 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
2291 if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->b >= prog->progs->numglobals)
2292 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
2306 if ((unsigned short) st->a >= prog->progs->numglobals)
2307 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
2310 Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", st->op, i, PRVM_NAME);
2314 if(prog->progs->numstatements < 1)
2316 PRVM_ERROR("PRVM_LoadProgs: empty program in %s", PRVM_NAME);
2318 else switch(prog->statements[prog->progs->numstatements - 1].op)
2325 PRVM_ERROR("PRVM_LoadProgs: program may fall off the edge (does not end with RETURN, GOTO or DONE) in %s", PRVM_NAME);
2329 PRVM_LoadLNO(filename);
2333 if(*prvm_language.string)
2334 // in CSQC we really shouldn't be able to change how stuff works... sorry for now
2335 // later idea: include a list of authorized .po file checksums with the csprogs
2337 qboolean deftrans = !!strcmp(PRVM_NAME, "client");
2338 if(deftrans) // once we have dotranslate_ strings, ALWAYS use the opt-in method!
2340 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
2343 name = PRVM_GetString(prog->globaldefs[i].s_name);
2344 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2345 if(name && !strncmp(name, "dotranslate_", 12))
2352 if(!strcmp(prvm_language.string, "dump"))
2354 qfile_t *f = FS_OpenRealFile(va("%s.pot", filename), "w", false);
2355 Con_Printf("Dumping to %s.pot\n", filename);
2358 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
2361 name = PRVM_GetString(prog->globaldefs[i].s_name);
2362 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2363 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2365 prvm_eval_t *val = (prvm_eval_t *)(prog->globals.generic + prog->globaldefs[i].ofs);
2366 const char *value = PRVM_GetString(val->string);
2369 char buf[MAX_INPUTLINE];
2370 PRVM_PO_UnparseString(buf, value, sizeof(buf));
2371 FS_Printf(f, "msgid \"%s\"\nmsgstr \"\"\n\n", buf);
2380 po_t *po = PRVM_PO_Load(va("%s.%s.po", filename, prvm_language.string), prog->progs_mempool);
2383 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
2386 name = PRVM_GetString(prog->globaldefs[i].s_name);
2387 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2388 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2390 prvm_eval_t *val = (prvm_eval_t *)(prog->globals.generic + prog->globaldefs[i].ofs);
2391 const char *value = PRVM_GetString(val->string);
2394 value = PRVM_PO_Lookup(po, value);
2396 val->string = PRVM_SetEngineString(value);
2404 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
2407 name = PRVM_GetString(prog->globaldefs[i].s_name);
2408 //Con_Printf("found var %s\n", name);
2410 && !strncmp(name, "autocvar_", 9)
2411 && !(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
2414 prvm_eval_t *val = (prvm_eval_t *)(prog->globals.generic + prog->globaldefs[i].ofs);
2415 cvar_t *cvar = Cvar_FindVar(name + 9);
2416 //Con_Printf("PRVM_LoadProgs: autocvar global %s in %s, processing...\n", name, PRVM_NAME);
2421 Con_DPrintf("PRVM_LoadProgs: no cvar for autocvar global %s in %s, creating...\n", name, PRVM_NAME);
2422 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2425 if((float)((int)(val->_float)) == val->_float)
2426 dpsnprintf(buf, sizeof(buf), "%i", (int)(val->_float));
2428 dpsnprintf(buf, sizeof(buf), "%.9g", val->_float);
2432 dpsnprintf(buf, sizeof(buf), "%.9g %.9g %.9g", val->vector[0], val->vector[1], val->vector[2]); value = buf;
2435 value = PRVM_GetString(val->string);
2438 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, PRVM_NAME);
2441 cvar = Cvar_Get(name + 9, value, 0, NULL);
2442 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2444 val->string = PRVM_SetEngineString(cvar->string);
2445 cvar->globaldefindex_stringno[prog - prog_list] = val->string;
2448 PRVM_ERROR("PRVM_LoadProgs: could not create cvar for autocvar global %s in %s", name, PRVM_NAME);
2449 cvar->globaldefindex_progid[prog - prog_list] = prog->id;
2450 cvar->globaldefindex[prog - prog_list] = i;
2452 else if((cvar->flags & CVAR_PRIVATE) == 0)
2454 // MUST BE SYNCED WITH cvar.c Cvar_Set
2457 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2460 val->_float = cvar->value;
2464 VectorClear(val->vector);
2465 for (j = 0;j < 3;j++)
2467 while (*s && ISWHITESPACE(*s))
2471 val->vector[j] = atof(s);
2472 while (!ISWHITESPACE(*s))
2479 val->string = PRVM_SetEngineString(cvar->string);
2480 cvar->globaldefindex_stringno[prog - prog_list] = val->string;
2483 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, PRVM_NAME);
2486 cvar->globaldefindex_progid[prog - prog_list] = prog->id;
2487 cvar->globaldefindex[prog - prog_list] = i;
2490 Con_Printf("PRVM_LoadProgs: private cvar for autocvar global %s in %s\n", name, PRVM_NAME);
2496 prog->loaded = TRUE;
2498 // set flags & ddef_ts in prog
2504 PRVM_GCALL(init_cmd)();
2511 void PRVM_Fields_f (void)
2513 int i, j, ednum, used, usedamount;
2515 char tempstring[MAX_INPUTLINE], tempstring2[260];
2525 Con_Print("no progs loaded\n");
2532 Con_Print("prvm_fields <program name>\n");
2537 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
2540 counts = (int *)Mem_Alloc(tempmempool, prog->progs->numfielddefs * sizeof(int));
2541 for (ednum = 0;ednum < prog->max_edicts;ednum++)
2543 ed = PRVM_EDICT_NUM(ednum);
2544 if (ed->priv.required->free)
2546 for (i = 1;i < prog->progs->numfielddefs;i++)
2548 d = &prog->fielddefs[i];
2549 name = PRVM_GetString(d->s_name);
2550 if (name[strlen(name)-2] == '_')
2551 continue; // skip _x, _y, _z vars
2552 v = (int *)(ed->fields.vp + d->ofs);
2553 // if the value is still all 0, skip the field
2554 for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
2567 for (i = 0;i < prog->progs->numfielddefs;i++)
2569 d = &prog->fielddefs[i];
2570 name = PRVM_GetString(d->s_name);
2571 if (name[strlen(name)-2] == '_')
2572 continue; // skip _x, _y, _z vars
2573 switch(d->type & ~DEF_SAVEGLOBAL)
2576 strlcat(tempstring, "string ", sizeof(tempstring));
2579 strlcat(tempstring, "entity ", sizeof(tempstring));
2582 strlcat(tempstring, "function ", sizeof(tempstring));
2585 strlcat(tempstring, "field ", sizeof(tempstring));
2588 strlcat(tempstring, "void ", sizeof(tempstring));
2591 strlcat(tempstring, "float ", sizeof(tempstring));
2594 strlcat(tempstring, "vector ", sizeof(tempstring));
2597 strlcat(tempstring, "pointer ", sizeof(tempstring));
2600 dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
2601 strlcat(tempstring, tempstring2, sizeof(tempstring));
2604 if (strlen(name) > sizeof(tempstring2)-4)
2606 memcpy (tempstring2, name, sizeof(tempstring2)-4);
2607 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
2608 tempstring2[sizeof(tempstring2)-1] = 0;
2611 strlcat(tempstring, name, sizeof(tempstring));
2612 for (j = (int)strlen(name);j < 25;j++)
2613 strlcat(tempstring, " ", sizeof(tempstring));
2614 dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
2615 strlcat(tempstring, tempstring2, sizeof(tempstring));
2616 strlcat(tempstring, "\n", sizeof(tempstring));
2617 if (strlen(tempstring) >= sizeof(tempstring)/2)
2619 Con_Print(tempstring);
2625 usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
2629 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);
2634 void PRVM_Globals_f (void)
2637 const char *wildcard;
2643 Con_Print("no progs loaded\n");
2646 if(Cmd_Argc () < 2 || Cmd_Argc() > 3)
2648 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
2653 if(!PRVM_SetProgFromString (Cmd_Argv (1)))
2656 if( Cmd_Argc() == 3)
2657 wildcard = Cmd_Argv(2);
2661 Con_Printf("%s :", PRVM_NAME);
2663 for (i = 0;i < prog->progs->numglobaldefs;i++)
2666 if( !matchpattern( PRVM_GetString(prog->globaldefs[i].s_name), wildcard, 1) )
2671 Con_Printf("%s\n", PRVM_GetString(prog->globaldefs[i].s_name));
2673 Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->progs->numglobals, numculled, prog->progs->numglobals * 4);
2683 void PRVM_Global_f(void)
2686 if( Cmd_Argc() != 3 ) {
2687 Con_Printf( "prvm_global <program name> <global name>\n" );
2692 if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
2695 global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
2697 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2699 Con_Printf( "%s: %s\n", Cmd_Argv(2), PRVM_ValueString( (etype_t)global->type, (prvm_eval_t *) &prog->globals.generic[ global->ofs ] ) );
2708 void PRVM_GlobalSet_f(void)
2711 if( Cmd_Argc() != 4 ) {
2712 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2717 if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
2720 global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
2722 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2724 PRVM_ED_ParseEpair( NULL, global, Cmd_Argv(3), true );
2733 void PRVM_Init (void)
2735 Cmd_AddCommand ("prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2736 Cmd_AddCommand ("prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2737 Cmd_AddCommand ("prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2738 Cmd_AddCommand ("prvm_profile", PRVM_Profile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu)");
2739 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");
2740 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)");
2741 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)");
2742 Cmd_AddCommand ("prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2743 Cmd_AddCommand ("prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2744 Cmd_AddCommand ("prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2745 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)");
2746 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");
2747 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");
2748 Cmd_AddCommand ("prvm_printfunction", PRVM_PrintFunction_f, "prints a disassembly (QuakeC instructions) of the specified function in the selected VM (server, client, menu)");
2749 Cmd_AddCommand ("cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2750 Cmd_AddCommand ("menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2751 Cmd_AddCommand ("sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2753 Cvar_RegisterVariable (&prvm_language);
2754 Cvar_RegisterVariable (&prvm_traceqc);
2755 Cvar_RegisterVariable (&prvm_statementprofiling);
2756 Cvar_RegisterVariable (&prvm_timeprofiling);
2757 Cvar_RegisterVariable (&prvm_backtraceforwarnings);
2758 Cvar_RegisterVariable (&prvm_leaktest);
2759 Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames);
2760 Cvar_RegisterVariable (&prvm_errordump);
2761 Cvar_RegisterVariable (&prvm_reuseedicts_startuptime);
2762 Cvar_RegisterVariable (&prvm_reuseedicts_neverinsameframe);
2764 // COMMANDLINEOPTION: PRVM: -norunaway disables the runaway loop check (it might be impossible to exit DarkPlaces if used!)
2765 prvm_runawaycheck = !COM_CheckParm("-norunaway");
2775 void PRVM_InitProg(int prognr)
2777 static unsigned int progid = 0;
2779 if(prognr < 0 || prognr >= PRVM_MAXPROGS)
2780 Sys_Error("PRVM_InitProg: Invalid program number %i",prognr);
2782 prog = &prog_list[prognr];
2787 memset(prog, 0, sizeof(prvm_prog_t));
2788 prog->starttime = Sys_DoubleTime();
2789 prog->id = ++progid;
2791 prog->error_cmd = Host_Error;
2792 prog->leaktest_active = prvm_leaktest.integer != 0;
2795 int PRVM_GetProgNr(void)
2797 return prog - prog_list;
2800 void *_PRVM_Alloc(size_t buffersize, const char *filename, int fileline)
2802 return _Mem_Alloc(prog->progs_mempool, NULL, buffersize, 16, filename, fileline);
2805 void _PRVM_Free(void *buffer, const char *filename, int fileline)
2807 _Mem_Free(buffer, filename, fileline);
2810 void _PRVM_FreeAll(const char *filename, int fileline)
2813 prog->fielddefs = NULL;
2814 prog->functions = NULL;
2815 _Mem_EmptyPool(prog->progs_mempool, filename, fileline);
2818 // LordHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
2819 unsigned int PRVM_EDICT_NUM_ERROR(unsigned int n, const char *filename, int fileline)
2821 PRVM_ERROR ("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", PRVM_NAME, n, filename, fileline);
2825 sizebuf_t vm_tempstringsbuf;
2826 #define PRVM_KNOWNSTRINGBASE 0x40000000
2828 const char *PRVM_GetString(int num)
2833 VM_Warning("PRVM_GetString: Invalid string offset (%i < 0)\n", num);
2836 else if (num < prog->stringssize)
2838 // constant string from progs.dat
2839 return prog->strings + num;
2841 else if (num <= prog->stringssize + vm_tempstringsbuf.maxsize)
2843 // tempstring returned by engine to QC (becomes invalid after returning to engine)
2844 num -= prog->stringssize;
2845 if (num < vm_tempstringsbuf.cursize)
2846 return (char *)vm_tempstringsbuf.data + num;
2849 VM_Warning("PRVM_GetString: Invalid temp-string offset (%i >= %i vm_tempstringsbuf.cursize)\n", num, vm_tempstringsbuf.cursize);
2853 else if (num & PRVM_KNOWNSTRINGBASE)
2856 num = num - PRVM_KNOWNSTRINGBASE;
2857 if (num >= 0 && num < prog->numknownstrings)
2859 if (!prog->knownstrings[num])
2861 VM_Warning("PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
2864 return prog->knownstrings[num];
2868 VM_Warning("PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
2874 // invalid string offset
2875 VM_Warning("PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
2880 const char *PRVM_ChangeEngineString(int i, const char *s)
2883 i = i - PRVM_KNOWNSTRINGBASE;
2884 if(i < 0 || i >= prog->numknownstrings)
2885 PRVM_ERROR("PRVM_ChangeEngineString: s is not an engine string");
2886 old = prog->knownstrings[i];
2887 prog->knownstrings[i] = s;
2891 int PRVM_SetEngineString(const char *s)
2896 if (s >= prog->strings && s <= prog->strings + prog->stringssize)
2897 PRVM_ERROR("PRVM_SetEngineString: s in prog->strings area");
2898 // if it's in the tempstrings area, use a reserved range
2899 // (otherwise we'd get millions of useless string offsets cluttering the database)
2900 if (s >= (char *)vm_tempstringsbuf.data && s < (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.maxsize)
2902 return prog->stringssize + (s - (char *)vm_tempstringsbuf.data);
2904 // see if it's a known string address
2905 for (i = 0;i < prog->numknownstrings;i++)
2906 if (prog->knownstrings[i] == s)
2907 return PRVM_KNOWNSTRINGBASE + i;
2908 // new unknown engine string
2909 if (developer_insane.integer)
2910 Con_DPrintf("new engine string %p = \"%s\"\n", s, s);
2911 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2912 if (!prog->knownstrings[i])
2914 if (i >= prog->numknownstrings)
2916 if (i >= prog->maxknownstrings)
2918 const char **oldstrings = prog->knownstrings;
2919 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2920 const char **oldstrings_origin = prog->knownstrings_origin;
2921 prog->maxknownstrings += 128;
2922 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2923 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2924 if(prog->leaktest_active)
2925 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2926 if (prog->numknownstrings)
2928 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2929 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2930 if(prog->leaktest_active)
2931 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
2934 prog->numknownstrings++;
2936 prog->firstfreeknownstring = i + 1;
2937 prog->knownstrings[i] = s;
2938 prog->knownstrings_freeable[i] = false;
2939 if(prog->leaktest_active)
2940 prog->knownstrings_origin[i] = NULL;
2941 return PRVM_KNOWNSTRINGBASE + i;
2944 // temp string handling
2946 // all tempstrings go into this buffer consecutively, and it is reset
2947 // whenever PRVM_ExecuteProgram returns to the engine
2948 // (technically each PRVM_ExecuteProgram call saves the cursize value and
2949 // restores it on return, so multiple recursive calls can share the same
2951 // the buffer size is automatically grown as needed
2953 int PRVM_SetTempString(const char *s)
2959 size = (int)strlen(s) + 1;
2960 if (developer_insane.integer)
2961 Con_DPrintf("PRVM_SetTempString: cursize %i, size %i\n", vm_tempstringsbuf.cursize, size);
2962 if (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
2964 sizebuf_t old = vm_tempstringsbuf;
2965 if (vm_tempstringsbuf.cursize + size >= 1<<28)
2966 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);
2967 vm_tempstringsbuf.maxsize = max(vm_tempstringsbuf.maxsize, 65536);
2968 while (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
2969 vm_tempstringsbuf.maxsize *= 2;
2970 if (vm_tempstringsbuf.maxsize != old.maxsize || vm_tempstringsbuf.data == NULL)
2972 Con_DPrintf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, vm_tempstringsbuf.maxsize/1024);
2973 vm_tempstringsbuf.data = (unsigned char *) Mem_Alloc(sv_mempool, vm_tempstringsbuf.maxsize);
2975 memcpy(vm_tempstringsbuf.data, old.data, old.cursize);
2980 t = (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.cursize;
2982 vm_tempstringsbuf.cursize += size;
2983 return PRVM_SetEngineString(t);
2986 int PRVM_AllocString(size_t bufferlength, char **pointer)
2991 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2992 if (!prog->knownstrings[i])
2994 if (i >= prog->numknownstrings)
2996 if (i >= prog->maxknownstrings)
2998 const char **oldstrings = prog->knownstrings;
2999 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
3000 const char **oldstrings_origin = prog->knownstrings_origin;
3001 prog->maxknownstrings += 128;
3002 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3003 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
3004 if(prog->leaktest_active)
3005 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3006 if (prog->numknownstrings)
3008 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
3009 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
3010 if(prog->leaktest_active)
3011 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
3014 Mem_Free((char **)oldstrings);
3015 if (oldstrings_freeable)
3016 Mem_Free((unsigned char *)oldstrings_freeable);
3017 if (oldstrings_origin)
3018 Mem_Free((char **)oldstrings_origin);
3020 prog->numknownstrings++;
3022 prog->firstfreeknownstring = i + 1;
3023 prog->knownstrings[i] = (char *)PRVM_Alloc(bufferlength);
3024 prog->knownstrings_freeable[i] = true;
3025 if(prog->leaktest_active)
3026 prog->knownstrings_origin[i] = PRVM_AllocationOrigin();
3028 *pointer = (char *)(prog->knownstrings[i]);
3029 return PRVM_KNOWNSTRINGBASE + i;
3032 void PRVM_FreeString(int num)
3035 PRVM_ERROR("PRVM_FreeString: attempt to free a NULL string");
3036 else if (num >= 0 && num < prog->stringssize)
3037 PRVM_ERROR("PRVM_FreeString: attempt to free a constant string");
3038 else if (num >= PRVM_KNOWNSTRINGBASE && num < PRVM_KNOWNSTRINGBASE + prog->numknownstrings)
3040 num = num - PRVM_KNOWNSTRINGBASE;
3041 if (!prog->knownstrings[num])
3042 PRVM_ERROR("PRVM_FreeString: attempt to free a non-existent or already freed string");
3043 if (!prog->knownstrings_freeable[num])
3044 PRVM_ERROR("PRVM_FreeString: attempt to free a string owned by the engine");
3045 PRVM_Free((char *)prog->knownstrings[num]);
3046 if(prog->leaktest_active)
3047 if(prog->knownstrings_origin[num])
3048 PRVM_Free((char *)prog->knownstrings_origin[num]);
3049 prog->knownstrings[num] = NULL;
3050 prog->knownstrings_freeable[num] = false;
3051 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
3054 PRVM_ERROR("PRVM_FreeString: invalid string offset %i", num);
3057 static qboolean PRVM_IsStringReferenced(string_t string)
3061 for (i = 0;i < prog->progs->numglobaldefs;i++)
3063 ddef_t *d = &prog->globaldefs[i];
3064 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3066 if(string == ((prvm_eval_t *) &prog->globals.generic[d->ofs])->string)
3070 for(j = 0; j < prog->num_edicts; ++j)
3072 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3073 if (ed->priv.required->free)
3075 for (i=0; i<prog->progs->numfielddefs; ++i)
3077 ddef_t *d = &prog->fielddefs[i];
3078 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3080 if(string == ((prvm_eval_t *) &ed->fields.vp[d->ofs])->string)
3088 static qboolean PRVM_IsEdictRelevant(prvm_edict_t *edict)
3090 if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
3091 return true; // world or clients
3092 switch(prog - prog_list)
3094 case PRVM_SERVERPROG:
3096 entvars_t *ev = edict->fields.server;
3097 if(ev->solid) // can block other stuff, or is a trigger?
3099 if(ev->modelindex) // visible ent?
3101 if(ev->effects) // particle effect?
3103 if(ev->think) // has a think function?
3104 if(ev->nextthink > 0) // that actually will eventually run?
3108 if(*prvm_leaktest_ignore_classnames.string)
3110 if(strstr(va(" %s ", prvm_leaktest_ignore_classnames.string), va(" %s ", PRVM_GetString(ev->classname))))
3115 case PRVM_CLIENTPROG:
3117 // TODO someone add more stuff here
3118 cl_entvars_t *ev = edict->fields.client;
3119 if(ev->entnum) // csqc networked
3121 if(ev->modelindex) // visible ent?
3123 if(ev->effects) // particle effect?
3125 if(ev->think) // has a think function?
3126 if(ev->nextthink > 0) // that actually will eventually run?
3128 if(*prvm_leaktest_ignore_classnames.string)
3130 if(strstr(va(" %s ", prvm_leaktest_ignore_classnames.string), va(" %s ", PRVM_GetString(ev->classname))))
3136 // menu prog does not have classnames
3142 static qboolean PRVM_IsEdictReferenced(prvm_edict_t *edict, int mark)
3145 int edictnum = PRVM_NUM_FOR_EDICT(edict);
3146 const char *targetname = NULL;
3148 switch(prog - prog_list)
3150 case PRVM_SERVERPROG:
3151 targetname = PRVM_GetString(edict->fields.server->targetname);
3156 if(!*targetname) // ""
3159 for (i = 0;i < prog->progs->numglobaldefs;i++)
3161 ddef_t *d = &prog->globaldefs[i];
3162 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3164 if(edictnum == ((prvm_eval_t *) &prog->globals.generic[d->ofs])->edict)
3168 for(j = 0; j < prog->num_edicts; ++j)
3170 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3171 if (ed->priv.required->mark < mark)
3177 const char *target = PRVM_GetString(ed->fields.server->target);
3179 if(!strcmp(target, targetname))
3182 for (i=0; i<prog->progs->numfielddefs; ++i)
3184 ddef_t *d = &prog->fielddefs[i];
3185 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3187 if(edictnum == ((prvm_eval_t *) &ed->fields.vp[d->ofs])->edict)
3195 static void PRVM_MarkReferencedEdicts(void)
3201 for(j = 0; j < prog->num_edicts; ++j)
3203 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3204 if(ed->priv.required->free)
3206 ed->priv.required->mark = PRVM_IsEdictRelevant(ed) ? 1 : 0;
3213 for(j = 0; j < prog->num_edicts; ++j)
3215 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3216 if(ed->priv.required->free)
3218 if(ed->priv.required->mark)
3220 if(PRVM_IsEdictReferenced(ed, stage))
3222 ed->priv.required->mark = stage + 1;
3229 Con_DPrintf("leak check used %d stages to find all references\n", stage);
3232 void PRVM_LeakTest(void)
3235 qboolean leaked = false;
3237 if(!prog->leaktest_active)
3241 for (i = 0; i < prog->numknownstrings; ++i)
3243 if(prog->knownstrings[i])
3244 if(prog->knownstrings_freeable[i])
3245 if(prog->knownstrings_origin[i])
3246 if(!PRVM_IsStringReferenced(PRVM_KNOWNSTRINGBASE + i))
3248 Con_Printf("Unreferenced string found!\n Value: %s\n Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
3254 PRVM_MarkReferencedEdicts();
3255 for(j = 0; j < prog->num_edicts; ++j)
3257 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3258 if(ed->priv.required->free)
3260 if(!ed->priv.required->mark)
3261 if(ed->priv.required->allocation_origin)
3263 Con_Printf("Unreferenced edict found!\n Allocated at: %s\n", ed->priv.required->allocation_origin);
3264 PRVM_ED_Print(ed, NULL);
3270 for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
3272 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
3274 if(stringbuffer->origin)
3276 Con_Printf("Open string buffer handle found!\n Allocated at: %s\n", stringbuffer->origin);
3281 for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
3283 if(prog->openfiles[i])
3284 if(prog->openfiles_origin[i])
3286 Con_Printf("Open file handle found!\n Allocated at: %s\n", prog->openfiles_origin[i]);
3291 for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
3293 if(prog->opensearches[i])
3294 if(prog->opensearches_origin[i])
3296 Con_Printf("Open search handle found!\n Allocated at: %s\n", prog->opensearches_origin[i]);
3302 Con_Printf("Congratulations. No leaks found.\n");