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.
28 static prvm_prog_t prog_list[PRVM_MAXPROGS];
30 int prvm_type_size[8] = {1,sizeof(string_t)/4,1,3,1,1,sizeof(func_t)/4,sizeof(void *)/4};
32 ddef_t *PRVM_ED_FieldAtOfs(int ofs);
33 qboolean PRVM_ED_ParseEpair(prvm_edict_t *ent, ddef_t *key, const char *s, qboolean parsebackslash);
35 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"};
36 // LordHavoc: prints every opcode as it executes - warning: this is significant spew
37 cvar_t prvm_traceqc = {0, "prvm_traceqc", "0", "prints every QuakeC statement as it is executed (only for really thorough debugging!)"};
38 // LordHavoc: counts usage of each QuakeC statement
39 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)"};
40 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)"};
41 cvar_t prvm_backtraceforwarnings = {0, "prvm_backtraceforwarnings", "0", "print a backtrace for warnings too"};
42 cvar_t prvm_leaktest = {0, "prvm_leaktest", "0", "try to detect memory leaks in strings or entities"};
43 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)"};
44 cvar_t prvm_errordump = {0, "prvm_errordump", "0", "write a savegame on crash to crash-server.dmp"};
45 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)"};
46 cvar_t prvm_reuseedicts_neverinsameframe = {0, "prvm_reuseedicts_neverinsameframe", "1", "never allows re-use of freed entity slots during same frame"};
48 static double prvm_reuseedicts_always_allow = 0;
49 qboolean prvm_runawaycheck = true;
51 extern sizebuf_t vm_tempstringsbuf;
53 //============================================================================
61 void PRVM_MEM_Alloc(void)
65 // reserve space for the null entity aka world
66 // check bound of max_edicts
67 prog->max_edicts = bound(1 + prog->reserved_edicts, prog->max_edicts, prog->limit_edicts);
68 prog->num_edicts = bound(1 + prog->reserved_edicts, prog->num_edicts, prog->max_edicts);
70 // edictprivate_size has to be min as big prvm_edict_private_t
71 prog->edictprivate_size = max(prog->edictprivate_size,(int)sizeof(prvm_edict_private_t));
74 prog->edicts = (prvm_edict_t *)Mem_Alloc(prog->progs_mempool,prog->limit_edicts * sizeof(prvm_edict_t));
76 // alloc edict private space
77 prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size);
80 prog->entityfieldsarea = prog->entityfields * prog->max_edicts;
81 prog->edictsfields = (vec_t *)Mem_Alloc(prog->progs_mempool, prog->entityfieldsarea * sizeof(vec_t));
84 for(i = 0; i < prog->max_edicts; i++)
86 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
87 prog->edicts[i].fields.vp = prog->edictsfields + i * prog->entityfields;
93 PRVM_MEM_IncreaseEdicts
96 void PRVM_MEM_IncreaseEdicts(void)
100 if(prog->max_edicts >= prog->limit_edicts)
103 PRVM_GCALL(begin_increase_edicts)();
106 prog->max_edicts = min(prog->max_edicts + 256, prog->limit_edicts);
108 prog->entityfieldsarea = prog->entityfields * prog->max_edicts;
109 prog->edictsfields = (vec_t*)Mem_Realloc(prog->progs_mempool, (void *)prog->edictsfields, prog->entityfieldsarea * sizeof(vec_t));
110 prog->edictprivate = (void *)Mem_Realloc(prog->progs_mempool, (void *)prog->edictprivate, prog->max_edicts * prog->edictprivate_size);
112 //set e and v pointers
113 for(i = 0; i < prog->max_edicts; i++)
115 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
116 prog->edicts[i].fields.vp = prog->edictsfields + i * prog->entityfields;
119 PRVM_GCALL(end_increase_edicts)();
122 //============================================================================
125 int PRVM_ED_FindFieldOffset(const char *field)
128 d = PRVM_ED_FindField(field);
134 int PRVM_ED_FindGlobalOffset(const char *global)
137 d = PRVM_ED_FindGlobal(global);
143 func_t PRVM_ED_FindFunctionOffset(const char *function)
146 f = PRVM_ED_FindFunction(function);
149 return (func_t)(f - prog->functions);
152 qboolean PRVM_ProgLoaded(int prognr)
154 if(prognr < 0 || prognr >= PRVM_MAXPROGS)
157 return (prog_list[prognr].loaded ? TRUE : FALSE);
162 PRVM_SetProgFromString
165 // perhaps add a return value when the str doesnt exist
166 qboolean PRVM_SetProgFromString(const char *str)
169 for(; i < PRVM_MAXPROGS ; i++)
170 if(prog_list[i].name && !strcmp(prog_list[i].name,str))
172 if(prog_list[i].loaded)
174 prog = &prog_list[i];
179 Con_Printf("%s not loaded !\n",PRVM_NAME);
184 Con_Printf("Invalid program name %s !\n", str);
193 void PRVM_SetProg(int prognr)
195 if(0 <= prognr && prognr < PRVM_MAXPROGS)
197 if(prog_list[prognr].loaded)
198 prog = &prog_list[prognr];
200 PRVM_ERROR("%i not loaded !", prognr);
203 PRVM_ERROR("Invalid program number %i", prognr);
210 Sets everything to NULL
213 void PRVM_ED_ClearEdict (prvm_edict_t *e)
215 memset (e->fields.vp, 0, prog->entityfields * 4);
216 e->priv.required->free = false;
218 // AK: Let the init_edict function determine if something needs to be initialized
219 PRVM_GCALL(init_edict)(e);
222 const char *PRVM_AllocationOrigin(void)
225 if(prog->leaktest_active)
226 if(prog->depth > 0) // actually in QC code and not just parsing the entities block of a map/savegame
228 buf = (char *)PRVM_Alloc(128);
229 PRVM_ShortStackTrace(buf, 128);
238 Returns if this particular edict could get allocated by PRVM_ED_Alloc
241 qboolean PRVM_ED_CanAlloc(prvm_edict_t *e)
243 if(!e->priv.required->free)
245 if(prvm_reuseedicts_always_allow == realtime)
247 if(realtime <= e->priv.required->freetime && prvm_reuseedicts_neverinsameframe.integer)
248 return false; // never allow reuse in same frame (causes networking trouble)
249 if(e->priv.required->freetime < prog->starttime + prvm_reuseedicts_startuptime.value)
251 if(realtime > e->priv.required->freetime + 1)
253 return false; // entity slot still blocked because the entity was freed less than one second ago
260 Either finds a free edict, or allocates a new one.
261 Try to avoid reusing an entity that was recently freed, because it
262 can cause the client to think the entity morphed into something else
263 instead of being removed and recreated, which can cause interpolated
264 angles and bad trails.
267 prvm_edict_t *PRVM_ED_Alloc (void)
272 // the client qc dont need maxclients
273 // thus it doesnt need to use svs.maxclients
274 // AK: changed i=svs.maxclients+1
275 // AK: changed so the edict 0 wont spawn -> used as reserved/world entity
276 // although the menu/client has no world
277 for (i = prog->reserved_edicts + 1;i < prog->num_edicts;i++)
279 e = PRVM_EDICT_NUM(i);
280 if(PRVM_ED_CanAlloc(e))
282 PRVM_ED_ClearEdict (e);
283 e->priv.required->allocation_origin = PRVM_AllocationOrigin();
288 if (i == prog->limit_edicts)
289 PRVM_ERROR ("%s: PRVM_ED_Alloc: no free edicts",PRVM_NAME);
292 if (prog->num_edicts >= prog->max_edicts)
293 PRVM_MEM_IncreaseEdicts();
295 e = PRVM_EDICT_NUM(i);
296 PRVM_ED_ClearEdict (e);
298 e->priv.required->allocation_origin = PRVM_AllocationOrigin();
307 Marks the edict as free
308 FIXME: walk all entities and NULL out references to this entity
311 void PRVM_ED_Free (prvm_edict_t *ed)
313 // dont delete the null entity (world) or reserved edicts
314 if(PRVM_NUM_FOR_EDICT(ed) <= prog->reserved_edicts )
317 PRVM_GCALL(free_edict)(ed);
319 ed->priv.required->free = true;
320 ed->priv.required->freetime = realtime;
321 if(ed->priv.required->allocation_origin)
323 PRVM_Free((char *)ed->priv.required->allocation_origin);
324 ed->priv.required->allocation_origin = NULL;
328 //===========================================================================
335 ddef_t *PRVM_ED_GlobalAtOfs (int ofs)
340 for (i = 0;i < prog->numglobaldefs;i++)
342 def = &prog->globaldefs[i];
354 ddef_t *PRVM_ED_FieldAtOfs (int ofs)
359 for (i = 0;i < prog->numfielddefs;i++)
361 def = &prog->fielddefs[i];
373 ddef_t *PRVM_ED_FindField (const char *name)
378 for (i = 0;i < prog->numfielddefs;i++)
380 def = &prog->fielddefs[i];
381 if (!strcmp(PRVM_GetString(def->s_name), name))
392 ddef_t *PRVM_ED_FindGlobal (const char *name)
397 for (i = 0;i < prog->numglobaldefs;i++)
399 def = &prog->globaldefs[i];
400 if (!strcmp(PRVM_GetString(def->s_name), name))
412 mfunction_t *PRVM_ED_FindFunction (const char *name)
417 for (i = 0;i < prog->numfunctions;i++)
419 func = &prog->functions[i];
420 if (!strcmp(PRVM_GetString(func->s_name), name))
431 Returns a string describing *data in a type specific manner
434 char *PRVM_ValueString (etype_t type, prvm_eval_t *val)
436 static char line[MAX_INPUTLINE];
441 type = (etype_t)((int) type & ~DEF_SAVEGLOBAL);
446 strlcpy (line, PRVM_GetString (val->string), sizeof (line));
450 if (n < 0 || n >= prog->max_edicts)
451 dpsnprintf (line, sizeof(line), "entity %i (invalid!)", n);
453 dpsnprintf (line, sizeof(line), "entity %i", n);
456 f = prog->functions + val->function;
457 dpsnprintf (line, sizeof(line), "%s()", PRVM_GetString(f->s_name));
460 def = PRVM_ED_FieldAtOfs ( val->_int );
461 dpsnprintf (line, sizeof(line), ".%s", PRVM_GetString(def->s_name));
464 dpsnprintf (line, sizeof(line), "void");
467 // LordHavoc: changed from %5.1f to %10.4f
468 dpsnprintf (line, sizeof(line), "%10.4f", val->_float);
471 // LordHavoc: changed from %5.1f to %10.4f
472 dpsnprintf (line, sizeof(line), "'%10.4f %10.4f %10.4f'", val->vector[0], val->vector[1], val->vector[2]);
475 dpsnprintf (line, sizeof(line), "pointer");
478 dpsnprintf (line, sizeof(line), "bad type %i", (int) type);
489 Returns a string describing *data in a type specific manner
490 Easier to parse than PR_ValueString
493 char *PRVM_UglyValueString (etype_t type, prvm_eval_t *val)
495 static char line[MAX_INPUTLINE];
501 type = (etype_t)((int)type & ~DEF_SAVEGLOBAL);
506 // Parse the string a bit to turn special characters
507 // (like newline, specifically) into escape codes,
508 // this fixes saving games from various mods
509 s = PRVM_GetString (val->string);
510 for (i = 0;i < (int)sizeof(line) - 2 && *s;)
539 dpsnprintf (line, sizeof (line), "%i", PRVM_NUM_FOR_EDICT(PRVM_PROG_TO_EDICT(val->edict)));
542 f = prog->functions + val->function;
543 strlcpy (line, PRVM_GetString (f->s_name), sizeof (line));
546 def = PRVM_ED_FieldAtOfs ( val->_int );
547 dpsnprintf (line, sizeof (line), ".%s", PRVM_GetString(def->s_name));
550 dpsnprintf (line, sizeof (line), "void");
553 dpsnprintf (line, sizeof (line), "%.9g", val->_float);
556 dpsnprintf (line, sizeof (line), "%.9g %.9g %.9g", val->vector[0], val->vector[1], val->vector[2]);
559 dpsnprintf (line, sizeof (line), "bad type %i", type);
570 Returns a string with a description and the contents of a global,
571 padded to 20 field width
574 char *PRVM_GlobalString (int ofs)
580 static char line[128];
582 val = (void *)&prog->globals.generic[ofs];
583 def = PRVM_ED_GlobalAtOfs(ofs);
585 dpsnprintf (line, sizeof(line), "GLOBAL%i", ofs);
588 s = PRVM_ValueString ((etype_t)def->type, (prvm_eval_t *)val);
589 dpsnprintf (line, sizeof(line), "%s (=%s)", PRVM_GetString(def->s_name), s);
593 //for ( ; i<20 ; i++)
594 // strcat (line," ");
600 char *PRVM_GlobalStringNoContents (int ofs)
604 static char line[128];
606 def = PRVM_ED_GlobalAtOfs(ofs);
608 dpsnprintf (line, sizeof(line), "GLOBAL%i", ofs);
610 dpsnprintf (line, sizeof(line), "%s", PRVM_GetString(def->s_name));
613 //for ( ; i<20 ; i++)
614 // strcat (line," ");
628 // LordHavoc: optimized this to print out much more quickly (tempstring)
629 // LordHavoc: changed to print out every 4096 characters (incase there are a lot of fields to print)
630 void PRVM_ED_Print(prvm_edict_t *ed, const char *wildcard_fieldname)
638 char tempstring[MAX_INPUTLINE], tempstring2[260]; // temporary string buffers
640 if (ed->priv.required->free)
642 Con_Printf("%s: FREE\n",PRVM_NAME);
647 dpsnprintf(tempstring, sizeof(tempstring), "\n%s EDICT %i:\n", PRVM_NAME, PRVM_NUM_FOR_EDICT(ed));
648 for (i = 1;i < prog->numfielddefs;i++)
650 d = &prog->fielddefs[i];
651 name = PRVM_GetString(d->s_name);
652 if(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
653 continue; // skip _x, _y, _z vars
655 // Check Field Name Wildcard
656 if(wildcard_fieldname)
657 if( !matchpattern(name, wildcard_fieldname, 1) )
658 // Didn't match; skip
661 v = (int *)(ed->fields.vp + d->ofs);
663 // if the value is still all 0, skip the field
664 type = d->type & ~DEF_SAVEGLOBAL;
666 for (j=0 ; j<prvm_type_size[type] ; j++)
669 if (j == prvm_type_size[type])
672 if (strlen(name) > sizeof(tempstring2)-4)
674 memcpy (tempstring2, name, sizeof(tempstring2)-4);
675 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
676 tempstring2[sizeof(tempstring2)-1] = 0;
679 strlcat(tempstring, name, sizeof(tempstring));
680 for (l = strlen(name);l < 14;l++)
681 strlcat(tempstring, " ", sizeof(tempstring));
682 strlcat(tempstring, " ", sizeof(tempstring));
684 name = PRVM_ValueString((etype_t)d->type, (prvm_eval_t *)v);
685 if (strlen(name) > sizeof(tempstring2)-4)
687 memcpy (tempstring2, name, sizeof(tempstring2)-4);
688 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
689 tempstring2[sizeof(tempstring2)-1] = 0;
692 strlcat(tempstring, name, sizeof(tempstring));
693 strlcat(tempstring, "\n", sizeof(tempstring));
694 if (strlen(tempstring) >= sizeof(tempstring)/2)
696 Con_Print(tempstring);
701 Con_Print(tempstring);
711 extern cvar_t developer_entityparsing;
712 void PRVM_ED_Write (qfile_t *f, prvm_edict_t *ed)
722 if (ed->priv.required->free)
728 for (i = 1;i < prog->numfielddefs;i++)
730 d = &prog->fielddefs[i];
731 name = PRVM_GetString(d->s_name);
733 if(developer_entityparsing.integer)
734 Con_Printf("PRVM_ED_Write: at entity %d field %s\n", PRVM_NUM_FOR_EDICT(ed), name);
736 //if(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
737 if(strlen(name) > 1 && name[strlen(name)-2] == '_')
738 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?)
740 v = (int *)(ed->fields.vp + d->ofs);
742 // if the value is still all 0, skip the field
743 type = d->type & ~DEF_SAVEGLOBAL;
744 for (j=0 ; j<prvm_type_size[type] ; j++)
747 if (j == prvm_type_size[type])
750 FS_Printf(f,"\"%s\" ",name);
751 prog->statestring = va("PRVM_ED_Write, ent=%d, name=%s", i, name);
752 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString((etype_t)d->type, (prvm_eval_t *)v));
753 prog->statestring = NULL;
759 void PRVM_ED_PrintNum (int ent, const char *wildcard_fieldname)
761 PRVM_ED_Print(PRVM_EDICT_NUM(ent), wildcard_fieldname);
766 PRVM_ED_PrintEdicts_f
768 For debugging, prints all the entities in the current server
771 void PRVM_ED_PrintEdicts_f (void)
774 const char *wildcard_fieldname;
776 if(Cmd_Argc() < 2 || Cmd_Argc() > 3)
778 Con_Print("prvm_edicts <program name> <optional field name wildcard>\n");
783 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
787 wildcard_fieldname = Cmd_Argv(2);
789 wildcard_fieldname = NULL;
791 Con_Printf("%s: %i entities\n", PRVM_NAME, prog->num_edicts);
792 for (i=0 ; i<prog->num_edicts ; i++)
793 PRVM_ED_PrintNum (i, wildcard_fieldname);
802 For debugging, prints a single edict
805 void PRVM_ED_PrintEdict_f (void)
808 const char *wildcard_fieldname;
810 if(Cmd_Argc() < 3 || Cmd_Argc() > 4)
812 Con_Print("prvm_edict <program name> <edict number> <optional field name wildcard>\n");
817 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
820 i = atoi (Cmd_Argv(2));
821 if (i >= prog->num_edicts)
823 Con_Print("Bad edict number\n");
828 // Optional Wildcard Provided
829 wildcard_fieldname = Cmd_Argv(3);
832 wildcard_fieldname = NULL;
833 PRVM_ED_PrintNum (i, wildcard_fieldname);
845 // 2 possibilities : 1. just displaying the active edict count
846 // 2. making a function pointer [x]
847 void PRVM_ED_Count_f (void)
855 Con_Print("prvm_count <program name>\n");
860 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
863 if(prog->count_edicts)
864 prog->count_edicts();
868 for (i=0 ; i<prog->num_edicts ; i++)
870 ent = PRVM_EDICT_NUM(i);
871 if (ent->priv.required->free)
876 Con_Printf("num_edicts:%3i\n", prog->num_edicts);
877 Con_Printf("active :%3i\n", active);
884 ==============================================================================
888 FIXME: need to tag constants, doesn't really work
889 ==============================================================================
897 void PRVM_ED_WriteGlobals (qfile_t *f)
905 for (i = 0;i < prog->numglobaldefs;i++)
907 def = &prog->globaldefs[i];
909 if ( !(def->type & DEF_SAVEGLOBAL) )
911 type &= ~DEF_SAVEGLOBAL;
913 if (type != ev_string && type != ev_float && type != ev_entity)
916 name = PRVM_GetString(def->s_name);
918 if(developer_entityparsing.integer)
919 Con_Printf("PRVM_ED_WriteGlobals: at global %s\n", name);
921 prog->statestring = va("PRVM_ED_WriteGlobals, name=%s", name);
922 FS_Printf(f,"\"%s\" ", name);
923 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString((etype_t)type, (prvm_eval_t *)&prog->globals.generic[def->ofs]));
924 prog->statestring = NULL;
934 void PRVM_ED_ParseGlobals (const char *data)
936 char keyname[MAX_INPUTLINE];
942 if (!COM_ParseToken_Simple(&data, false, false))
943 PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace");
944 if (com_token[0] == '}')
947 if (developer_entityparsing.integer)
948 Con_Printf("Key: \"%s\"", com_token);
950 strlcpy (keyname, com_token, sizeof(keyname));
953 if (!COM_ParseToken_Simple(&data, false, true))
954 PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace");
956 if (developer_entityparsing.integer)
957 Con_Printf(" \"%s\"\n", com_token);
959 if (com_token[0] == '}')
960 PRVM_ERROR ("PRVM_ED_ParseGlobals: closing brace without data");
962 key = PRVM_ED_FindGlobal (keyname);
965 Con_DPrintf("'%s' is not a global on %s\n", keyname, PRVM_NAME);
969 if (!PRVM_ED_ParseEpair(NULL, key, com_token, true))
970 PRVM_ERROR ("PRVM_ED_ParseGlobals: parse error");
974 //============================================================================
981 Can parse either fields or globals
982 returns false if error
985 qboolean PRVM_ED_ParseEpair(prvm_edict_t *ent, ddef_t *key, const char *s, qboolean parsebackslash)
994 val = (prvm_eval_t *)(ent->fields.vp + key->ofs);
996 val = (prvm_eval_t *)(prog->globals.generic + key->ofs);
997 switch (key->type & ~DEF_SAVEGLOBAL)
1000 l = (int)strlen(s) + 1;
1001 val->string = PRVM_AllocString(l, &new_p);
1002 for (i = 0;i < l;i++)
1004 if (s[i] == '\\' && s[i+1] && parsebackslash)
1009 else if (s[i] == 'r')
1020 while (*s && ISWHITESPACE(*s))
1022 val->_float = atof(s);
1026 for (i = 0;i < 3;i++)
1028 while (*s && ISWHITESPACE(*s))
1032 val->vector[i] = atof(s);
1033 while (!ISWHITESPACE(*s))
1041 while (*s && ISWHITESPACE(*s))
1044 if (i >= prog->limit_edicts)
1045 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);
1046 while (i >= prog->max_edicts)
1047 PRVM_MEM_IncreaseEdicts();
1048 // if IncreaseEdicts was called the base pointer needs to be updated
1050 val = (prvm_eval_t *)(ent->fields.vp + key->ofs);
1051 val->edict = PRVM_EDICT_TO_PROG(PRVM_EDICT_NUM((int)i));
1057 Con_DPrintf("PRVM_ED_ParseEpair: Bogus field name %s in %s\n", s, PRVM_NAME);
1060 def = PRVM_ED_FindField(s + 1);
1063 Con_DPrintf("PRVM_ED_ParseEpair: Can't find field %s in %s\n", s, PRVM_NAME);
1066 val->_int = def->ofs;
1070 func = PRVM_ED_FindFunction(s);
1073 Con_Printf("PRVM_ED_ParseEpair: Can't find function %s in %s\n", s, PRVM_NAME);
1076 val->function = func - prog->functions;
1080 Con_Printf("PRVM_ED_ParseEpair: Unknown key->type %i for key \"%s\" on %s\n", key->type, PRVM_GetString(key->s_name), PRVM_NAME);
1090 Console command to send a string to QC function GameCommand of the
1094 sv_cmd adminmsg 3 "do not teamkill"
1095 cl_cmd someclientcommand
1096 menu_cmd somemenucommand
1098 All progs can support this extension; sg calls it in server QC, cg in client
1102 void PRVM_GameCommand(const char *whichprogs, const char *whichcmd)
1106 Con_Printf("%s text...\n", whichcmd);
1111 if(!PRVM_SetProgFromString(whichprogs))
1112 // note: this is not PRVM_SetProg because that one aborts "hard" using PRVM_Error
1113 // also, it makes printing error messages easier!
1115 Con_Printf("%s program not loaded.\n", whichprogs);
1119 if(!prog->funcoffsets.GameCommand)
1121 Con_Printf("%s program do not support GameCommand!\n", whichprogs);
1125 int restorevm_tempstringsbuf_cursize;
1130 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
1131 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(s ? s : "");
1132 PRVM_ExecuteProgram (prog->funcoffsets.GameCommand, "QC function GameCommand is missing");
1133 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1138 void PRVM_GameCommand_Server_f(void)
1140 PRVM_GameCommand("server", "sv_cmd");
1142 void PRVM_GameCommand_Client_f(void)
1144 PRVM_GameCommand("client", "cl_cmd");
1146 void PRVM_GameCommand_Menu_f(void)
1148 PRVM_GameCommand("menu", "menu_cmd");
1155 Console command to load a field of a specified edict
1158 void PRVM_ED_EdictGet_f(void)
1165 if(Cmd_Argc() != 4 && Cmd_Argc() != 5)
1167 Con_Print("prvm_edictget <program name> <edict number> <field> [<cvar>]\n");
1172 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1174 Con_Printf("Wrong program name %s !\n", Cmd_Argv(1));
1178 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1180 if((key = PRVM_ED_FindField(Cmd_Argv(3))) == 0)
1182 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1186 v = (prvm_eval_t *)(ed->fields.vp + key->ofs);
1187 s = PRVM_UglyValueString((etype_t)key->type, v);
1190 cvar_t *cvar = Cvar_FindVar(Cmd_Argv(4));
1191 if (cvar && cvar->flags & CVAR_READONLY)
1193 Con_Printf("prvm_edictget: %s is read-only\n", cvar->name);
1196 Cvar_Get(Cmd_Argv(4), s, 0, NULL);
1199 Con_Printf("%s\n", s);
1205 void PRVM_ED_GlobalGet_f(void)
1211 if(Cmd_Argc() != 3 && Cmd_Argc() != 4)
1213 Con_Print("prvm_globalget <program name> <global> [<cvar>]\n");
1218 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1220 Con_Printf("Wrong program name %s !\n", Cmd_Argv(1));
1224 key = PRVM_ED_FindGlobal(Cmd_Argv(2));
1227 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
1231 v = (prvm_eval_t *) &prog->globals.generic[key->ofs];
1232 s = PRVM_UglyValueString((etype_t)key->type, v);
1235 cvar_t *cvar = Cvar_FindVar(Cmd_Argv(3));
1236 if (cvar && cvar->flags & CVAR_READONLY)
1238 Con_Printf("prvm_globalget: %s is read-only\n", cvar->name);
1241 Cvar_Get(Cmd_Argv(3), s, 0, NULL);
1244 Con_Printf("%s\n", s);
1254 Console command to set a field of a specified edict
1257 void PRVM_ED_EdictSet_f(void)
1264 Con_Print("prvm_edictset <program name> <edict number> <field> <value>\n");
1269 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1271 Con_Printf("Wrong program name %s !\n", Cmd_Argv(1));
1275 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1277 if((key = PRVM_ED_FindField(Cmd_Argv(3))) == 0)
1278 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1280 PRVM_ED_ParseEpair(ed, key, Cmd_Argv(4), true);
1286 ====================
1289 Parses an edict out of the given string, returning the new position
1290 ed should be a properly initialized empty edict.
1291 Used for initial level load and for savegames.
1292 ====================
1294 const char *PRVM_ED_ParseEdict (const char *data, prvm_edict_t *ent)
1304 // go through all the dictionary pairs
1308 if (!COM_ParseToken_Simple(&data, false, false))
1309 PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1310 if (developer_entityparsing.integer)
1311 Con_Printf("Key: \"%s\"", com_token);
1312 if (com_token[0] == '}')
1315 // anglehack is to allow QuakeEd to write single scalar angles
1316 // and allow them to be turned into vectors. (FIXME...)
1317 if (!strcmp(com_token, "angle"))
1319 strlcpy (com_token, "angles", sizeof(com_token));
1325 // FIXME: change light to _light to get rid of this hack
1326 if (!strcmp(com_token, "light"))
1327 strlcpy (com_token, "light_lev", sizeof(com_token)); // hack for single light def
1329 strlcpy (keyname, com_token, sizeof(keyname));
1331 // another hack to fix keynames with trailing spaces
1332 n = strlen(keyname);
1333 while (n && keyname[n-1] == ' ')
1340 if (!COM_ParseToken_Simple(&data, false, false))
1341 PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1342 if (developer_entityparsing.integer)
1343 Con_Printf(" \"%s\"\n", com_token);
1345 if (com_token[0] == '}')
1346 PRVM_ERROR ("PRVM_ED_ParseEdict: closing brace without data");
1350 // ignore attempts to set key "" (this problem occurs in nehahra neh1m8.bsp)
1354 // keynames with a leading underscore are used for utility comments,
1355 // and are immediately discarded by quake
1356 if (keyname[0] == '_')
1359 key = PRVM_ED_FindField (keyname);
1362 Con_DPrintf("%s: '%s' is not a field\n", PRVM_NAME, keyname);
1369 strlcpy (temp, com_token, sizeof(temp));
1370 dpsnprintf (com_token, sizeof(com_token), "0 %s 0", temp);
1373 if (!PRVM_ED_ParseEpair(ent, key, com_token, strcmp(keyname, "wad") != 0))
1374 PRVM_ERROR ("PRVM_ED_ParseEdict: parse error");
1378 ent->priv.required->free = true;
1386 PRVM_ED_LoadFromFile
1388 The entities are directly placed in the array, rather than allocated with
1389 PRVM_ED_Alloc, because otherwise an error loading the map would have entity
1390 number references out of order.
1392 Creates a server's entity / program execution context by
1393 parsing textual entity definitions out of an ent file.
1395 Used for both fresh maps and savegame loads. A fresh map would also need
1396 to call PRVM_ED_CallSpawnFunctions () to let the objects initialize themselves.
1399 void PRVM_ED_LoadFromFile (const char *data)
1402 int parsed, inhibited, spawned, died;
1403 const char *funcname;
1411 prvm_reuseedicts_always_allow = realtime;
1416 // parse the opening brace
1417 if (!COM_ParseToken_Simple(&data, false, false))
1419 if (com_token[0] != '{')
1420 PRVM_ERROR ("PRVM_ED_LoadFromFile: %s: found %s when expecting {", PRVM_NAME, com_token);
1422 // CHANGED: this is not conform to PR_LoadFromFile
1423 if(prog->loadintoworld)
1425 prog->loadintoworld = false;
1426 ent = PRVM_EDICT_NUM(0);
1429 ent = PRVM_ED_Alloc();
1432 if (ent != prog->edicts) // hack
1433 memset (ent->fields.vp, 0, prog->entityfields * 4);
1435 data = PRVM_ED_ParseEdict (data, ent);
1438 // remove the entity ?
1439 if(prog->load_edict && !prog->load_edict(ent))
1446 if (prog->funcoffsets.SV_OnEntityPreSpawnFunction)
1449 PRVM_GLOBALFIELDEDICT(prog->globaloffsets.self) = PRVM_EDICT_TO_PROG(ent);
1450 PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityPreSpawnFunction, "QC function SV_OnEntityPreSpawnFunction is missing");
1453 if(ent->priv.required->free)
1460 // immediately call spawn function, but only if there is a self global and a classname
1462 if(!ent->priv.required->free)
1463 if(prog->globaloffsets.self >= 0 && prog->fieldoffsets.classname >= 0)
1465 string_t handle = PRVM_EDICTFIELDSTRING(ent, prog->fieldoffsets.classname);
1468 Con_Print("No classname for:\n");
1469 PRVM_ED_Print(ent, NULL);
1474 // look for the spawn function
1475 funcname = PRVM_GetString(handle);
1476 func = PRVM_ED_FindFunction (va("spawnfunc_%s", funcname));
1478 if(!PRVM_GLOBALFIELDFLOAT(prog->globaloffsets.require_spawnfunc_prefix))
1479 func = PRVM_ED_FindFunction (funcname);
1483 // check for OnEntityNoSpawnFunction
1484 if (prog->funcoffsets.SV_OnEntityNoSpawnFunction)
1487 PRVM_GLOBALFIELDEDICT(prog->globaloffsets.self) = PRVM_EDICT_TO_PROG(ent);
1488 PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityNoSpawnFunction, "QC function SV_OnEntityNoSpawnFunction is missing");
1492 if (developer.integer > 0) // don't confuse non-developers with errors
1494 Con_Print("No spawn function for:\n");
1495 PRVM_ED_Print(ent, NULL);
1498 continue; // not included in "inhibited" count
1504 PRVM_GLOBALFIELDEDICT(prog->globaloffsets.self) = PRVM_EDICT_TO_PROG(ent);
1505 PRVM_ExecuteProgram (func - prog->functions, "");
1509 if(!ent->priv.required->free)
1510 if (prog->funcoffsets.SV_OnEntityPostSpawnFunction)
1513 PRVM_GLOBALFIELDEDICT(prog->globaloffsets.self) = PRVM_EDICT_TO_PROG(ent);
1514 PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityPostSpawnFunction, "QC function SV_OnEntityPostSpawnFunction is missing");
1518 if (ent->priv.required->free)
1522 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);
1524 prvm_reuseedicts_always_allow = 0;
1527 void PRVM_FindOffsets(void)
1529 // field and global searches use -1 for NULL
1530 memset(&prog->fieldoffsets, -1, sizeof(prog->fieldoffsets));
1531 memset(&prog->globaloffsets, -1, sizeof(prog->globaloffsets));
1532 // functions use 0 for NULL
1533 memset(&prog->funcoffsets, 0, sizeof(prog->funcoffsets));
1535 // server and client qc use a lot of similar fields, so this is combined
1536 prog->fieldoffsets.SendEntity = PRVM_ED_FindFieldOffset("SendEntity");
1537 prog->fieldoffsets.SendFlags = PRVM_ED_FindFieldOffset("SendFlags");
1538 prog->fieldoffsets.Version = PRVM_ED_FindFieldOffset("Version");
1539 prog->fieldoffsets.alpha = PRVM_ED_FindFieldOffset("alpha");
1540 prog->fieldoffsets.ammo_cells1 = PRVM_ED_FindFieldOffset("ammo_cells1");
1541 prog->fieldoffsets.ammo_lava_nails = PRVM_ED_FindFieldOffset("ammo_lava_nails");
1542 prog->fieldoffsets.ammo_multi_rockets = PRVM_ED_FindFieldOffset("ammo_multi_rockets");
1543 prog->fieldoffsets.ammo_nails1 = PRVM_ED_FindFieldOffset("ammo_nails1");
1544 prog->fieldoffsets.ammo_plasma = PRVM_ED_FindFieldOffset("ammo_plasma");
1545 prog->fieldoffsets.ammo_rockets1 = PRVM_ED_FindFieldOffset("ammo_rockets1");
1546 prog->fieldoffsets.ammo_shells1 = PRVM_ED_FindFieldOffset("ammo_shells1");
1547 prog->fieldoffsets.angles = PRVM_ED_FindFieldOffset("angles");
1548 prog->fieldoffsets.button3 = PRVM_ED_FindFieldOffset("button3");
1549 prog->fieldoffsets.button4 = PRVM_ED_FindFieldOffset("button4");
1550 prog->fieldoffsets.button5 = PRVM_ED_FindFieldOffset("button5");
1551 prog->fieldoffsets.button6 = PRVM_ED_FindFieldOffset("button6");
1552 prog->fieldoffsets.button7 = PRVM_ED_FindFieldOffset("button7");
1553 prog->fieldoffsets.button8 = PRVM_ED_FindFieldOffset("button8");
1554 prog->fieldoffsets.button9 = PRVM_ED_FindFieldOffset("button9");
1555 prog->fieldoffsets.button10 = PRVM_ED_FindFieldOffset("button10");
1556 prog->fieldoffsets.button11 = PRVM_ED_FindFieldOffset("button11");
1557 prog->fieldoffsets.button12 = PRVM_ED_FindFieldOffset("button12");
1558 prog->fieldoffsets.button13 = PRVM_ED_FindFieldOffset("button13");
1559 prog->fieldoffsets.button14 = PRVM_ED_FindFieldOffset("button14");
1560 prog->fieldoffsets.button15 = PRVM_ED_FindFieldOffset("button15");
1561 prog->fieldoffsets.button16 = PRVM_ED_FindFieldOffset("button16");
1562 prog->fieldoffsets.buttonchat = PRVM_ED_FindFieldOffset("buttonchat");
1563 prog->fieldoffsets.buttonuse = PRVM_ED_FindFieldOffset("buttonuse");
1564 prog->fieldoffsets.chain = PRVM_ED_FindFieldOffset("chain");
1565 prog->fieldoffsets.classname = PRVM_ED_FindFieldOffset("classname");
1566 prog->fieldoffsets.clientcamera = PRVM_ED_FindFieldOffset("clientcamera");
1567 prog->fieldoffsets.clientcolors = PRVM_ED_FindFieldOffset("clientcolors");
1568 prog->fieldoffsets.clientstatus = PRVM_ED_FindFieldOffset("clientstatus");
1569 prog->fieldoffsets.color = PRVM_ED_FindFieldOffset("color");
1570 prog->fieldoffsets.colormod = PRVM_ED_FindFieldOffset("colormod");
1571 prog->fieldoffsets.contentstransition = PRVM_ED_FindFieldOffset("contentstransition");
1572 prog->fieldoffsets.cursor_active = PRVM_ED_FindFieldOffset("cursor_active");
1573 prog->fieldoffsets.cursor_screen = PRVM_ED_FindFieldOffset("cursor_screen");
1574 prog->fieldoffsets.cursor_trace_endpos = PRVM_ED_FindFieldOffset("cursor_trace_endpos");
1575 prog->fieldoffsets.cursor_trace_ent = PRVM_ED_FindFieldOffset("cursor_trace_ent");
1576 prog->fieldoffsets.cursor_trace_start = PRVM_ED_FindFieldOffset("cursor_trace_start");
1577 prog->fieldoffsets.customizeentityforclient = PRVM_ED_FindFieldOffset("customizeentityforclient");
1578 prog->fieldoffsets.dimension_hit = PRVM_ED_FindFieldOffset("dimension_hit");
1579 prog->fieldoffsets.dimension_solid = PRVM_ED_FindFieldOffset("dimension_solid");
1580 prog->fieldoffsets.disableclientprediction = PRVM_ED_FindFieldOffset("disableclientprediction");
1581 prog->fieldoffsets.discardabledemo = PRVM_ED_FindFieldOffset("discardabledemo");
1582 prog->fieldoffsets.dphitcontentsmask = PRVM_ED_FindFieldOffset("dphitcontentsmask");
1583 prog->fieldoffsets.drawonlytoclient = PRVM_ED_FindFieldOffset("drawonlytoclient");
1584 prog->fieldoffsets.exteriormodeltoclient = PRVM_ED_FindFieldOffset("exteriormodeltoclient");
1585 prog->fieldoffsets.fatness = PRVM_ED_FindFieldOffset("fatness");
1586 prog->fieldoffsets.forceshader = PRVM_ED_FindFieldOffset("forceshader");
1587 prog->fieldoffsets.frame = PRVM_ED_FindFieldOffset("frame");
1588 prog->fieldoffsets.frame1time = PRVM_ED_FindFieldOffset("frame1time");
1589 prog->fieldoffsets.frame2 = PRVM_ED_FindFieldOffset("frame2");
1590 prog->fieldoffsets.frame2time = PRVM_ED_FindFieldOffset("frame2time");
1591 prog->fieldoffsets.frame3 = PRVM_ED_FindFieldOffset("frame3");
1592 prog->fieldoffsets.frame3time = PRVM_ED_FindFieldOffset("frame3time");
1593 prog->fieldoffsets.frame4 = PRVM_ED_FindFieldOffset("frame4");
1594 prog->fieldoffsets.frame4time = PRVM_ED_FindFieldOffset("frame4time");
1595 prog->fieldoffsets.fullbright = PRVM_ED_FindFieldOffset("fullbright");
1596 prog->fieldoffsets.glow_color = PRVM_ED_FindFieldOffset("glow_color");
1597 prog->fieldoffsets.glow_size = PRVM_ED_FindFieldOffset("glow_size");
1598 prog->fieldoffsets.glow_trail = PRVM_ED_FindFieldOffset("glow_trail");
1599 prog->fieldoffsets.glowmod = PRVM_ED_FindFieldOffset("glowmod");
1600 prog->fieldoffsets.gravity = PRVM_ED_FindFieldOffset("gravity");
1601 prog->fieldoffsets.groundentity = PRVM_ED_FindFieldOffset("groundentity");
1602 prog->fieldoffsets.hull = PRVM_ED_FindFieldOffset("hull");
1603 prog->fieldoffsets.ideal_yaw = PRVM_ED_FindFieldOffset("ideal_yaw");
1604 prog->fieldoffsets.idealpitch = PRVM_ED_FindFieldOffset("idealpitch");
1605 prog->fieldoffsets.items2 = PRVM_ED_FindFieldOffset("items2");
1606 prog->fieldoffsets.lerpfrac = PRVM_ED_FindFieldOffset("lerpfrac");
1607 prog->fieldoffsets.lerpfrac3 = PRVM_ED_FindFieldOffset("lerpfrac3");
1608 prog->fieldoffsets.lerpfrac4 = PRVM_ED_FindFieldOffset("lerpfrac4");
1609 prog->fieldoffsets.light_lev = PRVM_ED_FindFieldOffset("light_lev");
1610 prog->fieldoffsets.message = PRVM_ED_FindFieldOffset("message");
1611 prog->fieldoffsets.modelflags = PRVM_ED_FindFieldOffset("modelflags");
1612 prog->fieldoffsets.movement = PRVM_ED_FindFieldOffset("movement");
1613 prog->fieldoffsets.movetypesteplandevent = PRVM_ED_FindFieldOffset("movetypesteplandevent");
1614 prog->fieldoffsets.netaddress = PRVM_ED_FindFieldOffset("netaddress");
1615 prog->fieldoffsets.nextthink = PRVM_ED_FindFieldOffset("nextthink");
1616 prog->fieldoffsets.nodrawtoclient = PRVM_ED_FindFieldOffset("nodrawtoclient");
1617 prog->fieldoffsets.pflags = PRVM_ED_FindFieldOffset("pflags");
1618 prog->fieldoffsets.ping = PRVM_ED_FindFieldOffset("ping");
1619 prog->fieldoffsets.ping_packetloss = PRVM_ED_FindFieldOffset("ping_packetloss");
1620 prog->fieldoffsets.ping_movementloss = PRVM_ED_FindFieldOffset("ping_movementloss");
1621 prog->fieldoffsets.pitch_speed = PRVM_ED_FindFieldOffset("pitch_speed");
1622 prog->fieldoffsets.playermodel = PRVM_ED_FindFieldOffset("playermodel");
1623 prog->fieldoffsets.playerskin = PRVM_ED_FindFieldOffset("playerskin");
1624 prog->fieldoffsets.pmodel = PRVM_ED_FindFieldOffset("pmodel");
1625 prog->fieldoffsets.punchvector = PRVM_ED_FindFieldOffset("punchvector");
1626 prog->fieldoffsets.renderamt = PRVM_ED_FindFieldOffset("renderamt"); // HalfLife support
1627 prog->fieldoffsets.renderflags = PRVM_ED_FindFieldOffset("renderflags");
1628 prog->fieldoffsets.rendermode = PRVM_ED_FindFieldOffset("rendermode"); // HalfLife support
1629 prog->fieldoffsets.scale = PRVM_ED_FindFieldOffset("scale");
1630 prog->fieldoffsets.shadertime = PRVM_ED_FindFieldOffset("shadertime");
1631 prog->fieldoffsets.skeletonindex = PRVM_ED_FindFieldOffset("skeletonindex");
1632 prog->fieldoffsets.style = PRVM_ED_FindFieldOffset("style");
1633 prog->fieldoffsets.tag_entity = PRVM_ED_FindFieldOffset("tag_entity");
1634 prog->fieldoffsets.tag_index = PRVM_ED_FindFieldOffset("tag_index");
1635 prog->fieldoffsets.think = PRVM_ED_FindFieldOffset("think");
1636 prog->fieldoffsets.viewmodelforclient = PRVM_ED_FindFieldOffset("viewmodelforclient");
1637 prog->fieldoffsets.viewzoom = PRVM_ED_FindFieldOffset("viewzoom");
1638 prog->fieldoffsets.yaw_speed = PRVM_ED_FindFieldOffset("yaw_speed");
1639 prog->fieldoffsets.bouncefactor = PRVM_ED_FindFieldOffset("bouncefactor");
1640 prog->fieldoffsets.bouncestop = PRVM_ED_FindFieldOffset("bouncestop");
1641 prog->fieldoffsets.sendcomplexanimation = PRVM_ED_FindFieldOffset("sendcomplexanimation");
1643 prog->fieldoffsets.solid = PRVM_ED_FindFieldOffset("solid");
1644 prog->fieldoffsets.movetype = PRVM_ED_FindFieldOffset("movetype");
1645 prog->fieldoffsets.modelindex = PRVM_ED_FindFieldOffset("modelindex");
1646 prog->fieldoffsets.mins = PRVM_ED_FindFieldOffset("mins");
1647 prog->fieldoffsets.maxs = PRVM_ED_FindFieldOffset("maxs");
1648 prog->fieldoffsets.mass = PRVM_ED_FindFieldOffset("mass");
1649 prog->fieldoffsets.origin = PRVM_ED_FindFieldOffset("origin");
1650 prog->fieldoffsets.velocity = PRVM_ED_FindFieldOffset("velocity");
1651 //prog->fieldoffsets.axis_forward = PRVM_ED_FindFieldOffset("axis_forward");
1652 //prog->fieldoffsets.axis_left = PRVM_ED_FindFieldOffset("axis_left");
1653 //prog->fieldoffsets.axis_up = PRVM_ED_FindFieldOffset("axis_up");
1654 //prog->fieldoffsets.spinvelocity = PRVM_ED_FindFieldOffset("spinvelocity");
1655 prog->fieldoffsets.angles = PRVM_ED_FindFieldOffset("angles");
1656 prog->fieldoffsets.avelocity = PRVM_ED_FindFieldOffset("avelocity");
1657 prog->fieldoffsets.aiment = PRVM_ED_FindFieldOffset("aiment");
1658 prog->fieldoffsets.enemy = PRVM_ED_FindFieldOffset("enemy");
1659 prog->fieldoffsets.jointtype = PRVM_ED_FindFieldOffset("jointtype");
1660 prog->fieldoffsets.movedir = PRVM_ED_FindFieldOffset("movedir");
1662 prog->fieldoffsets.camera_transform = PRVM_ED_FindFieldOffset("camera_transform");
1663 prog->fieldoffsets.userwavefunc_param0 = PRVM_ED_FindFieldOffset("userwavefunc_param0");
1664 prog->fieldoffsets.userwavefunc_param1 = PRVM_ED_FindFieldOffset("userwavefunc_param1");
1665 prog->fieldoffsets.userwavefunc_param2 = PRVM_ED_FindFieldOffset("userwavefunc_param2");
1666 prog->fieldoffsets.userwavefunc_param3 = PRVM_ED_FindFieldOffset("userwavefunc_param3");
1668 prog->fieldoffsets.crypto_keyfp = PRVM_ED_FindFieldOffset("crypto_keyfp");
1669 prog->fieldoffsets.crypto_mykeyfp = PRVM_ED_FindFieldOffset("crypto_mykeyfp");
1670 prog->fieldoffsets.crypto_idfp = PRVM_ED_FindFieldOffset("crypto_idfp");
1671 prog->fieldoffsets.crypto_encryptmethod = PRVM_ED_FindFieldOffset("crypto_encryptmethod");
1672 prog->fieldoffsets.crypto_signmethod = PRVM_ED_FindFieldOffset("crypto_signmethod");
1674 prog->funcoffsets.CSQC_ConsoleCommand = PRVM_ED_FindFunctionOffset("CSQC_ConsoleCommand");
1675 prog->funcoffsets.CSQC_Ent_Remove = PRVM_ED_FindFunctionOffset("CSQC_Ent_Remove");
1676 prog->funcoffsets.CSQC_Ent_Spawn = PRVM_ED_FindFunctionOffset("CSQC_Ent_Spawn");
1677 prog->funcoffsets.CSQC_Ent_Update = PRVM_ED_FindFunctionOffset("CSQC_Ent_Update");
1678 prog->funcoffsets.CSQC_Event = PRVM_ED_FindFunctionOffset("CSQC_Event");
1679 prog->funcoffsets.CSQC_Event_Sound = PRVM_ED_FindFunctionOffset("CSQC_Event_Sound");
1680 prog->funcoffsets.CSQC_Init = PRVM_ED_FindFunctionOffset("CSQC_Init");
1681 prog->funcoffsets.CSQC_InputEvent = PRVM_ED_FindFunctionOffset("CSQC_InputEvent");
1682 prog->funcoffsets.CSQC_Parse_CenterPrint = PRVM_ED_FindFunctionOffset("CSQC_Parse_CenterPrint");
1683 prog->funcoffsets.CSQC_Parse_Print = PRVM_ED_FindFunctionOffset("CSQC_Parse_Print");
1684 prog->funcoffsets.CSQC_Parse_StuffCmd = PRVM_ED_FindFunctionOffset("CSQC_Parse_StuffCmd");
1685 prog->funcoffsets.CSQC_Parse_TempEntity = PRVM_ED_FindFunctionOffset("CSQC_Parse_TempEntity");
1686 prog->funcoffsets.CSQC_Shutdown = PRVM_ED_FindFunctionOffset("CSQC_Shutdown");
1687 prog->funcoffsets.CSQC_UpdateView = PRVM_ED_FindFunctionOffset("CSQC_UpdateView");
1688 prog->funcoffsets.EndFrame = PRVM_ED_FindFunctionOffset("EndFrame");
1689 prog->funcoffsets.GameCommand = PRVM_ED_FindFunctionOffset("GameCommand");
1690 prog->funcoffsets.Gecko_Query = PRVM_ED_FindFunctionOffset("Gecko_Query");
1691 prog->funcoffsets.RestoreGame = PRVM_ED_FindFunctionOffset("RestoreGame");
1692 prog->funcoffsets.SV_ChangeTeam = PRVM_ED_FindFunctionOffset("SV_ChangeTeam");
1693 prog->funcoffsets.SV_OnEntityNoSpawnFunction = PRVM_ED_FindFunctionOffset("SV_OnEntityNoSpawnFunction");
1694 prog->funcoffsets.SV_OnEntityPostSpawnFunction = PRVM_ED_FindFunctionOffset("SV_OnEntityPostSpawnFunction");
1695 prog->funcoffsets.SV_OnEntityPreSpawnFunction = PRVM_ED_FindFunctionOffset("SV_OnEntityPreSpawnFunction");
1696 prog->funcoffsets.SV_ParseClientCommand = PRVM_ED_FindFunctionOffset("SV_ParseClientCommand");
1697 prog->funcoffsets.SV_PausedTic = PRVM_ED_FindFunctionOffset("SV_PausedTic");
1698 prog->funcoffsets.SV_PlayerPhysics = PRVM_ED_FindFunctionOffset("SV_PlayerPhysics");
1699 prog->funcoffsets.SV_Shutdown = PRVM_ED_FindFunctionOffset("SV_Shutdown");
1700 prog->funcoffsets.URI_Get_Callback = PRVM_ED_FindFunctionOffset("URI_Get_Callback");
1701 prog->globaloffsets.SV_InitCmd = PRVM_ED_FindGlobalOffset("SV_InitCmd");
1702 prog->globaloffsets.coop = PRVM_ED_FindGlobalOffset("coop");
1703 prog->globaloffsets.deathmatch = PRVM_ED_FindGlobalOffset("deathmatch");
1704 prog->globaloffsets.dmg_origin = PRVM_ED_FindGlobalOffset("dmg_origin");
1705 prog->globaloffsets.dmg_save = PRVM_ED_FindGlobalOffset("dmg_save");
1706 prog->globaloffsets.dmg_take = PRVM_ED_FindGlobalOffset("dmg_take");
1707 prog->globaloffsets.drawfont = PRVM_ED_FindGlobalOffset("drawfont");
1708 prog->globaloffsets.drawfontscale = PRVM_ED_FindGlobalOffset("drawfontscale");
1709 prog->globaloffsets.gettaginfo_forward = PRVM_ED_FindGlobalOffset("gettaginfo_forward");
1710 prog->globaloffsets.gettaginfo_name = PRVM_ED_FindGlobalOffset("gettaginfo_name");
1711 prog->globaloffsets.gettaginfo_offset = PRVM_ED_FindGlobalOffset("gettaginfo_offset");
1712 prog->globaloffsets.gettaginfo_parent = PRVM_ED_FindGlobalOffset("gettaginfo_parent");
1713 prog->globaloffsets.gettaginfo_right = PRVM_ED_FindGlobalOffset("gettaginfo_right");
1714 prog->globaloffsets.gettaginfo_up = PRVM_ED_FindGlobalOffset("gettaginfo_up");
1715 prog->globaloffsets.transparent_offset = PRVM_ED_FindGlobalOffset("transparent_offset");
1716 prog->globaloffsets.intermission = PRVM_ED_FindGlobalOffset("intermission");
1717 prog->globaloffsets.require_spawnfunc_prefix = PRVM_ED_FindGlobalOffset("require_spawnfunc_prefix");
1718 prog->globaloffsets.sb_showscores = PRVM_ED_FindGlobalOffset("sb_showscores");
1719 prog->globaloffsets.self = PRVM_ED_FindGlobalOffset("self");
1720 prog->globaloffsets.serverdeltatime = PRVM_ED_FindGlobalOffset("serverdeltatime");
1721 prog->globaloffsets.serverprevtime = PRVM_ED_FindGlobalOffset("serverprevtime");
1722 prog->globaloffsets.servertime = PRVM_ED_FindGlobalOffset("servertime");
1723 prog->globaloffsets.time = PRVM_ED_FindGlobalOffset("time");
1724 prog->globaloffsets.trace_allsolid = PRVM_ED_FindGlobalOffset("trace_allsolid");
1725 prog->globaloffsets.trace_dphitcontents = PRVM_ED_FindGlobalOffset("trace_dphitcontents");
1726 prog->globaloffsets.trace_dphitq3surfaceflags = PRVM_ED_FindGlobalOffset("trace_dphitq3surfaceflags");
1727 prog->globaloffsets.trace_dphittexturename = PRVM_ED_FindGlobalOffset("trace_dphittexturename");
1728 prog->globaloffsets.trace_dpstartcontents = PRVM_ED_FindGlobalOffset("trace_dpstartcontents");
1729 prog->globaloffsets.trace_endpos = PRVM_ED_FindGlobalOffset("trace_endpos");
1730 prog->globaloffsets.trace_ent = PRVM_ED_FindGlobalOffset("trace_ent");
1731 prog->globaloffsets.trace_fraction = PRVM_ED_FindGlobalOffset("trace_fraction");
1732 prog->globaloffsets.trace_inopen = PRVM_ED_FindGlobalOffset("trace_inopen");
1733 prog->globaloffsets.trace_inwater = PRVM_ED_FindGlobalOffset("trace_inwater");
1734 prog->globaloffsets.trace_networkentity = PRVM_ED_FindGlobalOffset("trace_networkentity");
1735 prog->globaloffsets.trace_plane_dist = PRVM_ED_FindGlobalOffset("trace_plane_dist");
1736 prog->globaloffsets.trace_plane_normal = PRVM_ED_FindGlobalOffset("trace_plane_normal");
1737 prog->globaloffsets.trace_startsolid = PRVM_ED_FindGlobalOffset("trace_startsolid");
1738 prog->globaloffsets.v_forward = PRVM_ED_FindGlobalOffset("v_forward");
1739 prog->globaloffsets.v_right = PRVM_ED_FindGlobalOffset("v_right");
1740 prog->globaloffsets.v_up = PRVM_ED_FindGlobalOffset("v_up");
1741 prog->globaloffsets.view_angles = PRVM_ED_FindGlobalOffset("view_angles");
1742 prog->globaloffsets.view_punchangle = PRVM_ED_FindGlobalOffset("view_punchangle");
1743 prog->globaloffsets.view_punchvector = PRVM_ED_FindGlobalOffset("view_punchvector");
1744 prog->globaloffsets.worldstatus = PRVM_ED_FindGlobalOffset("worldstatus");
1745 prog->globaloffsets.particles_alphamin = PRVM_ED_FindGlobalOffset("particles_alphamin");
1746 prog->globaloffsets.particles_alphamax = PRVM_ED_FindGlobalOffset("particles_alphamax");
1747 prog->globaloffsets.particles_colormin = PRVM_ED_FindGlobalOffset("particles_colormin");
1748 prog->globaloffsets.particles_colormax = PRVM_ED_FindGlobalOffset("particles_colormax");
1750 // menu qc only uses some functions, nothing else
1751 prog->funcoffsets.m_draw = PRVM_ED_FindFunctionOffset("m_draw");
1752 prog->funcoffsets.m_init = PRVM_ED_FindFunctionOffset("m_init");
1753 prog->funcoffsets.m_keydown = PRVM_ED_FindFunctionOffset("m_keydown");
1754 prog->funcoffsets.m_keyup = PRVM_ED_FindFunctionOffset("m_keyup");
1755 prog->funcoffsets.m_shutdown = PRVM_ED_FindFunctionOffset("m_shutdown");
1756 prog->funcoffsets.m_toggle = PRVM_ED_FindFunctionOffset("m_toggle");
1757 prog->funcoffsets.m_newmap = PRVM_ED_FindFunctionOffset("m_newmap");
1762 typedef struct dpfield_s
1769 #define DPFIELDS (sizeof(dpfields) / sizeof(dpfield_t))
1771 dpfield_t dpfields[] =
1782 #define PO_HASHSIZE 16384
1783 typedef struct po_string_s
1786 struct po_string_s *nextonhashchain;
1791 po_string_t *hashtable[PO_HASHSIZE];
1794 void PRVM_PO_UnparseString(char *out, const char *in, size_t outsize)
1803 case '\a': if(outsize >= 2) { *out++ = '\\'; *out++ = 'a'; outsize -= 2; } break;
1804 case '\b': if(outsize >= 2) { *out++ = '\\'; *out++ = 'b'; outsize -= 2; } break;
1805 case '\t': if(outsize >= 2) { *out++ = '\\'; *out++ = 't'; outsize -= 2; } break;
1806 case '\r': if(outsize >= 2) { *out++ = '\\'; *out++ = 'r'; outsize -= 2; } break;
1807 case '\n': if(outsize >= 2) { *out++ = '\\'; *out++ = 'n'; outsize -= 2; } break;
1808 case '\\': if(outsize >= 2) { *out++ = '\\'; *out++ = '\\'; outsize -= 2; } break;
1809 case '"': if(outsize >= 2) { *out++ = '\\'; *out++ = '"'; outsize -= 2; } break;
1811 if(*in >= 0 && *in <= 0x1F)
1816 *out++ = '0' + ((*in & 0700) >> 6);
1817 *out++ = '0' + ((*in & 0070) >> 3);
1818 *out++ = '0' + ((*in & 0007));
1835 void PRVM_PO_ParseString(char *out, const char *in, size_t outsize)
1848 case 'a': if(outsize > 0) { *out++ = '\a'; --outsize; } break;
1849 case 'b': if(outsize > 0) { *out++ = '\b'; --outsize; } break;
1850 case 't': if(outsize > 0) { *out++ = '\t'; --outsize; } break;
1851 case 'r': if(outsize > 0) { *out++ = '\r'; --outsize; } break;
1852 case 'n': if(outsize > 0) { *out++ = '\n'; --outsize; } break;
1853 case '\\': if(outsize > 0) { *out++ = '\\'; --outsize; } break;
1854 case '"': if(outsize > 0) { *out++ = '"'; --outsize; } break;
1855 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
1859 if(*in >= '0' && *in <= '7')
1862 *out = (*out << 3) | (*in - '0');
1865 if(*in >= '0' && *in <= '7')
1868 *out = (*out << 3) | (*in - '0');
1879 if(outsize > 0) { *out++ = *in; --outsize; }
1894 po_t *PRVM_PO_Load(const char *filename, mempool_t *pool)
1899 char inbuf[MAX_INPUTLINE];
1900 char decodedbuf[MAX_INPUTLINE];
1903 po_string_t thisstr;
1904 const char *buf = (const char *) FS_LoadFile(filename, pool, true, NULL);
1909 memset(&thisstr, 0, sizeof(thisstr)); // hush compiler warning
1911 po = (po_t *)Mem_Alloc(pool, sizeof(*po));
1912 memset(po, 0, sizeof(*po));
1920 p = strchr(p, '\n');
1926 if(*p == '\r' || *p == '\n')
1931 if(!strncmp(p, "msgid \"", 7))
1936 else if(!strncmp(p, "msgstr \"", 8))
1943 p = strchr(p, '\n');
1953 q = strchr(p, '\n');
1960 if((size_t)(q - p) >= (size_t) sizeof(inbuf))
1962 strlcpy(inbuf, p, q - p); // not - 1, because this adds a NUL
1963 PRVM_PO_ParseString(decodedbuf + decodedpos, inbuf, sizeof(decodedbuf) - decodedpos);
1964 decodedpos += strlen(decodedbuf + decodedpos);
1974 Mem_Free(thisstr.key);
1975 thisstr.key = (char *)Mem_Alloc(pool, decodedpos + 1);
1976 memcpy(thisstr.key, decodedbuf, decodedpos + 1);
1978 else if(decodedpos > 0 && thisstr.key) // skip empty translation results
1980 thisstr.value = (char *)Mem_Alloc(pool, decodedpos + 1);
1981 memcpy(thisstr.value, decodedbuf, decodedpos + 1);
1982 hashindex = CRC_Block((const unsigned char *) thisstr.key, strlen(thisstr.key)) % PO_HASHSIZE;
1983 thisstr.nextonhashchain = po->hashtable[hashindex];
1984 po->hashtable[hashindex] = (po_string_t *)Mem_Alloc(pool, sizeof(thisstr));
1985 memcpy(po->hashtable[hashindex], &thisstr, sizeof(thisstr));
1986 memset(&thisstr, 0, sizeof(thisstr));
1990 Mem_Free((char *) buf);
1993 const char *PRVM_PO_Lookup(po_t *po, const char *str)
1995 int hashindex = CRC_Block((const unsigned char *) str, strlen(str)) % PO_HASHSIZE;
1996 po_string_t *p = po->hashtable[hashindex];
1999 if(!strcmp(str, p->key))
2001 p = p->nextonhashchain;
2005 void PRVM_PO_Destroy(po_t *po)
2008 for(i = 0; i < PO_HASHSIZE; ++i)
2010 po_string_t *p = po->hashtable[i];
2014 p = p->nextonhashchain;
2023 void PRVM_LeakTest(void);
2024 void PRVM_ResetProg(void)
2027 PRVM_GCALL(reset_cmd)();
2028 Mem_FreePool(&prog->progs_mempool);
2030 PRVM_PO_Destroy((po_t *) prog->po);
2031 memset(prog,0,sizeof(prvm_prog_t));
2032 prog->starttime = Sys_DoubleTime();
2040 void PRVM_LoadLNO( const char *progname ) {
2041 fs_offset_t filesize;
2043 unsigned int *header;
2046 FS_StripExtension( progname, filename, sizeof( filename ) );
2047 strlcat( filename, ".lno", sizeof( filename ) );
2049 lno = FS_LoadFile( filename, tempmempool, false, &filesize );
2055 <Spike> SafeWrite (h, &lnotype, sizeof(int));
2056 <Spike> SafeWrite (h, &version, sizeof(int));
2057 <Spike> SafeWrite (h, &numglobaldefs, sizeof(int));
2058 <Spike> SafeWrite (h, &numpr_globals, sizeof(int));
2059 <Spike> SafeWrite (h, &numfielddefs, sizeof(int));
2060 <Spike> SafeWrite (h, &numstatements, sizeof(int));
2061 <Spike> SafeWrite (h, statement_linenums, numstatements*sizeof(int));
2063 if ((unsigned int)filesize < (6 + prog->progs_numstatements) * sizeof(int))
2069 header = (unsigned int *) lno;
2070 if( header[ 0 ] == *(unsigned int *) "LNOF" &&
2071 LittleLong( header[ 1 ] ) == 1 &&
2072 (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs_numglobaldefs &&
2073 (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs_numglobals &&
2074 (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs_numfielddefs &&
2075 (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs_numstatements )
2077 prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof( int ) );
2078 memcpy( prog->statement_linenums, (int *) lno + 6, prog->progs_numstatements * sizeof( int ) );
2088 void PRVM_LoadProgs (const char * filename, int numrequiredfunc, const char **required_func, int numrequiredfields, prvm_required_field_t *required_field, int numrequiredglobals, prvm_required_field_t *required_global)
2091 dprograms_t *dprograms;
2092 dstatement_t *instatements;
2093 ddef_t *infielddefs;
2094 ddef_t *inglobaldefs;
2096 dfunction_t *infunctions;
2098 fs_offset_t filesize;
2099 int requiredglobalspace;
2106 PRVM_ERROR ("PRVM_LoadProgs: there is already a %s program loaded!", PRVM_NAME );
2108 dprograms = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
2109 if (dprograms == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
2110 PRVM_ERROR ("PRVM_LoadProgs: couldn't load %s for %s", filename, PRVM_NAME);
2111 // TODO bounds check header fields (e.g. numstatements), they must never go behind end of file
2113 Con_DPrintf("%s programs occupy %iK.\n", PRVM_NAME, (int)(filesize/1024));
2115 requiredglobalspace = 0;
2116 for (i = 0;i < numrequiredglobals;i++)
2117 requiredglobalspace += required_global[i].type == ev_vector ? 3 : 1;
2119 prog->filecrc = CRC_Block((unsigned char *)dprograms, filesize);
2121 // byte swap the header
2122 prog->progs_version = LittleLong(dprograms->version);
2123 prog->progs_crc = LittleLong(dprograms->crc);
2124 if (prog->progs_version != PROG_VERSION)
2125 PRVM_ERROR ("%s: %s has wrong version number (%i should be %i)", PRVM_NAME, filename, prog->progs_version, PROG_VERSION);
2126 if (prog->progs_crc != prog->headercrc && prog->progs_crc != prog->headercrc2)
2127 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);
2128 instatements = (dstatement_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_statements));
2129 prog->progs_numstatements = LittleLong(dprograms->numstatements);
2130 inglobaldefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globaldefs));
2131 prog->progs_numglobaldefs = LittleLong(dprograms->numglobaldefs);
2132 infielddefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_fielddefs));
2133 prog->progs_numfielddefs = LittleLong(dprograms->numfielddefs);
2134 infunctions = (dfunction_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_functions));
2135 prog->progs_numfunctions = LittleLong(dprograms->numfunctions);
2136 instrings = (char *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_strings));
2137 prog->progs_numstrings = LittleLong(dprograms->numstrings);
2138 inglobals = (float *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globals));
2139 prog->progs_numglobals = LittleLong(dprograms->numglobals);
2140 prog->progs_entityfields = LittleLong(dprograms->entityfields);
2142 prog->numstatements = prog->progs_numstatements;
2143 prog->numglobaldefs = prog->progs_numglobaldefs;
2144 prog->numfielddefs = prog->progs_numfielddefs;
2145 prog->numfunctions = prog->progs_numfunctions;
2146 prog->numstrings = prog->progs_numstrings;
2147 prog->numglobals = prog->progs_numglobals;
2148 prog->entityfields = prog->progs_entityfields;
2150 if (LittleLong(dprograms->ofs_strings) + prog->progs_numstrings >= (int)filesize)
2151 PRVM_ERROR ("%s: %s strings go past end of file", PRVM_NAME, filename);
2152 prog->strings = (char *)Mem_Alloc(prog->progs_mempool, prog->progs_numstrings);
2153 memcpy(prog->strings, (char *)dprograms + LittleLong(dprograms->ofs_strings), prog->progs_numstrings);
2154 prog->stringssize = prog->progs_numstrings;
2156 prog->numknownstrings = 0;
2157 prog->maxknownstrings = 0;
2158 prog->knownstrings = NULL;
2159 prog->knownstrings_freeable = NULL;
2161 Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
2163 // we need to expand the globaldefs and fielddefs to include engine defs
2164 prog->globaldefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobaldefs + numrequiredglobals) * sizeof(ddef_t));
2165 prog->globals.generic = (float *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobals + requiredglobalspace) * sizeof(float));
2166 prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numfielddefs + numrequiredfields) * sizeof(ddef_t));
2167 // we need to convert the statements to our memory format
2168 prog->statements = (mstatement_t *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(mstatement_t));
2169 // allocate space for profiling statement usage
2170 prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(*prog->statement_profile));
2171 // functions need to be converted to the memory format
2172 prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs_numfunctions);
2174 for (i = 0;i < prog->progs_numfunctions;i++)
2176 prog->functions[i].first_statement = LittleLong(infunctions[i].first_statement);
2177 prog->functions[i].parm_start = LittleLong(infunctions[i].parm_start);
2178 prog->functions[i].s_name = LittleLong(infunctions[i].s_name);
2179 prog->functions[i].s_file = LittleLong(infunctions[i].s_file);
2180 prog->functions[i].numparms = LittleLong(infunctions[i].numparms);
2181 prog->functions[i].locals = LittleLong(infunctions[i].locals);
2182 memcpy(prog->functions[i].parm_size, infunctions[i].parm_size, sizeof(infunctions[i].parm_size));
2183 if(prog->functions[i].first_statement >= prog->numstatements)
2184 PRVM_ERROR("PRVM_LoadProgs: out of bounds function statement (function %d) in %s", i, PRVM_NAME);
2185 // TODO bounds check parm_start, s_name, s_file, numparms, locals, parm_size
2188 // copy the globaldefs to the new globaldefs list
2189 for (i=0 ; i<prog->numglobaldefs ; i++)
2191 prog->globaldefs[i].type = LittleShort(inglobaldefs[i].type);
2192 prog->globaldefs[i].ofs = LittleShort(inglobaldefs[i].ofs);
2193 prog->globaldefs[i].s_name = LittleLong(inglobaldefs[i].s_name);
2194 // TODO bounds check ofs, s_name
2197 // append the required globals
2198 for (i = 0;i < numrequiredglobals;i++)
2200 prog->globaldefs[prog->numglobaldefs].type = required_global[i].type;
2201 prog->globaldefs[prog->numglobaldefs].ofs = prog->numglobals;
2202 prog->globaldefs[prog->numglobaldefs].s_name = PRVM_SetEngineString(required_global[i].name);
2203 if (prog->globaldefs[prog->numglobaldefs].type == ev_vector)
2204 prog->numglobals += 3;
2207 prog->numglobaldefs++;
2210 // copy the progs fields to the new fields list
2211 for (i = 0;i < prog->numfielddefs;i++)
2213 prog->fielddefs[i].type = LittleShort(infielddefs[i].type);
2214 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
2215 PRVM_ERROR ("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", PRVM_NAME);
2216 prog->fielddefs[i].ofs = LittleShort(infielddefs[i].ofs);
2217 prog->fielddefs[i].s_name = LittleLong(infielddefs[i].s_name);
2218 // TODO bounds check ofs, s_name
2221 // append the required fields
2222 for (i = 0;i < numrequiredfields;i++)
2224 prog->fielddefs[prog->numfielddefs].type = required_field[i].type;
2225 prog->fielddefs[prog->numfielddefs].ofs = prog->entityfields;
2226 prog->fielddefs[prog->numfielddefs].s_name = PRVM_SetEngineString(required_field[i].name);
2227 if (prog->fielddefs[prog->numfielddefs].type == ev_vector)
2228 prog->entityfields += 3;
2230 prog->entityfields++;
2231 prog->numfielddefs++;
2234 // LordHavoc: TODO: reorder globals to match engine struct
2235 // LordHavoc: TODO: reorder fields to match engine struct
2236 #define remapglobal(index) (index)
2237 #define remapfield(index) (index)
2240 for (i = 0;i < prog->progs_numglobals;i++)
2241 ((int *)prog->globals.generic)[remapglobal(i)] = LittleLong(((int *)inglobals)[i]);
2243 // LordHavoc: TODO: support 32bit progs statement formats
2244 // copy, remap globals in statements, bounds check
2245 for (i = 0;i < prog->progs_numstatements;i++)
2247 op = (opcode_t)LittleShort(instatements[i].op);
2248 a = (unsigned short)LittleShort(instatements[i].a);
2249 b = (unsigned short)LittleShort(instatements[i].b);
2250 c = (unsigned short)LittleShort(instatements[i].c);
2256 if (a >= prog->progs_numglobals || b + i < 0 || b + i >= prog->progs_numstatements)
2257 PRVM_ERROR("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", i, PRVM_NAME);
2258 prog->statements[i].op = op;
2259 prog->statements[i].operand[0] = remapglobal(a);
2260 prog->statements[i].operand[1] = -1;
2261 prog->statements[i].operand[2] = -1;
2262 prog->statements[i].jumpabsolute = i + b;
2266 if (a + i < 0 || a + i >= prog->progs_numstatements)
2267 PRVM_ERROR("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, PRVM_NAME);
2268 prog->statements[i].op = op;
2269 prog->statements[i].operand[0] = -1;
2270 prog->statements[i].operand[1] = -1;
2271 prog->statements[i].operand[2] = -1;
2272 prog->statements[i].jumpabsolute = i + a;
2275 Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", (int)op, i, PRVM_NAME);
2276 // global global global
2311 if (a >= prog->progs_numglobals || b >= prog->progs_numglobals || c >= prog->progs_numglobals)
2312 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
2313 prog->statements[i].op = op;
2314 prog->statements[i].operand[0] = remapglobal(a);
2315 prog->statements[i].operand[1] = remapglobal(b);
2316 prog->statements[i].operand[2] = remapglobal(c);
2317 prog->statements[i].jumpabsolute = -1;
2319 // global none global
2325 if (a >= prog->progs_numglobals || c >= prog->progs_numglobals)
2326 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
2327 prog->statements[i].op = op;
2328 prog->statements[i].operand[0] = remapglobal(a);
2329 prog->statements[i].operand[1] = -1;
2330 prog->statements[i].operand[2] = remapglobal(c);
2331 prog->statements[i].jumpabsolute = -1;
2347 if (a >= prog->progs_numglobals || b >= prog->progs_numglobals)
2348 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
2349 prog->statements[i].op = op;
2350 prog->statements[i].operand[0] = remapglobal(a);
2351 prog->statements[i].operand[1] = remapglobal(b);
2352 prog->statements[i].operand[2] = -1;
2353 prog->statements[i].jumpabsolute = -1;
2367 if ( a >= prog->progs_numglobals)
2368 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
2369 prog->statements[i].op = op;
2370 prog->statements[i].operand[0] = remapglobal(a);
2371 prog->statements[i].operand[1] = -1;
2372 prog->statements[i].operand[2] = -1;
2373 prog->statements[i].jumpabsolute = -1;
2377 if(prog->numstatements < 1)
2379 PRVM_ERROR("PRVM_LoadProgs: empty program in %s", PRVM_NAME);
2381 else switch(prog->statements[prog->numstatements - 1].op)
2388 PRVM_ERROR("PRVM_LoadProgs: program may fall off the edge (does not end with RETURN, GOTO or DONE) in %s", PRVM_NAME);
2392 // we're done with the file now
2393 Mem_Free(dprograms);
2396 // check required functions
2397 for(i=0 ; i < numrequiredfunc ; i++)
2398 if(PRVM_ED_FindFunction(required_func[i]) == 0)
2399 PRVM_ERROR("%s: %s not found in %s",PRVM_NAME, required_func[i], filename);
2401 PRVM_LoadLNO(filename);
2405 if(*prvm_language.string)
2406 // in CSQC we really shouldn't be able to change how stuff works... sorry for now
2407 // later idea: include a list of authorized .po file checksums with the csprogs
2409 qboolean deftrans = !!strcmp(PRVM_NAME, "client");
2410 const char *realfilename = (strcmp(PRVM_NAME, "client") ? filename : csqc_progname.string);
2411 if(deftrans) // once we have dotranslate_ strings, ALWAYS use the opt-in method!
2413 for (i=0 ; i<prog->numglobaldefs ; i++)
2416 name = PRVM_GetString(prog->globaldefs[i].s_name);
2417 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2418 if(name && !strncmp(name, "dotranslate_", 12))
2425 if(!strcmp(prvm_language.string, "dump"))
2427 qfile_t *f = FS_OpenRealFile(va("%s.pot", realfilename), "w", false);
2428 Con_Printf("Dumping to %s.pot\n", realfilename);
2431 for (i=0 ; i<prog->numglobaldefs ; i++)
2434 name = PRVM_GetString(prog->globaldefs[i].s_name);
2435 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2436 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2438 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2439 const char *value = PRVM_GetString(val->string);
2442 char buf[MAX_INPUTLINE];
2443 PRVM_PO_UnparseString(buf, value, sizeof(buf));
2444 FS_Printf(f, "msgid \"%s\"\nmsgstr \"\"\n\n", buf);
2453 po_t *po = PRVM_PO_Load(va("%s.%s.po", realfilename, prvm_language.string), prog->progs_mempool);
2456 for (i=0 ; i<prog->numglobaldefs ; i++)
2459 name = PRVM_GetString(prog->globaldefs[i].s_name);
2460 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2461 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2463 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2464 const char *value = PRVM_GetString(val->string);
2467 value = PRVM_PO_Lookup(po, value);
2469 val->string = PRVM_SetEngineString(value);
2477 for (i=0 ; i<prog->numglobaldefs ; i++)
2480 name = PRVM_GetString(prog->globaldefs[i].s_name);
2481 //Con_Printf("found var %s\n", name);
2483 && !strncmp(name, "autocvar_", 9)
2484 && !(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
2487 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2488 cvar_t *cvar = Cvar_FindVar(name + 9);
2489 //Con_Printf("PRVM_LoadProgs: autocvar global %s in %s, processing...\n", name, PRVM_NAME);
2494 Con_DPrintf("PRVM_LoadProgs: no cvar for autocvar global %s in %s, creating...\n", name, PRVM_NAME);
2495 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2498 if((float)((int)(val->_float)) == val->_float)
2499 dpsnprintf(buf, sizeof(buf), "%i", (int)(val->_float));
2501 dpsnprintf(buf, sizeof(buf), "%.9g", val->_float);
2505 dpsnprintf(buf, sizeof(buf), "%.9g %.9g %.9g", val->vector[0], val->vector[1], val->vector[2]); value = buf;
2508 value = PRVM_GetString(val->string);
2511 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, PRVM_NAME);
2514 cvar = Cvar_Get(name + 9, value, 0, NULL);
2515 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2517 val->string = PRVM_SetEngineString(cvar->string);
2518 cvar->globaldefindex_stringno[prog - prog_list] = val->string;
2521 PRVM_ERROR("PRVM_LoadProgs: could not create cvar for autocvar global %s in %s", name, PRVM_NAME);
2522 cvar->globaldefindex_progid[prog - prog_list] = prog->id;
2523 cvar->globaldefindex[prog - prog_list] = i;
2525 else if((cvar->flags & CVAR_PRIVATE) == 0)
2527 // MUST BE SYNCED WITH cvar.c Cvar_Set
2530 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2533 val->_float = cvar->value;
2537 VectorClear(val->vector);
2538 for (j = 0;j < 3;j++)
2540 while (*s && ISWHITESPACE(*s))
2544 val->vector[j] = atof(s);
2545 while (!ISWHITESPACE(*s))
2552 val->string = PRVM_SetEngineString(cvar->string);
2553 cvar->globaldefindex_stringno[prog - prog_list] = val->string;
2556 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, PRVM_NAME);
2559 cvar->globaldefindex_progid[prog - prog_list] = prog->id;
2560 cvar->globaldefindex[prog - prog_list] = i;
2563 Con_Printf("PRVM_LoadProgs: private cvar for autocvar global %s in %s\n", name, PRVM_NAME);
2569 prog->loaded = TRUE;
2571 // set flags & ddef_ts in prog
2577 PRVM_GCALL(init_cmd)();
2584 void PRVM_Fields_f (void)
2586 int i, j, ednum, used, usedamount;
2588 char tempstring[MAX_INPUTLINE], tempstring2[260];
2598 Con_Print("no progs loaded\n");
2605 Con_Print("prvm_fields <program name>\n");
2610 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
2613 counts = (int *)Mem_Alloc(tempmempool, prog->numfielddefs * sizeof(int));
2614 for (ednum = 0;ednum < prog->max_edicts;ednum++)
2616 ed = PRVM_EDICT_NUM(ednum);
2617 if (ed->priv.required->free)
2619 for (i = 1;i < prog->numfielddefs;i++)
2621 d = &prog->fielddefs[i];
2622 name = PRVM_GetString(d->s_name);
2623 if (name[strlen(name)-2] == '_')
2624 continue; // skip _x, _y, _z vars
2625 v = (int *)(ed->fields.vp + d->ofs);
2626 // if the value is still all 0, skip the field
2627 for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
2640 for (i = 0;i < prog->numfielddefs;i++)
2642 d = &prog->fielddefs[i];
2643 name = PRVM_GetString(d->s_name);
2644 if (name[strlen(name)-2] == '_')
2645 continue; // skip _x, _y, _z vars
2646 switch(d->type & ~DEF_SAVEGLOBAL)
2649 strlcat(tempstring, "string ", sizeof(tempstring));
2652 strlcat(tempstring, "entity ", sizeof(tempstring));
2655 strlcat(tempstring, "function ", sizeof(tempstring));
2658 strlcat(tempstring, "field ", sizeof(tempstring));
2661 strlcat(tempstring, "void ", sizeof(tempstring));
2664 strlcat(tempstring, "float ", sizeof(tempstring));
2667 strlcat(tempstring, "vector ", sizeof(tempstring));
2670 strlcat(tempstring, "pointer ", sizeof(tempstring));
2673 dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
2674 strlcat(tempstring, tempstring2, sizeof(tempstring));
2677 if (strlen(name) > sizeof(tempstring2)-4)
2679 memcpy (tempstring2, name, sizeof(tempstring2)-4);
2680 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
2681 tempstring2[sizeof(tempstring2)-1] = 0;
2684 strlcat(tempstring, name, sizeof(tempstring));
2685 for (j = (int)strlen(name);j < 25;j++)
2686 strlcat(tempstring, " ", sizeof(tempstring));
2687 dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
2688 strlcat(tempstring, tempstring2, sizeof(tempstring));
2689 strlcat(tempstring, "\n", sizeof(tempstring));
2690 if (strlen(tempstring) >= sizeof(tempstring)/2)
2692 Con_Print(tempstring);
2698 usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
2702 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->entityfields, used, prog->entityfields * 4, usedamount * 4, prog->max_edicts, prog->entityfields * 4 * prog->max_edicts, usedamount * 4 * prog->max_edicts);
2707 void PRVM_Globals_f (void)
2710 const char *wildcard;
2716 Con_Print("no progs loaded\n");
2719 if(Cmd_Argc () < 2 || Cmd_Argc() > 3)
2721 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
2726 if(!PRVM_SetProgFromString (Cmd_Argv (1)))
2729 if( Cmd_Argc() == 3)
2730 wildcard = Cmd_Argv(2);
2734 Con_Printf("%s :", PRVM_NAME);
2736 for (i = 0;i < prog->numglobaldefs;i++)
2739 if( !matchpattern( PRVM_GetString(prog->globaldefs[i].s_name), wildcard, 1) )
2744 Con_Printf("%s\n", PRVM_GetString(prog->globaldefs[i].s_name));
2746 Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->numglobals, numculled, prog->numglobals * 4);
2756 void PRVM_Global_f(void)
2759 if( Cmd_Argc() != 3 ) {
2760 Con_Printf( "prvm_global <program name> <global name>\n" );
2765 if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
2768 global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
2770 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2772 Con_Printf( "%s: %s\n", Cmd_Argv(2), PRVM_ValueString( (etype_t)global->type, PRVM_GLOBALFIELDVALUE(global->ofs) ) );
2781 void PRVM_GlobalSet_f(void)
2784 if( Cmd_Argc() != 4 ) {
2785 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2790 if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
2793 global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
2795 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2797 PRVM_ED_ParseEpair( NULL, global, Cmd_Argv(3), true );
2806 void PRVM_Init (void)
2808 Cmd_AddCommand ("prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2809 Cmd_AddCommand ("prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2810 Cmd_AddCommand ("prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2811 Cmd_AddCommand ("prvm_profile", PRVM_Profile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu)");
2812 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");
2813 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)");
2814 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)");
2815 Cmd_AddCommand ("prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2816 Cmd_AddCommand ("prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2817 Cmd_AddCommand ("prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2818 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)");
2819 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");
2820 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");
2821 Cmd_AddCommand ("prvm_printfunction", PRVM_PrintFunction_f, "prints a disassembly (QuakeC instructions) of the specified function in the selected VM (server, client, menu)");
2822 Cmd_AddCommand ("cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2823 Cmd_AddCommand ("menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2824 Cmd_AddCommand ("sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2826 Cvar_RegisterVariable (&prvm_language);
2827 Cvar_RegisterVariable (&prvm_traceqc);
2828 Cvar_RegisterVariable (&prvm_statementprofiling);
2829 Cvar_RegisterVariable (&prvm_timeprofiling);
2830 Cvar_RegisterVariable (&prvm_backtraceforwarnings);
2831 Cvar_RegisterVariable (&prvm_leaktest);
2832 Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames);
2833 Cvar_RegisterVariable (&prvm_errordump);
2834 Cvar_RegisterVariable (&prvm_reuseedicts_startuptime);
2835 Cvar_RegisterVariable (&prvm_reuseedicts_neverinsameframe);
2837 // COMMANDLINEOPTION: PRVM: -norunaway disables the runaway loop check (it might be impossible to exit DarkPlaces if used!)
2838 prvm_runawaycheck = !COM_CheckParm("-norunaway");
2848 void PRVM_InitProg(int prognr)
2850 static unsigned int progid = 0;
2852 if(prognr < 0 || prognr >= PRVM_MAXPROGS)
2853 Sys_Error("PRVM_InitProg: Invalid program number %i",prognr);
2855 prog = &prog_list[prognr];
2860 memset(prog, 0, sizeof(prvm_prog_t));
2861 prog->starttime = Sys_DoubleTime();
2862 prog->id = ++progid;
2864 prog->error_cmd = Host_Error;
2865 prog->leaktest_active = prvm_leaktest.integer != 0;
2868 int PRVM_GetProgNr(void)
2870 return prog - prog_list;
2873 void *_PRVM_Alloc(size_t buffersize, const char *filename, int fileline)
2875 return _Mem_Alloc(prog->progs_mempool, NULL, buffersize, 16, filename, fileline);
2878 void _PRVM_Free(void *buffer, const char *filename, int fileline)
2880 _Mem_Free(buffer, filename, fileline);
2883 void _PRVM_FreeAll(const char *filename, int fileline)
2885 prog->functions = NULL;
2886 prog->strings = NULL;
2887 prog->fielddefs = NULL;
2888 prog->globaldefs = NULL;
2889 prog->statements = NULL;
2890 // FIXME: what about knownstrings?
2891 _Mem_EmptyPool(prog->progs_mempool, filename, fileline);
2894 // LordHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
2895 unsigned int PRVM_EDICT_NUM_ERROR(unsigned int n, const char *filename, int fileline)
2897 PRVM_ERROR ("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", PRVM_NAME, n, filename, fileline);
2901 sizebuf_t vm_tempstringsbuf;
2902 #define PRVM_KNOWNSTRINGBASE 0x40000000
2904 const char *PRVM_GetString(int num)
2909 VM_Warning("PRVM_GetString: Invalid string offset (%i < 0)\n", num);
2912 else if (num < prog->stringssize)
2914 // constant string from progs.dat
2915 return prog->strings + num;
2917 else if (num <= prog->stringssize + vm_tempstringsbuf.maxsize)
2919 // tempstring returned by engine to QC (becomes invalid after returning to engine)
2920 num -= prog->stringssize;
2921 if (num < vm_tempstringsbuf.cursize)
2922 return (char *)vm_tempstringsbuf.data + num;
2925 VM_Warning("PRVM_GetString: Invalid temp-string offset (%i >= %i vm_tempstringsbuf.cursize)\n", num, vm_tempstringsbuf.cursize);
2929 else if (num & PRVM_KNOWNSTRINGBASE)
2932 num = num - PRVM_KNOWNSTRINGBASE;
2933 if (num >= 0 && num < prog->numknownstrings)
2935 if (!prog->knownstrings[num])
2937 VM_Warning("PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
2940 return prog->knownstrings[num];
2944 VM_Warning("PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
2950 // invalid string offset
2951 VM_Warning("PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
2956 const char *PRVM_ChangeEngineString(int i, const char *s)
2959 i = i - PRVM_KNOWNSTRINGBASE;
2960 if(i < 0 || i >= prog->numknownstrings)
2961 PRVM_ERROR("PRVM_ChangeEngineString: s is not an engine string");
2962 old = prog->knownstrings[i];
2963 prog->knownstrings[i] = s;
2967 int PRVM_SetEngineString(const char *s)
2972 if (s >= prog->strings && s <= prog->strings + prog->stringssize)
2973 PRVM_ERROR("PRVM_SetEngineString: s in prog->strings area");
2974 // if it's in the tempstrings area, use a reserved range
2975 // (otherwise we'd get millions of useless string offsets cluttering the database)
2976 if (s >= (char *)vm_tempstringsbuf.data && s < (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.maxsize)
2978 return prog->stringssize + (s - (char *)vm_tempstringsbuf.data);
2980 // see if it's a known string address
2981 for (i = 0;i < prog->numknownstrings;i++)
2982 if (prog->knownstrings[i] == s)
2983 return PRVM_KNOWNSTRINGBASE + i;
2984 // new unknown engine string
2985 if (developer_insane.integer)
2986 Con_DPrintf("new engine string %p = \"%s\"\n", s, s);
2987 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2988 if (!prog->knownstrings[i])
2990 if (i >= prog->numknownstrings)
2992 if (i >= prog->maxknownstrings)
2994 const char **oldstrings = prog->knownstrings;
2995 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2996 const char **oldstrings_origin = prog->knownstrings_origin;
2997 prog->maxknownstrings += 128;
2998 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2999 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
3000 if(prog->leaktest_active)
3001 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3002 if (prog->numknownstrings)
3004 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
3005 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
3006 if(prog->leaktest_active)
3007 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
3010 prog->numknownstrings++;
3012 prog->firstfreeknownstring = i + 1;
3013 prog->knownstrings[i] = s;
3014 prog->knownstrings_freeable[i] = false;
3015 if(prog->leaktest_active)
3016 prog->knownstrings_origin[i] = NULL;
3017 return PRVM_KNOWNSTRINGBASE + i;
3020 // temp string handling
3022 // all tempstrings go into this buffer consecutively, and it is reset
3023 // whenever PRVM_ExecuteProgram returns to the engine
3024 // (technically each PRVM_ExecuteProgram call saves the cursize value and
3025 // restores it on return, so multiple recursive calls can share the same
3027 // the buffer size is automatically grown as needed
3029 int PRVM_SetTempString(const char *s)
3035 size = (int)strlen(s) + 1;
3036 if (developer_insane.integer)
3037 Con_DPrintf("PRVM_SetTempString: cursize %i, size %i\n", vm_tempstringsbuf.cursize, size);
3038 if (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
3040 sizebuf_t old = vm_tempstringsbuf;
3041 if (vm_tempstringsbuf.cursize + size >= 1<<28)
3042 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);
3043 vm_tempstringsbuf.maxsize = max(vm_tempstringsbuf.maxsize, 65536);
3044 while (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
3045 vm_tempstringsbuf.maxsize *= 2;
3046 if (vm_tempstringsbuf.maxsize != old.maxsize || vm_tempstringsbuf.data == NULL)
3048 Con_DPrintf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, vm_tempstringsbuf.maxsize/1024);
3049 vm_tempstringsbuf.data = (unsigned char *) Mem_Alloc(sv_mempool, vm_tempstringsbuf.maxsize);
3051 memcpy(vm_tempstringsbuf.data, old.data, old.cursize);
3056 t = (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.cursize;
3058 vm_tempstringsbuf.cursize += size;
3059 return PRVM_SetEngineString(t);
3062 int PRVM_AllocString(size_t bufferlength, char **pointer)
3067 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3068 if (!prog->knownstrings[i])
3070 if (i >= prog->numknownstrings)
3072 if (i >= prog->maxknownstrings)
3074 const char **oldstrings = prog->knownstrings;
3075 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
3076 const char **oldstrings_origin = prog->knownstrings_origin;
3077 prog->maxknownstrings += 128;
3078 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3079 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
3080 if(prog->leaktest_active)
3081 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3082 if (prog->numknownstrings)
3084 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
3085 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
3086 if(prog->leaktest_active)
3087 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
3090 Mem_Free((char **)oldstrings);
3091 if (oldstrings_freeable)
3092 Mem_Free((unsigned char *)oldstrings_freeable);
3093 if (oldstrings_origin)
3094 Mem_Free((char **)oldstrings_origin);
3096 prog->numknownstrings++;
3098 prog->firstfreeknownstring = i + 1;
3099 prog->knownstrings[i] = (char *)PRVM_Alloc(bufferlength);
3100 prog->knownstrings_freeable[i] = true;
3101 if(prog->leaktest_active)
3102 prog->knownstrings_origin[i] = PRVM_AllocationOrigin();
3104 *pointer = (char *)(prog->knownstrings[i]);
3105 return PRVM_KNOWNSTRINGBASE + i;
3108 void PRVM_FreeString(int num)
3111 PRVM_ERROR("PRVM_FreeString: attempt to free a NULL string");
3112 else if (num >= 0 && num < prog->stringssize)
3113 PRVM_ERROR("PRVM_FreeString: attempt to free a constant string");
3114 else if (num >= PRVM_KNOWNSTRINGBASE && num < PRVM_KNOWNSTRINGBASE + prog->numknownstrings)
3116 num = num - PRVM_KNOWNSTRINGBASE;
3117 if (!prog->knownstrings[num])
3118 PRVM_ERROR("PRVM_FreeString: attempt to free a non-existent or already freed string");
3119 if (!prog->knownstrings_freeable[num])
3120 PRVM_ERROR("PRVM_FreeString: attempt to free a string owned by the engine");
3121 PRVM_Free((char *)prog->knownstrings[num]);
3122 if(prog->leaktest_active)
3123 if(prog->knownstrings_origin[num])
3124 PRVM_Free((char *)prog->knownstrings_origin[num]);
3125 prog->knownstrings[num] = NULL;
3126 prog->knownstrings_freeable[num] = false;
3127 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
3130 PRVM_ERROR("PRVM_FreeString: invalid string offset %i", num);
3133 static qboolean PRVM_IsStringReferenced(string_t string)
3137 for (i = 0;i < prog->numglobaldefs;i++)
3139 ddef_t *d = &prog->globaldefs[i];
3140 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3142 if(string == PRVM_GLOBALFIELDSTRING(d->ofs))
3146 for(j = 0; j < prog->num_edicts; ++j)
3148 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3149 if (ed->priv.required->free)
3151 for (i=0; i<prog->numfielddefs; ++i)
3153 ddef_t *d = &prog->fielddefs[i];
3154 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3156 if(string == PRVM_EDICTFIELDSTRING(ed, d->ofs))
3164 static qboolean PRVM_IsEdictRelevant(prvm_edict_t *edict)
3166 if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
3167 return true; // world or clients
3168 switch(prog - prog_list)
3170 case PRVM_SERVERPROG:
3172 entvars_t *ev = edict->fields.server;
3173 if(ev->solid) // can block other stuff, or is a trigger?
3175 if(ev->modelindex) // visible ent?
3177 if(ev->effects) // particle effect?
3179 if(ev->think) // has a think function?
3180 if(ev->nextthink > 0) // that actually will eventually run?
3184 if(*prvm_leaktest_ignore_classnames.string)
3186 if(strstr(va(" %s ", prvm_leaktest_ignore_classnames.string), va(" %s ", PRVM_GetString(ev->classname))))
3191 case PRVM_CLIENTPROG:
3193 // TODO someone add more stuff here
3194 cl_entvars_t *ev = edict->fields.client;
3195 if(ev->entnum) // csqc networked
3197 if(ev->modelindex) // visible ent?
3199 if(ev->effects) // particle effect?
3201 if(ev->think) // has a think function?
3202 if(ev->nextthink > 0) // that actually will eventually run?
3204 if(*prvm_leaktest_ignore_classnames.string)
3206 if(strstr(va(" %s ", prvm_leaktest_ignore_classnames.string), va(" %s ", PRVM_GetString(ev->classname))))
3212 // menu prog does not have classnames
3218 static qboolean PRVM_IsEdictReferenced(prvm_edict_t *edict, int mark)
3221 int edictnum = PRVM_NUM_FOR_EDICT(edict);
3222 const char *targetname = NULL;
3224 switch(prog - prog_list)
3226 case PRVM_SERVERPROG:
3227 targetname = PRVM_GetString(edict->fields.server->targetname);
3232 if(!*targetname) // ""
3235 for (i = 0;i < prog->numglobaldefs;i++)
3237 ddef_t *d = &prog->globaldefs[i];
3238 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3240 if(edictnum == PRVM_GLOBALFIELDEDICT(d->ofs))
3244 for(j = 0; j < prog->num_edicts; ++j)
3246 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3247 if (ed->priv.required->mark < mark)
3253 const char *target = PRVM_GetString(ed->fields.server->target);
3255 if(!strcmp(target, targetname))
3258 for (i=0; i<prog->numfielddefs; ++i)
3260 ddef_t *d = &prog->fielddefs[i];
3261 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3263 if(edictnum == PRVM_EDICTFIELDEDICT(ed, d->ofs))
3271 static void PRVM_MarkReferencedEdicts(void)
3277 for(j = 0; j < prog->num_edicts; ++j)
3279 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3280 if(ed->priv.required->free)
3282 ed->priv.required->mark = PRVM_IsEdictRelevant(ed) ? 1 : 0;
3289 for(j = 0; j < prog->num_edicts; ++j)
3291 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3292 if(ed->priv.required->free)
3294 if(ed->priv.required->mark)
3296 if(PRVM_IsEdictReferenced(ed, stage))
3298 ed->priv.required->mark = stage + 1;
3305 Con_DPrintf("leak check used %d stages to find all references\n", stage);
3308 void PRVM_LeakTest(void)
3311 qboolean leaked = false;
3313 if(!prog->leaktest_active)
3317 for (i = 0; i < prog->numknownstrings; ++i)
3319 if(prog->knownstrings[i])
3320 if(prog->knownstrings_freeable[i])
3321 if(prog->knownstrings_origin[i])
3322 if(!PRVM_IsStringReferenced(PRVM_KNOWNSTRINGBASE + i))
3324 Con_Printf("Unreferenced string found!\n Value: %s\n Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
3330 PRVM_MarkReferencedEdicts();
3331 for(j = 0; j < prog->num_edicts; ++j)
3333 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3334 if(ed->priv.required->free)
3336 if(!ed->priv.required->mark)
3337 if(ed->priv.required->allocation_origin)
3339 Con_Printf("Unreferenced edict found!\n Allocated at: %s\n", ed->priv.required->allocation_origin);
3340 PRVM_ED_Print(ed, NULL);
3346 for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
3348 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
3350 if(stringbuffer->origin)
3352 Con_Printf("Open string buffer handle found!\n Allocated at: %s\n", stringbuffer->origin);
3357 for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
3359 if(prog->openfiles[i])
3360 if(prog->openfiles_origin[i])
3362 Con_Printf("Open file handle found!\n Allocated at: %s\n", prog->openfiles_origin[i]);
3367 for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
3369 if(prog->opensearches[i])
3370 if(prog->opensearches_origin[i])
3372 Con_Printf("Open search handle found!\n Allocated at: %s\n", prog->opensearches_origin[i]);
3378 Con_Printf("Congratulations. No leaks found.\n");