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_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 (name[strlen(name)-2] == '_')
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 (name[strlen(name)-2] == '_')
736 continue; // skip _x, _y, _z vars
738 v = (int *)(ed->fields.vp + d->ofs);
740 // if the value is still all 0, skip the field
741 type = d->type & ~DEF_SAVEGLOBAL;
742 for (j=0 ; j<prvm_type_size[type] ; j++)
745 if (j == prvm_type_size[type])
748 FS_Printf(f,"\"%s\" ",name);
749 prog->statestring = va("PRVM_ED_Write, ent=%d, name=%s", i, name);
750 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString((etype_t)d->type, (prvm_eval_t *)v));
751 prog->statestring = NULL;
757 void PRVM_ED_PrintNum (int ent, const char *wildcard_fieldname)
759 PRVM_ED_Print(PRVM_EDICT_NUM(ent), wildcard_fieldname);
764 PRVM_ED_PrintEdicts_f
766 For debugging, prints all the entities in the current server
769 void PRVM_ED_PrintEdicts_f (void)
772 const char *wildcard_fieldname;
774 if(Cmd_Argc() < 2 || Cmd_Argc() > 3)
776 Con_Print("prvm_edicts <program name> <optional field name wildcard>\n");
781 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
785 wildcard_fieldname = Cmd_Argv(2);
787 wildcard_fieldname = NULL;
789 Con_Printf("%s: %i entities\n", PRVM_NAME, prog->num_edicts);
790 for (i=0 ; i<prog->num_edicts ; i++)
791 PRVM_ED_PrintNum (i, wildcard_fieldname);
800 For debugging, prints a single edict
803 void PRVM_ED_PrintEdict_f (void)
806 const char *wildcard_fieldname;
808 if(Cmd_Argc() < 3 || Cmd_Argc() > 4)
810 Con_Print("prvm_edict <program name> <edict number> <optional field name wildcard>\n");
815 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
818 i = atoi (Cmd_Argv(2));
819 if (i >= prog->num_edicts)
821 Con_Print("Bad edict number\n");
826 // Optional Wildcard Provided
827 wildcard_fieldname = Cmd_Argv(3);
830 wildcard_fieldname = NULL;
831 PRVM_ED_PrintNum (i, wildcard_fieldname);
843 // 2 possibilities : 1. just displaying the active edict count
844 // 2. making a function pointer [x]
845 void PRVM_ED_Count_f (void)
853 Con_Print("prvm_count <program name>\n");
858 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
861 if(prog->count_edicts)
862 prog->count_edicts();
866 for (i=0 ; i<prog->num_edicts ; i++)
868 ent = PRVM_EDICT_NUM(i);
869 if (ent->priv.required->free)
874 Con_Printf("num_edicts:%3i\n", prog->num_edicts);
875 Con_Printf("active :%3i\n", active);
882 ==============================================================================
886 FIXME: need to tag constants, doesn't really work
887 ==============================================================================
895 void PRVM_ED_WriteGlobals (qfile_t *f)
903 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
905 def = &prog->globaldefs[i];
907 if ( !(def->type & DEF_SAVEGLOBAL) )
909 type &= ~DEF_SAVEGLOBAL;
911 if (type != ev_string && type != ev_float && type != ev_entity)
914 name = PRVM_GetString(def->s_name);
916 if(developer_entityparsing.integer)
917 Con_Printf("PRVM_ED_WriteGlobals: at global %s\n", name);
919 prog->statestring = va("PRVM_ED_WriteGlobals, name=%s", name);
920 FS_Printf(f,"\"%s\" ", name);
921 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString((etype_t)type, (prvm_eval_t *)&prog->globals.generic[def->ofs]));
922 prog->statestring = NULL;
932 void PRVM_ED_ParseGlobals (const char *data)
934 char keyname[MAX_INPUTLINE];
940 if (!COM_ParseToken_Simple(&data, false, false))
941 PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace");
942 if (com_token[0] == '}')
945 if (developer_entityparsing.integer)
946 Con_Printf("Key: \"%s\"", com_token);
948 strlcpy (keyname, com_token, sizeof(keyname));
951 if (!COM_ParseToken_Simple(&data, false, true))
952 PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace");
954 if (developer_entityparsing.integer)
955 Con_Printf(" \"%s\"\n", com_token);
957 if (com_token[0] == '}')
958 PRVM_ERROR ("PRVM_ED_ParseGlobals: closing brace without data");
960 key = PRVM_ED_FindGlobal (keyname);
963 Con_DPrintf("'%s' is not a global on %s\n", keyname, PRVM_NAME);
967 if (!PRVM_ED_ParseEpair(NULL, key, com_token, true))
968 PRVM_ERROR ("PRVM_ED_ParseGlobals: parse error");
972 //============================================================================
979 Can parse either fields or globals
980 returns false if error
983 qboolean PRVM_ED_ParseEpair(prvm_edict_t *ent, ddef_t *key, const char *s, qboolean parsebackslash)
992 val = (prvm_eval_t *)(ent->fields.vp + key->ofs);
994 val = (prvm_eval_t *)(prog->globals.generic + key->ofs);
995 switch (key->type & ~DEF_SAVEGLOBAL)
998 l = (int)strlen(s) + 1;
999 val->string = PRVM_AllocString(l, &new_p);
1000 for (i = 0;i < l;i++)
1002 if (s[i] == '\\' && s[i+1] && parsebackslash)
1007 else if (s[i] == 'r')
1018 while (*s && ISWHITESPACE(*s))
1020 val->_float = atof(s);
1024 for (i = 0;i < 3;i++)
1026 while (*s && ISWHITESPACE(*s))
1030 val->vector[i] = atof(s);
1031 while (!ISWHITESPACE(*s))
1039 while (*s && ISWHITESPACE(*s))
1042 if (i >= prog->limit_edicts)
1043 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);
1044 while (i >= prog->max_edicts)
1045 PRVM_MEM_IncreaseEdicts();
1046 // if IncreaseEdicts was called the base pointer needs to be updated
1048 val = (prvm_eval_t *)(ent->fields.vp + key->ofs);
1049 val->edict = PRVM_EDICT_TO_PROG(PRVM_EDICT_NUM((int)i));
1055 Con_DPrintf("PRVM_ED_ParseEpair: Bogus field name %s in %s\n", s, PRVM_NAME);
1058 def = PRVM_ED_FindField(s + 1);
1061 Con_DPrintf("PRVM_ED_ParseEpair: Can't find field %s in %s\n", s, PRVM_NAME);
1064 val->_int = def->ofs;
1068 func = PRVM_ED_FindFunction(s);
1071 Con_Printf("PRVM_ED_ParseEpair: Can't find function %s in %s\n", s, PRVM_NAME);
1074 val->function = func - prog->functions;
1078 Con_Printf("PRVM_ED_ParseEpair: Unknown key->type %i for key \"%s\" on %s\n", key->type, PRVM_GetString(key->s_name), PRVM_NAME);
1088 Console command to send a string to QC function GameCommand of the
1092 sv_cmd adminmsg 3 "do not teamkill"
1093 cl_cmd someclientcommand
1094 menu_cmd somemenucommand
1096 All progs can support this extension; sg calls it in server QC, cg in client
1100 void PRVM_GameCommand(const char *whichprogs, const char *whichcmd)
1104 Con_Printf("%s text...\n", whichcmd);
1109 if(!PRVM_SetProgFromString(whichprogs))
1110 // note: this is not PRVM_SetProg because that one aborts "hard" using PRVM_Error
1111 // also, it makes printing error messages easier!
1113 Con_Printf("%s program not loaded.\n", whichprogs);
1117 if(!prog->funcoffsets.GameCommand)
1119 Con_Printf("%s program do not support GameCommand!\n", whichprogs);
1123 int restorevm_tempstringsbuf_cursize;
1128 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
1129 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(s ? s : "");
1130 PRVM_ExecuteProgram (prog->funcoffsets.GameCommand, "QC function GameCommand is missing");
1131 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1136 void PRVM_GameCommand_Server_f(void)
1138 PRVM_GameCommand("server", "sv_cmd");
1140 void PRVM_GameCommand_Client_f(void)
1142 PRVM_GameCommand("client", "cl_cmd");
1144 void PRVM_GameCommand_Menu_f(void)
1146 PRVM_GameCommand("menu", "menu_cmd");
1153 Console command to load a field of a specified edict
1156 void PRVM_ED_EdictGet_f(void)
1163 if(Cmd_Argc() != 4 && Cmd_Argc() != 5)
1165 Con_Print("prvm_edictget <program name> <edict number> <field> [<cvar>]\n");
1170 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1172 Con_Printf("Wrong program name %s !\n", Cmd_Argv(1));
1176 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1178 if((key = PRVM_ED_FindField(Cmd_Argv(3))) == 0)
1180 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1184 v = (prvm_eval_t *)(ed->fields.vp + key->ofs);
1185 s = PRVM_UglyValueString((etype_t)key->type, v);
1188 cvar_t *cvar = Cvar_FindVar(Cmd_Argv(4));
1189 if (cvar && cvar->flags & CVAR_READONLY)
1191 Con_Printf("prvm_edictget: %s is read-only\n", cvar->name);
1194 Cvar_Get(Cmd_Argv(4), s, 0, NULL);
1197 Con_Printf("%s\n", s);
1203 void PRVM_ED_GlobalGet_f(void)
1209 if(Cmd_Argc() != 3 && Cmd_Argc() != 4)
1211 Con_Print("prvm_globalget <program name> <global> [<cvar>]\n");
1216 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1218 Con_Printf("Wrong program name %s !\n", Cmd_Argv(1));
1222 key = PRVM_ED_FindGlobal(Cmd_Argv(2));
1225 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
1229 v = (prvm_eval_t *) &prog->globals.generic[key->ofs];
1230 s = PRVM_UglyValueString((etype_t)key->type, v);
1233 cvar_t *cvar = Cvar_FindVar(Cmd_Argv(3));
1234 if (cvar && cvar->flags & CVAR_READONLY)
1236 Con_Printf("prvm_globalget: %s is read-only\n", cvar->name);
1239 Cvar_Get(Cmd_Argv(3), s, 0, NULL);
1242 Con_Printf("%s\n", s);
1252 Console command to set a field of a specified edict
1255 void PRVM_ED_EdictSet_f(void)
1262 Con_Print("prvm_edictset <program name> <edict number> <field> <value>\n");
1267 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1269 Con_Printf("Wrong program name %s !\n", Cmd_Argv(1));
1273 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1275 if((key = PRVM_ED_FindField(Cmd_Argv(3))) == 0)
1276 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1278 PRVM_ED_ParseEpair(ed, key, Cmd_Argv(4), true);
1284 ====================
1287 Parses an edict out of the given string, returning the new position
1288 ed should be a properly initialized empty edict.
1289 Used for initial level load and for savegames.
1290 ====================
1292 const char *PRVM_ED_ParseEdict (const char *data, prvm_edict_t *ent)
1302 // go through all the dictionary pairs
1306 if (!COM_ParseToken_Simple(&data, false, false))
1307 PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1308 if (developer_entityparsing.integer)
1309 Con_Printf("Key: \"%s\"", com_token);
1310 if (com_token[0] == '}')
1313 // anglehack is to allow QuakeEd to write single scalar angles
1314 // and allow them to be turned into vectors. (FIXME...)
1315 if (!strcmp(com_token, "angle"))
1317 strlcpy (com_token, "angles", sizeof(com_token));
1323 // FIXME: change light to _light to get rid of this hack
1324 if (!strcmp(com_token, "light"))
1325 strlcpy (com_token, "light_lev", sizeof(com_token)); // hack for single light def
1327 strlcpy (keyname, com_token, sizeof(keyname));
1329 // another hack to fix keynames with trailing spaces
1330 n = strlen(keyname);
1331 while (n && keyname[n-1] == ' ')
1338 if (!COM_ParseToken_Simple(&data, false, false))
1339 PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1340 if (developer_entityparsing.integer)
1341 Con_Printf(" \"%s\"\n", com_token);
1343 if (com_token[0] == '}')
1344 PRVM_ERROR ("PRVM_ED_ParseEdict: closing brace without data");
1348 // ignore attempts to set key "" (this problem occurs in nehahra neh1m8.bsp)
1352 // keynames with a leading underscore are used for utility comments,
1353 // and are immediately discarded by quake
1354 if (keyname[0] == '_')
1357 key = PRVM_ED_FindField (keyname);
1360 Con_DPrintf("%s: '%s' is not a field\n", PRVM_NAME, keyname);
1367 strlcpy (temp, com_token, sizeof(temp));
1368 dpsnprintf (com_token, sizeof(com_token), "0 %s 0", temp);
1371 if (!PRVM_ED_ParseEpair(ent, key, com_token, strcmp(keyname, "wad") != 0))
1372 PRVM_ERROR ("PRVM_ED_ParseEdict: parse error");
1376 ent->priv.required->free = true;
1384 PRVM_ED_LoadFromFile
1386 The entities are directly placed in the array, rather than allocated with
1387 PRVM_ED_Alloc, because otherwise an error loading the map would have entity
1388 number references out of order.
1390 Creates a server's entity / program execution context by
1391 parsing textual entity definitions out of an ent file.
1393 Used for both fresh maps and savegame loads. A fresh map would also need
1394 to call PRVM_ED_CallSpawnFunctions () to let the objects initialize themselves.
1397 void PRVM_ED_LoadFromFile (const char *data)
1400 int parsed, inhibited, spawned, died;
1401 const char *funcname;
1409 prvm_reuseedicts_always_allow = realtime;
1414 // parse the opening brace
1415 if (!COM_ParseToken_Simple(&data, false, false))
1417 if (com_token[0] != '{')
1418 PRVM_ERROR ("PRVM_ED_LoadFromFile: %s: found %s when expecting {", PRVM_NAME, com_token);
1420 // CHANGED: this is not conform to PR_LoadFromFile
1421 if(prog->loadintoworld)
1423 prog->loadintoworld = false;
1424 ent = PRVM_EDICT_NUM(0);
1427 ent = PRVM_ED_Alloc();
1430 if (ent != prog->edicts) // hack
1431 memset (ent->fields.vp, 0, prog->progs->entityfields * 4);
1433 data = PRVM_ED_ParseEdict (data, ent);
1436 // remove the entity ?
1437 if(prog->load_edict && !prog->load_edict(ent))
1444 if (prog->funcoffsets.SV_OnEntityPreSpawnFunction)
1447 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1448 PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityPreSpawnFunction, "QC function SV_OnEntityPreSpawnFunction is missing");
1451 if(ent->priv.required->free)
1458 // immediately call spawn function, but only if there is a self global and a classname
1460 if(!ent->priv.required->free)
1461 if(prog->globaloffsets.self >= 0 && prog->fieldoffsets.classname >= 0)
1463 string_t handle = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.classname)->string;
1466 Con_Print("No classname for:\n");
1467 PRVM_ED_Print(ent, NULL);
1472 // look for the spawn function
1473 funcname = PRVM_GetString(handle);
1474 func = PRVM_ED_FindFunction (va("spawnfunc_%s", funcname));
1476 if(prog->globaloffsets.require_spawnfunc_prefix < 0)
1477 func = PRVM_ED_FindFunction (funcname);
1481 // check for OnEntityNoSpawnFunction
1482 if (prog->funcoffsets.SV_OnEntityNoSpawnFunction)
1485 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1486 PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityNoSpawnFunction, "QC function SV_OnEntityNoSpawnFunction is missing");
1490 if (developer.integer > 0) // don't confuse non-developers with errors
1492 Con_Print("No spawn function for:\n");
1493 PRVM_ED_Print(ent, NULL);
1496 continue; // not included in "inhibited" count
1502 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1503 PRVM_ExecuteProgram (func - prog->functions, "");
1507 if(!ent->priv.required->free)
1508 if (prog->funcoffsets.SV_OnEntityPostSpawnFunction)
1511 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1512 PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityPostSpawnFunction, "QC function SV_OnEntityPostSpawnFunction is missing");
1516 if (ent->priv.required->free)
1520 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);
1522 prvm_reuseedicts_always_allow = 0;
1525 void PRVM_FindOffsets(void)
1527 // field and global searches use -1 for NULL
1528 memset(&prog->fieldoffsets, -1, sizeof(prog->fieldoffsets));
1529 memset(&prog->globaloffsets, -1, sizeof(prog->globaloffsets));
1530 // functions use 0 for NULL
1531 memset(&prog->funcoffsets, 0, sizeof(prog->funcoffsets));
1533 // server and client qc use a lot of similar fields, so this is combined
1534 prog->fieldoffsets.SendEntity = PRVM_ED_FindFieldOffset("SendEntity");
1535 prog->fieldoffsets.SendFlags = PRVM_ED_FindFieldOffset("SendFlags");
1536 prog->fieldoffsets.Version = PRVM_ED_FindFieldOffset("Version");
1537 prog->fieldoffsets.alpha = PRVM_ED_FindFieldOffset("alpha");
1538 prog->fieldoffsets.ammo_cells1 = PRVM_ED_FindFieldOffset("ammo_cells1");
1539 prog->fieldoffsets.ammo_lava_nails = PRVM_ED_FindFieldOffset("ammo_lava_nails");
1540 prog->fieldoffsets.ammo_multi_rockets = PRVM_ED_FindFieldOffset("ammo_multi_rockets");
1541 prog->fieldoffsets.ammo_nails1 = PRVM_ED_FindFieldOffset("ammo_nails1");
1542 prog->fieldoffsets.ammo_plasma = PRVM_ED_FindFieldOffset("ammo_plasma");
1543 prog->fieldoffsets.ammo_rockets1 = PRVM_ED_FindFieldOffset("ammo_rockets1");
1544 prog->fieldoffsets.ammo_shells1 = PRVM_ED_FindFieldOffset("ammo_shells1");
1545 prog->fieldoffsets.angles = PRVM_ED_FindFieldOffset("angles");
1546 prog->fieldoffsets.button3 = PRVM_ED_FindFieldOffset("button3");
1547 prog->fieldoffsets.button4 = PRVM_ED_FindFieldOffset("button4");
1548 prog->fieldoffsets.button5 = PRVM_ED_FindFieldOffset("button5");
1549 prog->fieldoffsets.button6 = PRVM_ED_FindFieldOffset("button6");
1550 prog->fieldoffsets.button7 = PRVM_ED_FindFieldOffset("button7");
1551 prog->fieldoffsets.button8 = PRVM_ED_FindFieldOffset("button8");
1552 prog->fieldoffsets.button9 = PRVM_ED_FindFieldOffset("button9");
1553 prog->fieldoffsets.button10 = PRVM_ED_FindFieldOffset("button10");
1554 prog->fieldoffsets.button11 = PRVM_ED_FindFieldOffset("button11");
1555 prog->fieldoffsets.button12 = PRVM_ED_FindFieldOffset("button12");
1556 prog->fieldoffsets.button13 = PRVM_ED_FindFieldOffset("button13");
1557 prog->fieldoffsets.button14 = PRVM_ED_FindFieldOffset("button14");
1558 prog->fieldoffsets.button15 = PRVM_ED_FindFieldOffset("button15");
1559 prog->fieldoffsets.button16 = PRVM_ED_FindFieldOffset("button16");
1560 prog->fieldoffsets.buttonchat = PRVM_ED_FindFieldOffset("buttonchat");
1561 prog->fieldoffsets.buttonuse = PRVM_ED_FindFieldOffset("buttonuse");
1562 prog->fieldoffsets.chain = PRVM_ED_FindFieldOffset("chain");
1563 prog->fieldoffsets.classname = PRVM_ED_FindFieldOffset("classname");
1564 prog->fieldoffsets.clientcamera = PRVM_ED_FindFieldOffset("clientcamera");
1565 prog->fieldoffsets.clientcolors = PRVM_ED_FindFieldOffset("clientcolors");
1566 prog->fieldoffsets.clientstatus = PRVM_ED_FindFieldOffset("clientstatus");
1567 prog->fieldoffsets.color = PRVM_ED_FindFieldOffset("color");
1568 prog->fieldoffsets.colormod = PRVM_ED_FindFieldOffset("colormod");
1569 prog->fieldoffsets.contentstransition = PRVM_ED_FindFieldOffset("contentstransition");
1570 prog->fieldoffsets.cursor_active = PRVM_ED_FindFieldOffset("cursor_active");
1571 prog->fieldoffsets.cursor_screen = PRVM_ED_FindFieldOffset("cursor_screen");
1572 prog->fieldoffsets.cursor_trace_endpos = PRVM_ED_FindFieldOffset("cursor_trace_endpos");
1573 prog->fieldoffsets.cursor_trace_ent = PRVM_ED_FindFieldOffset("cursor_trace_ent");
1574 prog->fieldoffsets.cursor_trace_start = PRVM_ED_FindFieldOffset("cursor_trace_start");
1575 prog->fieldoffsets.customizeentityforclient = PRVM_ED_FindFieldOffset("customizeentityforclient");
1576 prog->fieldoffsets.dimension_hit = PRVM_ED_FindFieldOffset("dimension_hit");
1577 prog->fieldoffsets.dimension_solid = PRVM_ED_FindFieldOffset("dimension_solid");
1578 prog->fieldoffsets.disableclientprediction = PRVM_ED_FindFieldOffset("disableclientprediction");
1579 prog->fieldoffsets.discardabledemo = PRVM_ED_FindFieldOffset("discardabledemo");
1580 prog->fieldoffsets.dphitcontentsmask = PRVM_ED_FindFieldOffset("dphitcontentsmask");
1581 prog->fieldoffsets.drawonlytoclient = PRVM_ED_FindFieldOffset("drawonlytoclient");
1582 prog->fieldoffsets.exteriormodeltoclient = PRVM_ED_FindFieldOffset("exteriormodeltoclient");
1583 prog->fieldoffsets.fatness = PRVM_ED_FindFieldOffset("fatness");
1584 prog->fieldoffsets.forceshader = PRVM_ED_FindFieldOffset("forceshader");
1585 prog->fieldoffsets.frame = PRVM_ED_FindFieldOffset("frame");
1586 prog->fieldoffsets.frame1time = PRVM_ED_FindFieldOffset("frame1time");
1587 prog->fieldoffsets.frame2 = PRVM_ED_FindFieldOffset("frame2");
1588 prog->fieldoffsets.frame2time = PRVM_ED_FindFieldOffset("frame2time");
1589 prog->fieldoffsets.frame3 = PRVM_ED_FindFieldOffset("frame3");
1590 prog->fieldoffsets.frame3time = PRVM_ED_FindFieldOffset("frame3time");
1591 prog->fieldoffsets.frame4 = PRVM_ED_FindFieldOffset("frame4");
1592 prog->fieldoffsets.frame4time = PRVM_ED_FindFieldOffset("frame4time");
1593 prog->fieldoffsets.fullbright = PRVM_ED_FindFieldOffset("fullbright");
1594 prog->fieldoffsets.glow_color = PRVM_ED_FindFieldOffset("glow_color");
1595 prog->fieldoffsets.glow_size = PRVM_ED_FindFieldOffset("glow_size");
1596 prog->fieldoffsets.glow_trail = PRVM_ED_FindFieldOffset("glow_trail");
1597 prog->fieldoffsets.glowmod = PRVM_ED_FindFieldOffset("glowmod");
1598 prog->fieldoffsets.gravity = PRVM_ED_FindFieldOffset("gravity");
1599 prog->fieldoffsets.groundentity = PRVM_ED_FindFieldOffset("groundentity");
1600 prog->fieldoffsets.hull = PRVM_ED_FindFieldOffset("hull");
1601 prog->fieldoffsets.ideal_yaw = PRVM_ED_FindFieldOffset("ideal_yaw");
1602 prog->fieldoffsets.idealpitch = PRVM_ED_FindFieldOffset("idealpitch");
1603 prog->fieldoffsets.items2 = PRVM_ED_FindFieldOffset("items2");
1604 prog->fieldoffsets.lerpfrac = PRVM_ED_FindFieldOffset("lerpfrac");
1605 prog->fieldoffsets.lerpfrac3 = PRVM_ED_FindFieldOffset("lerpfrac3");
1606 prog->fieldoffsets.lerpfrac4 = PRVM_ED_FindFieldOffset("lerpfrac4");
1607 prog->fieldoffsets.light_lev = PRVM_ED_FindFieldOffset("light_lev");
1608 prog->fieldoffsets.message = PRVM_ED_FindFieldOffset("message");
1609 prog->fieldoffsets.modelflags = PRVM_ED_FindFieldOffset("modelflags");
1610 prog->fieldoffsets.movement = PRVM_ED_FindFieldOffset("movement");
1611 prog->fieldoffsets.movetypesteplandevent = PRVM_ED_FindFieldOffset("movetypesteplandevent");
1612 prog->fieldoffsets.netaddress = PRVM_ED_FindFieldOffset("netaddress");
1613 prog->fieldoffsets.nextthink = PRVM_ED_FindFieldOffset("nextthink");
1614 prog->fieldoffsets.nodrawtoclient = PRVM_ED_FindFieldOffset("nodrawtoclient");
1615 prog->fieldoffsets.pflags = PRVM_ED_FindFieldOffset("pflags");
1616 prog->fieldoffsets.ping = PRVM_ED_FindFieldOffset("ping");
1617 prog->fieldoffsets.packetloss = PRVM_ED_FindFieldOffset("ping_packetloss");
1618 prog->fieldoffsets.movementloss = PRVM_ED_FindFieldOffset("ping_movementloss");
1619 prog->fieldoffsets.pitch_speed = PRVM_ED_FindFieldOffset("pitch_speed");
1620 prog->fieldoffsets.playermodel = PRVM_ED_FindFieldOffset("playermodel");
1621 prog->fieldoffsets.playerskin = PRVM_ED_FindFieldOffset("playerskin");
1622 prog->fieldoffsets.pmodel = PRVM_ED_FindFieldOffset("pmodel");
1623 prog->fieldoffsets.punchvector = PRVM_ED_FindFieldOffset("punchvector");
1624 prog->fieldoffsets.renderamt = PRVM_ED_FindFieldOffset("renderamt"); // HalfLife support
1625 prog->fieldoffsets.renderflags = PRVM_ED_FindFieldOffset("renderflags");
1626 prog->fieldoffsets.rendermode = PRVM_ED_FindFieldOffset("rendermode"); // HalfLife support
1627 prog->fieldoffsets.scale = PRVM_ED_FindFieldOffset("scale");
1628 prog->fieldoffsets.shadertime = PRVM_ED_FindFieldOffset("shadertime");
1629 prog->fieldoffsets.skeletonindex = PRVM_ED_FindFieldOffset("skeletonindex");
1630 prog->fieldoffsets.style = PRVM_ED_FindFieldOffset("style");
1631 prog->fieldoffsets.tag_entity = PRVM_ED_FindFieldOffset("tag_entity");
1632 prog->fieldoffsets.tag_index = PRVM_ED_FindFieldOffset("tag_index");
1633 prog->fieldoffsets.think = PRVM_ED_FindFieldOffset("think");
1634 prog->fieldoffsets.viewmodelforclient = PRVM_ED_FindFieldOffset("viewmodelforclient");
1635 prog->fieldoffsets.viewzoom = PRVM_ED_FindFieldOffset("viewzoom");
1636 prog->fieldoffsets.yaw_speed = PRVM_ED_FindFieldOffset("yaw_speed");
1637 prog->fieldoffsets.bouncefactor = PRVM_ED_FindFieldOffset("bouncefactor");
1638 prog->fieldoffsets.bouncestop = PRVM_ED_FindFieldOffset("bouncestop");
1640 prog->fieldoffsets.solid = PRVM_ED_FindFieldOffset("solid");
1641 prog->fieldoffsets.movetype = PRVM_ED_FindFieldOffset("movetype");
1642 prog->fieldoffsets.modelindex = PRVM_ED_FindFieldOffset("modelindex");
1643 prog->fieldoffsets.mins = PRVM_ED_FindFieldOffset("mins");
1644 prog->fieldoffsets.maxs = PRVM_ED_FindFieldOffset("maxs");
1645 prog->fieldoffsets.mass = PRVM_ED_FindFieldOffset("mass");
1646 prog->fieldoffsets.origin = PRVM_ED_FindFieldOffset("origin");
1647 prog->fieldoffsets.velocity = PRVM_ED_FindFieldOffset("velocity");
1648 //prog->fieldoffsets.axis_forward = PRVM_ED_FindFieldOffset("axis_forward");
1649 //prog->fieldoffsets.axis_left = PRVM_ED_FindFieldOffset("axis_left");
1650 //prog->fieldoffsets.axis_up = PRVM_ED_FindFieldOffset("axis_up");
1651 //prog->fieldoffsets.spinvelocity = PRVM_ED_FindFieldOffset("spinvelocity");
1652 prog->fieldoffsets.angles = PRVM_ED_FindFieldOffset("angles");
1653 prog->fieldoffsets.avelocity = PRVM_ED_FindFieldOffset("avelocity");
1654 prog->fieldoffsets.aiment = PRVM_ED_FindFieldOffset("aiment");
1655 prog->fieldoffsets.enemy = PRVM_ED_FindFieldOffset("enemy");
1656 prog->fieldoffsets.jointtype = PRVM_ED_FindFieldOffset("jointtype");
1657 prog->fieldoffsets.movedir = PRVM_ED_FindFieldOffset("movedir");
1659 prog->fieldoffsets.camera_transform = PRVM_ED_FindFieldOffset("camera_transform");
1660 prog->fieldoffsets.userwavefunc_param0 = PRVM_ED_FindFieldOffset("userwavefunc_param0");
1661 prog->fieldoffsets.userwavefunc_param1 = PRVM_ED_FindFieldOffset("userwavefunc_param1");
1662 prog->fieldoffsets.userwavefunc_param2 = PRVM_ED_FindFieldOffset("userwavefunc_param2");
1663 prog->fieldoffsets.userwavefunc_param3 = PRVM_ED_FindFieldOffset("userwavefunc_param3");
1665 prog->fieldoffsets.crypto_keyfp = PRVM_ED_FindFieldOffset("crypto_keyfp");
1666 prog->fieldoffsets.crypto_mykeyfp = PRVM_ED_FindFieldOffset("crypto_mykeyfp");
1667 prog->fieldoffsets.crypto_idfp = PRVM_ED_FindFieldOffset("crypto_idfp");
1668 prog->fieldoffsets.crypto_encryptmethod = PRVM_ED_FindFieldOffset("crypto_encryptmethod");
1669 prog->fieldoffsets.crypto_signmethod = PRVM_ED_FindFieldOffset("crypto_signmethod");
1671 prog->funcoffsets.CSQC_ConsoleCommand = PRVM_ED_FindFunctionOffset("CSQC_ConsoleCommand");
1672 prog->funcoffsets.CSQC_Ent_Remove = PRVM_ED_FindFunctionOffset("CSQC_Ent_Remove");
1673 prog->funcoffsets.CSQC_Ent_Spawn = PRVM_ED_FindFunctionOffset("CSQC_Ent_Spawn");
1674 prog->funcoffsets.CSQC_Ent_Update = PRVM_ED_FindFunctionOffset("CSQC_Ent_Update");
1675 prog->funcoffsets.CSQC_Event = PRVM_ED_FindFunctionOffset("CSQC_Event");
1676 prog->funcoffsets.CSQC_Event_Sound = PRVM_ED_FindFunctionOffset("CSQC_Event_Sound");
1677 prog->funcoffsets.CSQC_Init = PRVM_ED_FindFunctionOffset("CSQC_Init");
1678 prog->funcoffsets.CSQC_InputEvent = PRVM_ED_FindFunctionOffset("CSQC_InputEvent");
1679 prog->funcoffsets.CSQC_Parse_CenterPrint = PRVM_ED_FindFunctionOffset("CSQC_Parse_CenterPrint");
1680 prog->funcoffsets.CSQC_Parse_Print = PRVM_ED_FindFunctionOffset("CSQC_Parse_Print");
1681 prog->funcoffsets.CSQC_Parse_StuffCmd = PRVM_ED_FindFunctionOffset("CSQC_Parse_StuffCmd");
1682 prog->funcoffsets.CSQC_Parse_TempEntity = PRVM_ED_FindFunctionOffset("CSQC_Parse_TempEntity");
1683 prog->funcoffsets.CSQC_Shutdown = PRVM_ED_FindFunctionOffset("CSQC_Shutdown");
1684 prog->funcoffsets.CSQC_UpdateView = PRVM_ED_FindFunctionOffset("CSQC_UpdateView");
1685 prog->funcoffsets.EndFrame = PRVM_ED_FindFunctionOffset("EndFrame");
1686 prog->funcoffsets.GameCommand = PRVM_ED_FindFunctionOffset("GameCommand");
1687 prog->funcoffsets.Gecko_Query = PRVM_ED_FindFunctionOffset("Gecko_Query");
1688 prog->funcoffsets.RestoreGame = PRVM_ED_FindFunctionOffset("RestoreGame");
1689 prog->funcoffsets.SV_ChangeTeam = PRVM_ED_FindFunctionOffset("SV_ChangeTeam");
1690 prog->funcoffsets.SV_OnEntityNoSpawnFunction = PRVM_ED_FindFunctionOffset("SV_OnEntityNoSpawnFunction");
1691 prog->funcoffsets.SV_OnEntityPostSpawnFunction = PRVM_ED_FindFunctionOffset("SV_OnEntityPostSpawnFunction");
1692 prog->funcoffsets.SV_OnEntityPreSpawnFunction = PRVM_ED_FindFunctionOffset("SV_OnEntityPreSpawnFunction");
1693 prog->funcoffsets.SV_ParseClientCommand = PRVM_ED_FindFunctionOffset("SV_ParseClientCommand");
1694 prog->funcoffsets.SV_PausedTic = PRVM_ED_FindFunctionOffset("SV_PausedTic");
1695 prog->funcoffsets.SV_PlayerPhysics = PRVM_ED_FindFunctionOffset("SV_PlayerPhysics");
1696 prog->funcoffsets.SV_Shutdown = PRVM_ED_FindFunctionOffset("SV_Shutdown");
1697 prog->funcoffsets.URI_Get_Callback = PRVM_ED_FindFunctionOffset("URI_Get_Callback");
1698 prog->globaloffsets.SV_InitCmd = PRVM_ED_FindGlobalOffset("SV_InitCmd");
1699 prog->globaloffsets.coop = PRVM_ED_FindGlobalOffset("coop");
1700 prog->globaloffsets.deathmatch = PRVM_ED_FindGlobalOffset("deathmatch");
1701 prog->globaloffsets.dmg_origin = PRVM_ED_FindGlobalOffset("dmg_origin");
1702 prog->globaloffsets.dmg_save = PRVM_ED_FindGlobalOffset("dmg_save");
1703 prog->globaloffsets.dmg_take = PRVM_ED_FindGlobalOffset("dmg_take");
1704 prog->globaloffsets.drawfont = PRVM_ED_FindGlobalOffset("drawfont");
1705 prog->globaloffsets.drawfontscale = PRVM_ED_FindGlobalOffset("drawfontscale");
1706 prog->globaloffsets.gettaginfo_forward = PRVM_ED_FindGlobalOffset("gettaginfo_forward");
1707 prog->globaloffsets.gettaginfo_name = PRVM_ED_FindGlobalOffset("gettaginfo_name");
1708 prog->globaloffsets.gettaginfo_offset = PRVM_ED_FindGlobalOffset("gettaginfo_offset");
1709 prog->globaloffsets.gettaginfo_parent = PRVM_ED_FindGlobalOffset("gettaginfo_parent");
1710 prog->globaloffsets.gettaginfo_right = PRVM_ED_FindGlobalOffset("gettaginfo_right");
1711 prog->globaloffsets.gettaginfo_up = PRVM_ED_FindGlobalOffset("gettaginfo_up");
1712 prog->globaloffsets.transparent_offset = PRVM_ED_FindGlobalOffset("transparent_offset");
1713 prog->globaloffsets.intermission = PRVM_ED_FindGlobalOffset("intermission");
1714 prog->globaloffsets.require_spawnfunc_prefix = PRVM_ED_FindGlobalOffset("require_spawnfunc_prefix");
1715 prog->globaloffsets.sb_showscores = PRVM_ED_FindGlobalOffset("sb_showscores");
1716 prog->globaloffsets.self = PRVM_ED_FindGlobalOffset("self");
1717 prog->globaloffsets.serverdeltatime = PRVM_ED_FindGlobalOffset("serverdeltatime");
1718 prog->globaloffsets.serverprevtime = PRVM_ED_FindGlobalOffset("serverprevtime");
1719 prog->globaloffsets.servertime = PRVM_ED_FindGlobalOffset("servertime");
1720 prog->globaloffsets.time = PRVM_ED_FindGlobalOffset("time");
1721 prog->globaloffsets.trace_allsolid = PRVM_ED_FindGlobalOffset("trace_allsolid");
1722 prog->globaloffsets.trace_dphitcontents = PRVM_ED_FindGlobalOffset("trace_dphitcontents");
1723 prog->globaloffsets.trace_dphitq3surfaceflags = PRVM_ED_FindGlobalOffset("trace_dphitq3surfaceflags");
1724 prog->globaloffsets.trace_dphittexturename = PRVM_ED_FindGlobalOffset("trace_dphittexturename");
1725 prog->globaloffsets.trace_dpstartcontents = PRVM_ED_FindGlobalOffset("trace_dpstartcontents");
1726 prog->globaloffsets.trace_endpos = PRVM_ED_FindGlobalOffset("trace_endpos");
1727 prog->globaloffsets.trace_ent = PRVM_ED_FindGlobalOffset("trace_ent");
1728 prog->globaloffsets.trace_fraction = PRVM_ED_FindGlobalOffset("trace_fraction");
1729 prog->globaloffsets.trace_inopen = PRVM_ED_FindGlobalOffset("trace_inopen");
1730 prog->globaloffsets.trace_inwater = PRVM_ED_FindGlobalOffset("trace_inwater");
1731 prog->globaloffsets.trace_networkentity = PRVM_ED_FindGlobalOffset("trace_networkentity");
1732 prog->globaloffsets.trace_plane_dist = PRVM_ED_FindGlobalOffset("trace_plane_dist");
1733 prog->globaloffsets.trace_plane_normal = PRVM_ED_FindGlobalOffset("trace_plane_normal");
1734 prog->globaloffsets.trace_startsolid = PRVM_ED_FindGlobalOffset("trace_startsolid");
1735 prog->globaloffsets.v_forward = PRVM_ED_FindGlobalOffset("v_forward");
1736 prog->globaloffsets.v_right = PRVM_ED_FindGlobalOffset("v_right");
1737 prog->globaloffsets.v_up = PRVM_ED_FindGlobalOffset("v_up");
1738 prog->globaloffsets.view_angles = PRVM_ED_FindGlobalOffset("view_angles");
1739 prog->globaloffsets.view_punchangle = PRVM_ED_FindGlobalOffset("view_punchangle");
1740 prog->globaloffsets.view_punchvector = PRVM_ED_FindGlobalOffset("view_punchvector");
1741 prog->globaloffsets.worldstatus = PRVM_ED_FindGlobalOffset("worldstatus");
1742 prog->globaloffsets.particles_alphamin = PRVM_ED_FindGlobalOffset("particles_alphamin");
1743 prog->globaloffsets.particles_alphamax = PRVM_ED_FindGlobalOffset("particles_alphamax");
1744 prog->globaloffsets.particles_colormin = PRVM_ED_FindGlobalOffset("particles_colormin");
1745 prog->globaloffsets.particles_colormax = PRVM_ED_FindGlobalOffset("particles_colormax");
1747 // menu qc only uses some functions, nothing else
1748 prog->funcoffsets.m_draw = PRVM_ED_FindFunctionOffset("m_draw");
1749 prog->funcoffsets.m_init = PRVM_ED_FindFunctionOffset("m_init");
1750 prog->funcoffsets.m_keydown = PRVM_ED_FindFunctionOffset("m_keydown");
1751 prog->funcoffsets.m_keyup = PRVM_ED_FindFunctionOffset("m_keyup");
1752 prog->funcoffsets.m_shutdown = PRVM_ED_FindFunctionOffset("m_shutdown");
1753 prog->funcoffsets.m_toggle = PRVM_ED_FindFunctionOffset("m_toggle");
1754 prog->funcoffsets.m_newmap = PRVM_ED_FindFunctionOffset("m_newmap");
1759 typedef struct dpfield_s
1766 #define DPFIELDS (sizeof(dpfields) / sizeof(dpfield_t))
1768 dpfield_t dpfields[] =
1779 #define PO_HASHSIZE 16384
1780 typedef struct po_string_s
1783 struct po_string_s *nextonhashchain;
1788 po_string_t *hashtable[PO_HASHSIZE];
1791 void PRVM_PO_UnparseString(char *out, const char *in, size_t outsize)
1800 case '\a': if(outsize >= 2) { *out++ = '\\'; *out++ = 'a'; outsize -= 2; } break;
1801 case '\b': if(outsize >= 2) { *out++ = '\\'; *out++ = 'b'; outsize -= 2; } break;
1802 case '\t': if(outsize >= 2) { *out++ = '\\'; *out++ = 't'; outsize -= 2; } break;
1803 case '\r': if(outsize >= 2) { *out++ = '\\'; *out++ = 'r'; outsize -= 2; } break;
1804 case '\n': if(outsize >= 2) { *out++ = '\\'; *out++ = 'n'; outsize -= 2; } break;
1805 case '\\': if(outsize >= 2) { *out++ = '\\'; *out++ = '\\'; outsize -= 2; } break;
1806 case '"': if(outsize >= 2) { *out++ = '\\'; *out++ = '"'; outsize -= 2; } break;
1808 if(*in >= 0 && *in <= 0x1F)
1813 *out++ = '0' + ((*in & 0700) >> 6);
1814 *out++ = '0' + ((*in & 0070) >> 3);
1815 *out++ = '0' + ((*in & 0007));
1832 void PRVM_PO_ParseString(char *out, const char *in, size_t outsize)
1845 case 'a': if(outsize > 0) { *out++ = '\a'; --outsize; } break;
1846 case 'b': if(outsize > 0) { *out++ = '\b'; --outsize; } break;
1847 case 't': if(outsize > 0) { *out++ = '\t'; --outsize; } break;
1848 case 'r': if(outsize > 0) { *out++ = '\r'; --outsize; } break;
1849 case 'n': if(outsize > 0) { *out++ = '\n'; --outsize; } break;
1850 case '\\': if(outsize > 0) { *out++ = '\\'; --outsize; } break;
1851 case '"': if(outsize > 0) { *out++ = '"'; --outsize; } break;
1852 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
1856 if(*in >= '0' && *in <= '7')
1859 *out = (*out << 3) | (*in - '0');
1862 if(*in >= '0' && *in <= '7')
1865 *out = (*out << 3) | (*in - '0');
1876 if(outsize > 0) { *out++ = *in; --outsize; }
1891 po_t *PRVM_PO_Load(const char *filename, mempool_t *pool)
1896 char inbuf[MAX_INPUTLINE];
1897 char decodedbuf[MAX_INPUTLINE];
1900 po_string_t thisstr;
1901 const char *buf = (const char *) FS_LoadFile(filename, pool, true, NULL);
1906 memset(&thisstr, 0, sizeof(thisstr)); // hush compiler warning
1908 po = (po_t *)Mem_Alloc(pool, sizeof(*po));
1909 memset(po, 0, sizeof(*po));
1917 p = strchr(p, '\n');
1923 if(*p == '\r' || *p == '\n')
1928 if(!strncmp(p, "msgid \"", 7))
1933 else if(!strncmp(p, "msgstr \"", 8))
1940 p = strchr(p, '\n');
1950 q = strchr(p, '\n');
1957 if((size_t)(q - p) >= (size_t) sizeof(inbuf))
1959 strlcpy(inbuf, p, q - p); // not - 1, because this adds a NUL
1960 PRVM_PO_ParseString(decodedbuf + decodedpos, inbuf, sizeof(decodedbuf) - decodedpos);
1961 decodedpos += strlen(decodedbuf + decodedpos);
1971 Mem_Free(thisstr.key);
1972 thisstr.key = (char *)Mem_Alloc(pool, decodedpos + 1);
1973 memcpy(thisstr.key, decodedbuf, decodedpos + 1);
1975 else if(decodedpos > 0 && thisstr.key) // skip empty translation results
1977 thisstr.value = (char *)Mem_Alloc(pool, decodedpos + 1);
1978 memcpy(thisstr.value, decodedbuf, decodedpos + 1);
1979 hashindex = CRC_Block((const unsigned char *) thisstr.key, strlen(thisstr.key)) % PO_HASHSIZE;
1980 thisstr.nextonhashchain = po->hashtable[hashindex];
1981 po->hashtable[hashindex] = (po_string_t *)Mem_Alloc(pool, sizeof(thisstr));
1982 memcpy(po->hashtable[hashindex], &thisstr, sizeof(thisstr));
1983 memset(&thisstr, 0, sizeof(thisstr));
1987 Mem_Free((char *) buf);
1990 const char *PRVM_PO_Lookup(po_t *po, const char *str)
1992 int hashindex = CRC_Block((const unsigned char *) str, strlen(str)) % PO_HASHSIZE;
1993 po_string_t *p = po->hashtable[hashindex];
1996 if(!strcmp(str, p->key))
1998 p = p->nextonhashchain;
2002 void PRVM_PO_Destroy(po_t *po)
2005 for(i = 0; i < PO_HASHSIZE; ++i)
2007 po_string_t *p = po->hashtable[i];
2011 p = p->nextonhashchain;
2020 void PRVM_LeakTest(void);
2021 void PRVM_ResetProg(void)
2024 PRVM_GCALL(reset_cmd)();
2025 Mem_FreePool(&prog->progs_mempool);
2027 PRVM_PO_Destroy((po_t *) prog->po);
2028 memset(prog,0,sizeof(prvm_prog_t));
2029 prog->starttime = Sys_DoubleTime();
2037 void PRVM_LoadLNO( const char *progname ) {
2038 fs_offset_t filesize;
2040 unsigned int *header;
2043 FS_StripExtension( progname, filename, sizeof( filename ) );
2044 strlcat( filename, ".lno", sizeof( filename ) );
2046 lno = FS_LoadFile( filename, tempmempool, false, &filesize );
2052 <Spike> SafeWrite (h, &lnotype, sizeof(int));
2053 <Spike> SafeWrite (h, &version, sizeof(int));
2054 <Spike> SafeWrite (h, &numglobaldefs, sizeof(int));
2055 <Spike> SafeWrite (h, &numpr_globals, sizeof(int));
2056 <Spike> SafeWrite (h, &numfielddefs, sizeof(int));
2057 <Spike> SafeWrite (h, &numstatements, sizeof(int));
2058 <Spike> SafeWrite (h, statement_linenums, numstatements*sizeof(int));
2060 if( (unsigned) filesize < (6 + prog->progs->numstatements) * sizeof( int ) ) {
2065 header = (unsigned int *) lno;
2066 if( header[ 0 ] == *(unsigned int *) "LNOF" &&
2067 LittleLong( header[ 1 ] ) == 1 &&
2068 (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs->numglobaldefs &&
2069 (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs->numglobals &&
2070 (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs->numfielddefs &&
2071 (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs->numstatements )
2073 prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs->numstatements * sizeof( int ) );
2074 memcpy( prog->statement_linenums, (int *) lno + 6, prog->progs->numstatements * sizeof( int ) );
2084 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)
2088 ddef_t *infielddefs;
2089 dfunction_t *dfunctions;
2090 fs_offset_t filesize;
2092 if( prog->loaded ) {
2093 PRVM_ERROR ("PRVM_LoadProgs: there is already a %s program loaded!", PRVM_NAME );
2096 prog->progs = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
2097 if (prog->progs == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
2098 PRVM_ERROR ("PRVM_LoadProgs: couldn't load %s for %s", filename, PRVM_NAME);
2099 // TODO bounds check header fields (e.g. numstatements), they must never go behind end of file
2101 Con_DPrintf("%s programs occupy %iK.\n", PRVM_NAME, (int)(filesize/1024));
2103 prog->filecrc = CRC_Block((unsigned char *)prog->progs, filesize);
2105 // byte swap the header
2106 for (i = 0;i < (int) sizeof(*prog->progs) / 4;i++)
2107 ((int *)prog->progs)[i] = LittleLong ( ((int *)prog->progs)[i] );
2109 if (prog->progs->version != PROG_VERSION)
2110 PRVM_ERROR ("%s: %s has wrong version number (%i should be %i)", PRVM_NAME, filename, prog->progs->version, PROG_VERSION);
2111 if (prog->progs->crc != prog->headercrc && prog->progs->crc != prog->headercrc2)
2112 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);
2114 //prog->functions = (dfunction_t *)((unsigned char *)progs + progs->ofs_functions);
2115 dfunctions = (dfunction_t *)((unsigned char *)prog->progs + prog->progs->ofs_functions);
2117 if (prog->progs->ofs_strings + prog->progs->numstrings >= (int)filesize)
2118 PRVM_ERROR ("%s: %s strings go past end of file", PRVM_NAME, filename);
2119 prog->strings = (char *)prog->progs + prog->progs->ofs_strings;
2120 prog->stringssize = prog->progs->numstrings;
2122 prog->numknownstrings = 0;
2123 prog->maxknownstrings = 0;
2124 prog->knownstrings = NULL;
2125 prog->knownstrings_freeable = NULL;
2127 Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
2129 prog->globaldefs = (ddef_t *)((unsigned char *)prog->progs + prog->progs->ofs_globaldefs);
2131 // we need to expand the fielddefs list to include all the engine fields,
2132 // so allocate a new place for it
2133 infielddefs = (ddef_t *)((unsigned char *)prog->progs + prog->progs->ofs_fielddefs);
2135 prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs->numfielddefs + numrequiredfields) * sizeof(ddef_t));
2137 prog->statements = (dstatement_t *)((unsigned char *)prog->progs + prog->progs->ofs_statements);
2139 prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs->numstatements * sizeof(*prog->statement_profile));
2141 //pr_global_struct = (globalvars_t *)((unsigned char *)progs + progs->ofs_globals);
2142 prog->globals.generic = (float *)((unsigned char *)prog->progs + prog->progs->ofs_globals);
2144 // byte swap the lumps
2145 for (i=0 ; i<prog->progs->numstatements ; i++)
2147 prog->statements[i].op = LittleShort(prog->statements[i].op);
2148 prog->statements[i].a = LittleShort(prog->statements[i].a);
2149 prog->statements[i].b = LittleShort(prog->statements[i].b);
2150 prog->statements[i].c = LittleShort(prog->statements[i].c);
2153 prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs->numfunctions);
2154 for (i = 0;i < prog->progs->numfunctions;i++)
2156 prog->functions[i].first_statement = LittleLong (dfunctions[i].first_statement);
2157 prog->functions[i].parm_start = LittleLong (dfunctions[i].parm_start);
2158 prog->functions[i].s_name = LittleLong (dfunctions[i].s_name);
2159 prog->functions[i].s_file = LittleLong (dfunctions[i].s_file);
2160 prog->functions[i].numparms = LittleLong (dfunctions[i].numparms);
2161 prog->functions[i].locals = LittleLong (dfunctions[i].locals);
2162 memcpy(prog->functions[i].parm_size, dfunctions[i].parm_size, sizeof(dfunctions[i].parm_size));
2163 if(prog->functions[i].first_statement >= prog->progs->numstatements)
2164 PRVM_ERROR("PRVM_LoadProgs: out of bounds function statement (function %d) in %s", i, PRVM_NAME);
2165 // TODO bounds check parm_start, s_name, s_file, numparms, locals, parm_size
2168 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
2170 prog->globaldefs[i].type = LittleShort (prog->globaldefs[i].type);
2171 prog->globaldefs[i].ofs = LittleShort (prog->globaldefs[i].ofs);
2172 prog->globaldefs[i].s_name = LittleLong (prog->globaldefs[i].s_name);
2173 // TODO bounds check ofs, s_name
2176 // copy the progs fields to the new fields list
2177 for (i = 0;i < prog->progs->numfielddefs;i++)
2179 prog->fielddefs[i].type = LittleShort (infielddefs[i].type);
2180 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
2181 PRVM_ERROR ("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", PRVM_NAME);
2182 prog->fielddefs[i].ofs = LittleShort (infielddefs[i].ofs);
2183 prog->fielddefs[i].s_name = LittleLong (infielddefs[i].s_name);
2184 // TODO bounds check ofs, s_name
2187 // append the required fields
2188 for (i = 0;i < (int) numrequiredfields;i++)
2190 prog->fielddefs[prog->progs->numfielddefs].type = required_field[i].type;
2191 prog->fielddefs[prog->progs->numfielddefs].ofs = prog->progs->entityfields;
2192 prog->fielddefs[prog->progs->numfielddefs].s_name = PRVM_SetEngineString(required_field[i].name);
2193 // TODO bounds check ofs, s_name
2194 if (prog->fielddefs[prog->progs->numfielddefs].type == ev_vector)
2195 prog->progs->entityfields += 3;
2197 prog->progs->entityfields++;
2198 prog->progs->numfielddefs++;
2200 prog->entityfields = prog->progs->entityfields;
2202 // check required functions
2203 for(i=0 ; i < numrequiredfunc ; i++)
2204 if(PRVM_ED_FindFunction(required_func[i]) == 0)
2205 PRVM_ERROR("%s: %s not found in %s",PRVM_NAME, required_func[i], filename);
2207 // check required globals
2208 for(i=0 ; i < numrequiredglobals ; i++)
2209 if(PRVM_ED_FindGlobal(required_global[i]) == 0)
2210 PRVM_ERROR("%s: %s not found in %s",PRVM_NAME, required_global[i], filename);
2212 for (i=0 ; i<prog->progs->numglobals ; i++)
2213 ((int *)prog->globals.generic)[i] = LittleLong (((int *)prog->globals.generic)[i]);
2215 // LordHavoc: bounds check anything static
2216 for (i = 0,st = prog->statements;i < prog->progs->numstatements;i++,st++)
2222 if ((unsigned short) st->a >= prog->progs->numglobals || st->b + i < 0 || st->b + i >= prog->progs->numstatements)
2223 PRVM_ERROR("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", i, PRVM_NAME);
2226 if (st->a + i < 0 || st->a + i >= prog->progs->numstatements)
2227 PRVM_ERROR("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, PRVM_NAME);
2229 // global global global
2264 if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->b >= prog->progs->numglobals || (unsigned short) st->c >= prog->progs->numglobals)
2265 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
2267 // global none global
2273 if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->c >= prog->progs->numglobals)
2274 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
2290 if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->b >= prog->progs->numglobals)
2291 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
2305 if ((unsigned short) st->a >= prog->progs->numglobals)
2306 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
2309 Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", st->op, i, PRVM_NAME);
2313 if(prog->progs->numstatements < 1)
2315 PRVM_ERROR("PRVM_LoadProgs: empty program in %s", PRVM_NAME);
2317 else switch(prog->statements[prog->progs->numstatements - 1].op)
2324 PRVM_ERROR("PRVM_LoadProgs: program may fall off the edge (does not end with RETURN, GOTO or DONE) in %s", PRVM_NAME);
2328 PRVM_LoadLNO(filename);
2332 if(*prvm_language.string)
2333 // in CSQC we really shouldn't be able to change how stuff works... sorry for now
2334 // later idea: include a list of authorized .po file checksums with the csprogs
2336 qboolean deftrans = !!strcmp(PRVM_NAME, "client");
2337 if(deftrans) // once we have dotranslate_ strings, ALWAYS use the opt-in method!
2339 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
2342 name = PRVM_GetString(prog->globaldefs[i].s_name);
2343 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2344 if(name && !strncmp(name, "dotranslate_", 12))
2351 if(!strcmp(prvm_language.string, "dump"))
2353 qfile_t *f = FS_OpenRealFile(va("%s.%s.po", filename, prvm_language.string), "w", false);
2354 Con_Printf("Dumping to %s.%s.po\n", filename, prvm_language.string);
2357 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
2360 name = PRVM_GetString(prog->globaldefs[i].s_name);
2361 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2362 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2364 prvm_eval_t *val = (prvm_eval_t *)(prog->globals.generic + prog->globaldefs[i].ofs);
2365 const char *value = PRVM_GetString(val->string);
2368 char buf[MAX_INPUTLINE];
2369 PRVM_PO_UnparseString(buf, value, sizeof(buf));
2370 FS_Printf(f, "msgid \"%s\"\nmsgstr \"\"\n\n", buf);
2379 po_t *po = PRVM_PO_Load(va("%s.%s.po", filename, prvm_language.string), prog->progs_mempool);
2382 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
2385 name = PRVM_GetString(prog->globaldefs[i].s_name);
2386 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2387 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2389 prvm_eval_t *val = (prvm_eval_t *)(prog->globals.generic + prog->globaldefs[i].ofs);
2390 const char *value = PRVM_GetString(val->string);
2393 value = PRVM_PO_Lookup(po, value);
2395 val->string = PRVM_SetEngineString(value);
2403 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
2406 name = PRVM_GetString(prog->globaldefs[i].s_name);
2407 //Con_Printf("found var %s\n", name);
2409 && !strncmp(name, "autocvar_", 9)
2410 && !(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
2413 prvm_eval_t *val = (prvm_eval_t *)(prog->globals.generic + prog->globaldefs[i].ofs);
2414 cvar_t *cvar = Cvar_FindVar(name + 9);
2415 //Con_Printf("PRVM_LoadProgs: autocvar global %s in %s, processing...\n", name, PRVM_NAME);
2420 Con_Printf("PRVM_LoadProgs: no cvar for autocvar global %s in %s, creating...\n", name, PRVM_NAME);
2421 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2424 if((float)((int)(val->_float)) == val->_float)
2425 dpsnprintf(buf, sizeof(buf), "%i", (int)(val->_float));
2427 dpsnprintf(buf, sizeof(buf), "%.9g", val->_float);
2431 dpsnprintf(buf, sizeof(buf), "%.9g %.9g %.9g", val->vector[0], val->vector[1], val->vector[2]); value = buf;
2434 value = PRVM_GetString(val->string);
2437 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, PRVM_NAME);
2440 cvar = Cvar_Get(name + 9, value, 0, NULL);
2441 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2443 val->string = PRVM_SetEngineString(cvar->string);
2444 cvar->globaldefindex_stringno[prog - prog_list] = val->string;
2447 PRVM_ERROR("PRVM_LoadProgs: could not create cvar for autocvar global %s in %s", name, PRVM_NAME);
2448 cvar->globaldefindex_progid[prog - prog_list] = prog->id;
2449 cvar->globaldefindex[prog - prog_list] = i;
2451 else if((cvar->flags & CVAR_PRIVATE) == 0)
2453 // MUST BE SYNCED WITH cvar.c Cvar_Set
2456 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2459 val->_float = cvar->value;
2463 VectorClear(val->vector);
2464 for (j = 0;j < 3;j++)
2466 while (*s && ISWHITESPACE(*s))
2470 val->vector[j] = atof(s);
2471 while (!ISWHITESPACE(*s))
2478 val->string = PRVM_SetEngineString(cvar->string);
2479 cvar->globaldefindex_stringno[prog - prog_list] = val->string;
2482 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, PRVM_NAME);
2485 cvar->globaldefindex_progid[prog - prog_list] = prog->id;
2486 cvar->globaldefindex[prog - prog_list] = i;
2489 Con_Printf("PRVM_LoadProgs: private cvar for autocvar global %s in %s\n", name, PRVM_NAME);
2495 prog->loaded = TRUE;
2497 // set flags & ddef_ts in prog
2503 PRVM_GCALL(init_cmd)();
2510 void PRVM_Fields_f (void)
2512 int i, j, ednum, used, usedamount;
2514 char tempstring[MAX_INPUTLINE], tempstring2[260];
2524 Con_Print("no progs loaded\n");
2531 Con_Print("prvm_fields <program name>\n");
2536 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
2539 counts = (int *)Mem_Alloc(tempmempool, prog->progs->numfielddefs * sizeof(int));
2540 for (ednum = 0;ednum < prog->max_edicts;ednum++)
2542 ed = PRVM_EDICT_NUM(ednum);
2543 if (ed->priv.required->free)
2545 for (i = 1;i < prog->progs->numfielddefs;i++)
2547 d = &prog->fielddefs[i];
2548 name = PRVM_GetString(d->s_name);
2549 if (name[strlen(name)-2] == '_')
2550 continue; // skip _x, _y, _z vars
2551 v = (int *)(ed->fields.vp + d->ofs);
2552 // if the value is still all 0, skip the field
2553 for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
2566 for (i = 0;i < prog->progs->numfielddefs;i++)
2568 d = &prog->fielddefs[i];
2569 name = PRVM_GetString(d->s_name);
2570 if (name[strlen(name)-2] == '_')
2571 continue; // skip _x, _y, _z vars
2572 switch(d->type & ~DEF_SAVEGLOBAL)
2575 strlcat(tempstring, "string ", sizeof(tempstring));
2578 strlcat(tempstring, "entity ", sizeof(tempstring));
2581 strlcat(tempstring, "function ", sizeof(tempstring));
2584 strlcat(tempstring, "field ", sizeof(tempstring));
2587 strlcat(tempstring, "void ", sizeof(tempstring));
2590 strlcat(tempstring, "float ", sizeof(tempstring));
2593 strlcat(tempstring, "vector ", sizeof(tempstring));
2596 strlcat(tempstring, "pointer ", sizeof(tempstring));
2599 dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
2600 strlcat(tempstring, tempstring2, sizeof(tempstring));
2603 if (strlen(name) > sizeof(tempstring2)-4)
2605 memcpy (tempstring2, name, sizeof(tempstring2)-4);
2606 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
2607 tempstring2[sizeof(tempstring2)-1] = 0;
2610 strlcat(tempstring, name, sizeof(tempstring));
2611 for (j = (int)strlen(name);j < 25;j++)
2612 strlcat(tempstring, " ", sizeof(tempstring));
2613 dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
2614 strlcat(tempstring, tempstring2, sizeof(tempstring));
2615 strlcat(tempstring, "\n", sizeof(tempstring));
2616 if (strlen(tempstring) >= sizeof(tempstring)/2)
2618 Con_Print(tempstring);
2624 usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
2628 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);
2633 void PRVM_Globals_f (void)
2636 const char *wildcard;
2642 Con_Print("no progs loaded\n");
2645 if(Cmd_Argc () < 2 || Cmd_Argc() > 3)
2647 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
2652 if(!PRVM_SetProgFromString (Cmd_Argv (1)))
2655 if( Cmd_Argc() == 3)
2656 wildcard = Cmd_Argv(2);
2660 Con_Printf("%s :", PRVM_NAME);
2662 for (i = 0;i < prog->progs->numglobaldefs;i++)
2665 if( !matchpattern( PRVM_GetString(prog->globaldefs[i].s_name), wildcard, 1) )
2670 Con_Printf("%s\n", PRVM_GetString(prog->globaldefs[i].s_name));
2672 Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->progs->numglobals, numculled, prog->progs->numglobals * 4);
2682 void PRVM_Global_f(void)
2685 if( Cmd_Argc() != 3 ) {
2686 Con_Printf( "prvm_global <program name> <global name>\n" );
2691 if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
2694 global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
2696 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2698 Con_Printf( "%s: %s\n", Cmd_Argv(2), PRVM_ValueString( (etype_t)global->type, (prvm_eval_t *) &prog->globals.generic[ global->ofs ] ) );
2707 void PRVM_GlobalSet_f(void)
2710 if( Cmd_Argc() != 4 ) {
2711 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2716 if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
2719 global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
2721 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2723 PRVM_ED_ParseEpair( NULL, global, Cmd_Argv(3), true );
2732 void PRVM_Init (void)
2734 Cmd_AddCommand ("prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2735 Cmd_AddCommand ("prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2736 Cmd_AddCommand ("prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2737 Cmd_AddCommand ("prvm_profile", PRVM_Profile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu)");
2738 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");
2739 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)");
2740 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)");
2741 Cmd_AddCommand ("prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2742 Cmd_AddCommand ("prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2743 Cmd_AddCommand ("prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2744 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)");
2745 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");
2746 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");
2747 Cmd_AddCommand ("prvm_printfunction", PRVM_PrintFunction_f, "prints a disassembly (QuakeC instructions) of the specified function in the selected VM (server, client, menu)");
2748 Cmd_AddCommand ("cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2749 Cmd_AddCommand ("menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2750 Cmd_AddCommand ("sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2752 Cvar_RegisterVariable (&prvm_language);
2753 Cvar_RegisterVariable (&prvm_traceqc);
2754 Cvar_RegisterVariable (&prvm_statementprofiling);
2755 Cvar_RegisterVariable (&prvm_timeprofiling);
2756 Cvar_RegisterVariable (&prvm_backtraceforwarnings);
2757 Cvar_RegisterVariable (&prvm_leaktest);
2758 Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames);
2759 Cvar_RegisterVariable (&prvm_errordump);
2760 Cvar_RegisterVariable (&prvm_reuseedicts_startuptime);
2761 Cvar_RegisterVariable (&prvm_reuseedicts_neverinsameframe);
2763 // COMMANDLINEOPTION: PRVM: -norunaway disables the runaway loop check (it might be impossible to exit DarkPlaces if used!)
2764 prvm_runawaycheck = !COM_CheckParm("-norunaway");
2774 void PRVM_InitProg(int prognr)
2776 static unsigned int progid = 0;
2778 if(prognr < 0 || prognr >= PRVM_MAXPROGS)
2779 Sys_Error("PRVM_InitProg: Invalid program number %i",prognr);
2781 prog = &prog_list[prognr];
2786 memset(prog, 0, sizeof(prvm_prog_t));
2787 prog->starttime = Sys_DoubleTime();
2788 prog->id = ++progid;
2790 prog->error_cmd = Host_Error;
2791 prog->leaktest_active = prvm_leaktest.integer != 0;
2794 int PRVM_GetProgNr(void)
2796 return prog - prog_list;
2799 void *_PRVM_Alloc(size_t buffersize, const char *filename, int fileline)
2801 return _Mem_Alloc(prog->progs_mempool, NULL, buffersize, 16, filename, fileline);
2804 void _PRVM_Free(void *buffer, const char *filename, int fileline)
2806 _Mem_Free(buffer, filename, fileline);
2809 void _PRVM_FreeAll(const char *filename, int fileline)
2812 prog->fielddefs = NULL;
2813 prog->functions = NULL;
2814 _Mem_EmptyPool(prog->progs_mempool, filename, fileline);
2817 // LordHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
2818 unsigned int PRVM_EDICT_NUM_ERROR(unsigned int n, const char *filename, int fileline)
2820 PRVM_ERROR ("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", PRVM_NAME, n, filename, fileline);
2824 sizebuf_t vm_tempstringsbuf;
2825 #define PRVM_KNOWNSTRINGBASE 0x40000000
2827 const char *PRVM_GetString(int num)
2832 VM_Warning("PRVM_GetString: Invalid string offset (%i < 0)\n", num);
2835 else if (num < prog->stringssize)
2837 // constant string from progs.dat
2838 return prog->strings + num;
2840 else if (num <= prog->stringssize + vm_tempstringsbuf.maxsize)
2842 // tempstring returned by engine to QC (becomes invalid after returning to engine)
2843 num -= prog->stringssize;
2844 if (num < vm_tempstringsbuf.cursize)
2845 return (char *)vm_tempstringsbuf.data + num;
2848 VM_Warning("PRVM_GetString: Invalid temp-string offset (%i >= %i vm_tempstringsbuf.cursize)\n", num, vm_tempstringsbuf.cursize);
2852 else if (num & PRVM_KNOWNSTRINGBASE)
2855 num = num - PRVM_KNOWNSTRINGBASE;
2856 if (num >= 0 && num < prog->numknownstrings)
2858 if (!prog->knownstrings[num])
2860 VM_Warning("PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
2863 return prog->knownstrings[num];
2867 VM_Warning("PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
2873 // invalid string offset
2874 VM_Warning("PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
2879 const char *PRVM_ChangeEngineString(int i, const char *s)
2882 i = i - PRVM_KNOWNSTRINGBASE;
2883 if(i < 0 || i >= prog->numknownstrings)
2884 PRVM_ERROR("PRVM_ChangeEngineString: s is not an engine string");
2885 old = prog->knownstrings[i];
2886 prog->knownstrings[i] = s;
2890 int PRVM_SetEngineString(const char *s)
2895 if (s >= prog->strings && s <= prog->strings + prog->stringssize)
2896 PRVM_ERROR("PRVM_SetEngineString: s in prog->strings area");
2897 // if it's in the tempstrings area, use a reserved range
2898 // (otherwise we'd get millions of useless string offsets cluttering the database)
2899 if (s >= (char *)vm_tempstringsbuf.data && s < (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.maxsize)
2901 return prog->stringssize + (s - (char *)vm_tempstringsbuf.data);
2903 // see if it's a known string address
2904 for (i = 0;i < prog->numknownstrings;i++)
2905 if (prog->knownstrings[i] == s)
2906 return PRVM_KNOWNSTRINGBASE + i;
2907 // new unknown engine string
2908 if (developer_insane.integer)
2909 Con_DPrintf("new engine string %p = \"%s\"\n", s, s);
2910 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2911 if (!prog->knownstrings[i])
2913 if (i >= prog->numknownstrings)
2915 if (i >= prog->maxknownstrings)
2917 const char **oldstrings = prog->knownstrings;
2918 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2919 const char **oldstrings_origin = prog->knownstrings_origin;
2920 prog->maxknownstrings += 128;
2921 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2922 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2923 if(prog->leaktest_active)
2924 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2925 if (prog->numknownstrings)
2927 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2928 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2929 if(prog->leaktest_active)
2930 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
2933 prog->numknownstrings++;
2935 prog->firstfreeknownstring = i + 1;
2936 prog->knownstrings[i] = s;
2937 prog->knownstrings_freeable[i] = false;
2938 if(prog->leaktest_active)
2939 prog->knownstrings_origin[i] = NULL;
2940 return PRVM_KNOWNSTRINGBASE + i;
2943 // temp string handling
2945 // all tempstrings go into this buffer consecutively, and it is reset
2946 // whenever PRVM_ExecuteProgram returns to the engine
2947 // (technically each PRVM_ExecuteProgram call saves the cursize value and
2948 // restores it on return, so multiple recursive calls can share the same
2950 // the buffer size is automatically grown as needed
2952 int PRVM_SetTempString(const char *s)
2958 size = (int)strlen(s) + 1;
2959 if (developer_insane.integer)
2960 Con_DPrintf("PRVM_SetTempString: cursize %i, size %i\n", vm_tempstringsbuf.cursize, size);
2961 if (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
2963 sizebuf_t old = vm_tempstringsbuf;
2964 if (vm_tempstringsbuf.cursize + size >= 1<<28)
2965 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);
2966 vm_tempstringsbuf.maxsize = max(vm_tempstringsbuf.maxsize, 65536);
2967 while (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
2968 vm_tempstringsbuf.maxsize *= 2;
2969 if (vm_tempstringsbuf.maxsize != old.maxsize || vm_tempstringsbuf.data == NULL)
2971 Con_DPrintf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, vm_tempstringsbuf.maxsize/1024);
2972 vm_tempstringsbuf.data = (unsigned char *) Mem_Alloc(sv_mempool, vm_tempstringsbuf.maxsize);
2974 memcpy(vm_tempstringsbuf.data, old.data, old.cursize);
2979 t = (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.cursize;
2981 vm_tempstringsbuf.cursize += size;
2982 return PRVM_SetEngineString(t);
2985 int PRVM_AllocString(size_t bufferlength, char **pointer)
2990 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2991 if (!prog->knownstrings[i])
2993 if (i >= prog->numknownstrings)
2995 if (i >= prog->maxknownstrings)
2997 const char **oldstrings = prog->knownstrings;
2998 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2999 const char **oldstrings_origin = prog->knownstrings_origin;
3000 prog->maxknownstrings += 128;
3001 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3002 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
3003 if(prog->leaktest_active)
3004 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3005 if (prog->numknownstrings)
3007 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
3008 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
3009 if(prog->leaktest_active)
3010 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
3013 Mem_Free((char **)oldstrings);
3014 if (oldstrings_freeable)
3015 Mem_Free((unsigned char *)oldstrings_freeable);
3016 if (oldstrings_origin)
3017 Mem_Free((char **)oldstrings_origin);
3019 prog->numknownstrings++;
3021 prog->firstfreeknownstring = i + 1;
3022 prog->knownstrings[i] = (char *)PRVM_Alloc(bufferlength);
3023 prog->knownstrings_freeable[i] = true;
3024 if(prog->leaktest_active)
3025 prog->knownstrings_origin[i] = PRVM_AllocationOrigin();
3027 *pointer = (char *)(prog->knownstrings[i]);
3028 return PRVM_KNOWNSTRINGBASE + i;
3031 void PRVM_FreeString(int num)
3034 PRVM_ERROR("PRVM_FreeString: attempt to free a NULL string");
3035 else if (num >= 0 && num < prog->stringssize)
3036 PRVM_ERROR("PRVM_FreeString: attempt to free a constant string");
3037 else if (num >= PRVM_KNOWNSTRINGBASE && num < PRVM_KNOWNSTRINGBASE + prog->numknownstrings)
3039 num = num - PRVM_KNOWNSTRINGBASE;
3040 if (!prog->knownstrings[num])
3041 PRVM_ERROR("PRVM_FreeString: attempt to free a non-existent or already freed string");
3042 if (!prog->knownstrings_freeable[num])
3043 PRVM_ERROR("PRVM_FreeString: attempt to free a string owned by the engine");
3044 PRVM_Free((char *)prog->knownstrings[num]);
3045 if(prog->leaktest_active)
3046 if(prog->knownstrings_origin[num])
3047 PRVM_Free((char *)prog->knownstrings_origin[num]);
3048 prog->knownstrings[num] = NULL;
3049 prog->knownstrings_freeable[num] = false;
3050 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
3053 PRVM_ERROR("PRVM_FreeString: invalid string offset %i", num);
3056 static qboolean PRVM_IsStringReferenced(string_t string)
3060 for (i = 0;i < prog->progs->numglobaldefs;i++)
3062 ddef_t *d = &prog->globaldefs[i];
3063 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3065 if(string == ((prvm_eval_t *) &prog->globals.generic[d->ofs])->string)
3069 for(j = 0; j < prog->num_edicts; ++j)
3071 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3072 if (ed->priv.required->free)
3074 for (i=0; i<prog->progs->numfielddefs; ++i)
3076 ddef_t *d = &prog->fielddefs[i];
3077 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3079 if(string == ((prvm_eval_t *) &ed->fields.vp[d->ofs])->string)
3087 static qboolean PRVM_IsEdictRelevant(prvm_edict_t *edict)
3089 if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
3090 return true; // world or clients
3091 switch(prog - prog_list)
3093 case PRVM_SERVERPROG:
3095 entvars_t *ev = edict->fields.server;
3096 if(ev->solid) // can block other stuff, or is a trigger?
3098 if(ev->modelindex) // visible ent?
3100 if(ev->effects) // particle effect?
3102 if(ev->think) // has a think function?
3103 if(ev->nextthink > 0) // that actually will eventually run?
3107 if(*prvm_leaktest_ignore_classnames.string)
3109 if(strstr(va(" %s ", prvm_leaktest_ignore_classnames.string), va(" %s ", PRVM_GetString(ev->classname))))
3114 case PRVM_CLIENTPROG:
3116 // TODO someone add more stuff here
3117 cl_entvars_t *ev = edict->fields.client;
3118 if(ev->entnum) // csqc networked
3120 if(ev->modelindex) // visible ent?
3122 if(ev->effects) // particle effect?
3124 if(ev->think) // has a think function?
3125 if(ev->nextthink > 0) // that actually will eventually run?
3127 if(*prvm_leaktest_ignore_classnames.string)
3129 if(strstr(va(" %s ", prvm_leaktest_ignore_classnames.string), va(" %s ", PRVM_GetString(ev->classname))))
3135 // menu prog does not have classnames
3141 static qboolean PRVM_IsEdictReferenced(prvm_edict_t *edict, int mark)
3144 int edictnum = PRVM_NUM_FOR_EDICT(edict);
3145 const char *targetname = NULL;
3147 switch(prog - prog_list)
3149 case PRVM_SERVERPROG:
3150 targetname = PRVM_GetString(edict->fields.server->targetname);
3155 if(!*targetname) // ""
3158 for (i = 0;i < prog->progs->numglobaldefs;i++)
3160 ddef_t *d = &prog->globaldefs[i];
3161 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3163 if(edictnum == ((prvm_eval_t *) &prog->globals.generic[d->ofs])->edict)
3167 for(j = 0; j < prog->num_edicts; ++j)
3169 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3170 if (ed->priv.required->mark < mark)
3176 const char *target = PRVM_GetString(ed->fields.server->target);
3178 if(!strcmp(target, targetname))
3181 for (i=0; i<prog->progs->numfielddefs; ++i)
3183 ddef_t *d = &prog->fielddefs[i];
3184 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3186 if(edictnum == ((prvm_eval_t *) &ed->fields.vp[d->ofs])->edict)
3194 static void PRVM_MarkReferencedEdicts(void)
3200 for(j = 0; j < prog->num_edicts; ++j)
3202 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3203 if(ed->priv.required->free)
3205 ed->priv.required->mark = PRVM_IsEdictRelevant(ed) ? 1 : 0;
3212 for(j = 0; j < prog->num_edicts; ++j)
3214 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3215 if(ed->priv.required->free)
3217 if(ed->priv.required->mark)
3219 if(PRVM_IsEdictReferenced(ed, stage))
3221 ed->priv.required->mark = stage + 1;
3228 Con_DPrintf("leak check used %d stages to find all references\n", stage);
3231 void PRVM_LeakTest(void)
3234 qboolean leaked = false;
3236 if(!prog->leaktest_active)
3240 for (i = 0; i < prog->numknownstrings; ++i)
3242 if(prog->knownstrings[i])
3243 if(prog->knownstrings_freeable[i])
3244 if(prog->knownstrings_origin[i])
3245 if(!PRVM_IsStringReferenced(PRVM_KNOWNSTRINGBASE + i))
3247 Con_Printf("Unreferenced string found!\n Value: %s\n Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
3253 PRVM_MarkReferencedEdicts();
3254 for(j = 0; j < prog->num_edicts; ++j)
3256 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3257 if(ed->priv.required->free)
3259 if(!ed->priv.required->mark)
3260 if(ed->priv.required->allocation_origin)
3262 Con_Printf("Unreferenced edict found!\n Allocated at: %s\n", ed->priv.required->allocation_origin);
3263 PRVM_ED_Print(ed, NULL);
3269 for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
3271 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
3273 if(stringbuffer->origin)
3275 Con_Printf("Open string buffer handle found!\n Allocated at: %s\n", stringbuffer->origin);
3280 for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
3282 if(prog->openfiles[i])
3283 if(prog->openfiles_origin[i])
3285 Con_Printf("Open file handle found!\n Allocated at: %s\n", prog->openfiles_origin[i]);
3290 for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
3292 if(prog->opensearches[i])
3293 if(prog->opensearches_origin[i])
3295 Con_Printf("Open search handle found!\n Allocated at: %s\n", prog->opensearches_origin[i]);
3301 Con_Printf("Congratulations. No leaks found.\n");