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 prvm_eval_t prvm_badvalue; // used only for error returns
34 ddef_t *PRVM_ED_FieldAtOfs(int ofs);
35 qboolean PRVM_ED_ParseEpair(prvm_edict_t *ent, ddef_t *key, const char *s, qboolean parsebackslash);
37 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"};
38 // LordHavoc: prints every opcode as it executes - warning: this is significant spew
39 cvar_t prvm_traceqc = {0, "prvm_traceqc", "0", "prints every QuakeC statement as it is executed (only for really thorough debugging!)"};
40 // LordHavoc: counts usage of each QuakeC statement
41 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)"};
42 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)"};
43 cvar_t prvm_backtraceforwarnings = {0, "prvm_backtraceforwarnings", "0", "print a backtrace for warnings too"};
44 cvar_t prvm_leaktest = {0, "prvm_leaktest", "0", "try to detect memory leaks in strings or entities"};
45 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)"};
46 cvar_t prvm_errordump = {0, "prvm_errordump", "0", "write a savegame on crash to crash-server.dmp"};
47 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)"};
48 cvar_t prvm_reuseedicts_neverinsameframe = {0, "prvm_reuseedicts_neverinsameframe", "1", "never allows re-use of freed entity slots during same frame"};
50 static double prvm_reuseedicts_always_allow = 0;
51 qboolean prvm_runawaycheck = true;
53 extern sizebuf_t vm_tempstringsbuf;
55 //============================================================================
63 void PRVM_MEM_Alloc(void)
67 // reserve space for the null entity aka world
68 // check bound of max_edicts
69 prog->max_edicts = bound(1 + prog->reserved_edicts, prog->max_edicts, prog->limit_edicts);
70 prog->num_edicts = bound(1 + prog->reserved_edicts, prog->num_edicts, prog->max_edicts);
72 // edictprivate_size has to be min as big prvm_edict_private_t
73 prog->edictprivate_size = max(prog->edictprivate_size,(int)sizeof(prvm_edict_private_t));
76 prog->edicts = (prvm_edict_t *)Mem_Alloc(prog->progs_mempool,prog->limit_edicts * sizeof(prvm_edict_t));
78 // alloc edict private space
79 prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size);
82 prog->entityfieldsarea = prog->entityfields * prog->max_edicts;
83 prog->edictsfields = (vec_t *)Mem_Alloc(prog->progs_mempool, prog->entityfieldsarea * sizeof(vec_t));
86 for(i = 0; i < prog->max_edicts; i++)
88 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
89 prog->edicts[i].fields.vp = prog->edictsfields + i * prog->entityfields;
95 PRVM_MEM_IncreaseEdicts
98 void PRVM_MEM_IncreaseEdicts(void)
102 if(prog->max_edicts >= prog->limit_edicts)
105 PRVM_GCALL(begin_increase_edicts)();
108 prog->max_edicts = min(prog->max_edicts + 256, prog->limit_edicts);
110 prog->entityfieldsarea = prog->entityfields * prog->max_edicts;
111 prog->edictsfields = (vec_t*)Mem_Realloc(prog->progs_mempool, (void *)prog->edictsfields, prog->entityfieldsarea * sizeof(vec_t));
112 prog->edictprivate = (void *)Mem_Realloc(prog->progs_mempool, (void *)prog->edictprivate, prog->max_edicts * prog->edictprivate_size);
114 //set e and v pointers
115 for(i = 0; i < prog->max_edicts; i++)
117 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
118 prog->edicts[i].fields.vp = prog->edictsfields + i * prog->entityfields;
121 PRVM_GCALL(end_increase_edicts)();
124 //============================================================================
127 int PRVM_ED_FindFieldOffset(const char *field)
130 d = PRVM_ED_FindField(field);
136 int PRVM_ED_FindGlobalOffset(const char *global)
139 d = PRVM_ED_FindGlobal(global);
145 func_t PRVM_ED_FindFunctionOffset(const char *function)
148 f = PRVM_ED_FindFunction(function);
151 return (func_t)(f - prog->functions);
154 qboolean PRVM_ProgLoaded(int prognr)
156 if(prognr < 0 || prognr >= PRVM_MAXPROGS)
159 return (prog_list[prognr].loaded ? TRUE : FALSE);
164 PRVM_SetProgFromString
167 // perhaps add a return value when the str doesnt exist
168 qboolean PRVM_SetProgFromString(const char *str)
171 for(; i < PRVM_MAXPROGS ; i++)
172 if(prog_list[i].name && !strcmp(prog_list[i].name,str))
174 if(prog_list[i].loaded)
176 prog = &prog_list[i];
181 Con_Printf("%s not loaded !\n",PRVM_NAME);
186 Con_Printf("Invalid program name %s !\n", str);
195 void PRVM_SetProg(int prognr)
197 if(0 <= prognr && prognr < PRVM_MAXPROGS)
199 if(prog_list[prognr].loaded)
200 prog = &prog_list[prognr];
202 PRVM_ERROR("%i not loaded !", prognr);
205 PRVM_ERROR("Invalid program number %i", prognr);
212 Sets everything to NULL
215 void PRVM_ED_ClearEdict (prvm_edict_t *e)
217 memset (e->fields.vp, 0, prog->entityfields * 4);
218 e->priv.required->free = false;
220 // AK: Let the init_edict function determine if something needs to be initialized
221 PRVM_GCALL(init_edict)(e);
224 const char *PRVM_AllocationOrigin(void)
227 if(prog->leaktest_active)
228 if(prog->depth > 0) // actually in QC code and not just parsing the entities block of a map/savegame
230 buf = (char *)PRVM_Alloc(128);
231 PRVM_ShortStackTrace(buf, 128);
240 Returns if this particular edict could get allocated by PRVM_ED_Alloc
243 qboolean PRVM_ED_CanAlloc(prvm_edict_t *e)
245 if(!e->priv.required->free)
247 if(prvm_reuseedicts_always_allow == realtime)
249 if(realtime <= e->priv.required->freetime && prvm_reuseedicts_neverinsameframe.integer)
250 return false; // never allow reuse in same frame (causes networking trouble)
251 if(e->priv.required->freetime < prog->starttime + prvm_reuseedicts_startuptime.value)
253 if(realtime > e->priv.required->freetime + 1)
255 return false; // entity slot still blocked because the entity was freed less than one second ago
262 Either finds a free edict, or allocates a new one.
263 Try to avoid reusing an entity that was recently freed, because it
264 can cause the client to think the entity morphed into something else
265 instead of being removed and recreated, which can cause interpolated
266 angles and bad trails.
269 prvm_edict_t *PRVM_ED_Alloc (void)
274 // the client qc dont need maxclients
275 // thus it doesnt need to use svs.maxclients
276 // AK: changed i=svs.maxclients+1
277 // AK: changed so the edict 0 wont spawn -> used as reserved/world entity
278 // although the menu/client has no world
279 for (i = prog->reserved_edicts + 1;i < prog->num_edicts;i++)
281 e = PRVM_EDICT_NUM(i);
282 if(PRVM_ED_CanAlloc(e))
284 PRVM_ED_ClearEdict (e);
285 e->priv.required->allocation_origin = PRVM_AllocationOrigin();
290 if (i == prog->limit_edicts)
291 PRVM_ERROR ("%s: PRVM_ED_Alloc: no free edicts",PRVM_NAME);
294 if (prog->num_edicts >= prog->max_edicts)
295 PRVM_MEM_IncreaseEdicts();
297 e = PRVM_EDICT_NUM(i);
298 PRVM_ED_ClearEdict (e);
300 e->priv.required->allocation_origin = PRVM_AllocationOrigin();
309 Marks the edict as free
310 FIXME: walk all entities and NULL out references to this entity
313 void PRVM_ED_Free (prvm_edict_t *ed)
315 // dont delete the null entity (world) or reserved edicts
316 if(PRVM_NUM_FOR_EDICT(ed) <= prog->reserved_edicts )
319 PRVM_GCALL(free_edict)(ed);
321 ed->priv.required->free = true;
322 ed->priv.required->freetime = realtime;
323 if(ed->priv.required->allocation_origin)
325 PRVM_Free((char *)ed->priv.required->allocation_origin);
326 ed->priv.required->allocation_origin = NULL;
330 //===========================================================================
337 ddef_t *PRVM_ED_GlobalAtOfs (int ofs)
342 for (i = 0;i < prog->numglobaldefs;i++)
344 def = &prog->globaldefs[i];
356 ddef_t *PRVM_ED_FieldAtOfs (int ofs)
361 for (i = 0;i < prog->numfielddefs;i++)
363 def = &prog->fielddefs[i];
375 ddef_t *PRVM_ED_FindField (const char *name)
380 for (i = 0;i < prog->numfielddefs;i++)
382 def = &prog->fielddefs[i];
383 if (!strcmp(PRVM_GetString(def->s_name), name))
394 ddef_t *PRVM_ED_FindGlobal (const char *name)
399 for (i = 0;i < prog->numglobaldefs;i++)
401 def = &prog->globaldefs[i];
402 if (!strcmp(PRVM_GetString(def->s_name), name))
414 mfunction_t *PRVM_ED_FindFunction (const char *name)
419 for (i = 0;i < prog->numfunctions;i++)
421 func = &prog->functions[i];
422 if (!strcmp(PRVM_GetString(func->s_name), name))
433 Returns a string describing *data in a type specific manner
436 char *PRVM_ValueString (etype_t type, prvm_eval_t *val)
438 static char line[MAX_INPUTLINE];
443 type = (etype_t)((int) type & ~DEF_SAVEGLOBAL);
448 strlcpy (line, PRVM_GetString (val->string), sizeof (line));
452 if (n < 0 || n >= prog->max_edicts)
453 dpsnprintf (line, sizeof(line), "entity %i (invalid!)", n);
455 dpsnprintf (line, sizeof(line), "entity %i", n);
458 f = prog->functions + val->function;
459 dpsnprintf (line, sizeof(line), "%s()", PRVM_GetString(f->s_name));
462 def = PRVM_ED_FieldAtOfs ( val->_int );
463 dpsnprintf (line, sizeof(line), ".%s", PRVM_GetString(def->s_name));
466 dpsnprintf (line, sizeof(line), "void");
469 // LordHavoc: changed from %5.1f to %10.4f
470 dpsnprintf (line, sizeof(line), "%10.4f", val->_float);
473 // LordHavoc: changed from %5.1f to %10.4f
474 dpsnprintf (line, sizeof(line), "'%10.4f %10.4f %10.4f'", val->vector[0], val->vector[1], val->vector[2]);
477 dpsnprintf (line, sizeof(line), "pointer");
480 dpsnprintf (line, sizeof(line), "bad type %i", (int) type);
491 Returns a string describing *data in a type specific manner
492 Easier to parse than PR_ValueString
495 char *PRVM_UglyValueString (etype_t type, prvm_eval_t *val)
497 static char line[MAX_INPUTLINE];
503 type = (etype_t)((int)type & ~DEF_SAVEGLOBAL);
508 // Parse the string a bit to turn special characters
509 // (like newline, specifically) into escape codes,
510 // this fixes saving games from various mods
511 s = PRVM_GetString (val->string);
512 for (i = 0;i < (int)sizeof(line) - 2 && *s;)
541 dpsnprintf (line, sizeof (line), "%i", PRVM_NUM_FOR_EDICT(PRVM_PROG_TO_EDICT(val->edict)));
544 f = prog->functions + val->function;
545 strlcpy (line, PRVM_GetString (f->s_name), sizeof (line));
548 def = PRVM_ED_FieldAtOfs ( val->_int );
549 dpsnprintf (line, sizeof (line), ".%s", PRVM_GetString(def->s_name));
552 dpsnprintf (line, sizeof (line), "void");
555 dpsnprintf (line, sizeof (line), "%.9g", val->_float);
558 dpsnprintf (line, sizeof (line), "%.9g %.9g %.9g", val->vector[0], val->vector[1], val->vector[2]);
561 dpsnprintf (line, sizeof (line), "bad type %i", type);
572 Returns a string with a description and the contents of a global,
573 padded to 20 field width
576 char *PRVM_GlobalString (int ofs)
582 static char line[128];
584 val = (void *)&prog->globals.generic[ofs];
585 def = PRVM_ED_GlobalAtOfs(ofs);
587 dpsnprintf (line, sizeof(line), "GLOBAL%i", ofs);
590 s = PRVM_ValueString ((etype_t)def->type, (prvm_eval_t *)val);
591 dpsnprintf (line, sizeof(line), "%s (=%s)", PRVM_GetString(def->s_name), s);
595 //for ( ; i<20 ; i++)
596 // strcat (line," ");
602 char *PRVM_GlobalStringNoContents (int ofs)
606 static char line[128];
608 def = PRVM_ED_GlobalAtOfs(ofs);
610 dpsnprintf (line, sizeof(line), "GLOBAL%i", ofs);
612 dpsnprintf (line, sizeof(line), "%s", PRVM_GetString(def->s_name));
615 //for ( ; i<20 ; i++)
616 // strcat (line," ");
630 // LordHavoc: optimized this to print out much more quickly (tempstring)
631 // LordHavoc: changed to print out every 4096 characters (incase there are a lot of fields to print)
632 void PRVM_ED_Print(prvm_edict_t *ed, const char *wildcard_fieldname)
640 char tempstring[MAX_INPUTLINE], tempstring2[260]; // temporary string buffers
642 if (ed->priv.required->free)
644 Con_Printf("%s: FREE\n",PRVM_NAME);
649 dpsnprintf(tempstring, sizeof(tempstring), "\n%s EDICT %i:\n", PRVM_NAME, PRVM_NUM_FOR_EDICT(ed));
650 for (i = 1;i < prog->numfielddefs;i++)
652 d = &prog->fielddefs[i];
653 name = PRVM_GetString(d->s_name);
654 if(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
655 continue; // skip _x, _y, _z vars
657 // Check Field Name Wildcard
658 if(wildcard_fieldname)
659 if( !matchpattern(name, wildcard_fieldname, 1) )
660 // Didn't match; skip
663 v = (int *)(ed->fields.vp + d->ofs);
665 // if the value is still all 0, skip the field
666 type = d->type & ~DEF_SAVEGLOBAL;
668 for (j=0 ; j<prvm_type_size[type] ; j++)
671 if (j == prvm_type_size[type])
674 if (strlen(name) > sizeof(tempstring2)-4)
676 memcpy (tempstring2, name, sizeof(tempstring2)-4);
677 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
678 tempstring2[sizeof(tempstring2)-1] = 0;
681 strlcat(tempstring, name, sizeof(tempstring));
682 for (l = strlen(name);l < 14;l++)
683 strlcat(tempstring, " ", sizeof(tempstring));
684 strlcat(tempstring, " ", sizeof(tempstring));
686 name = PRVM_ValueString((etype_t)d->type, (prvm_eval_t *)v);
687 if (strlen(name) > sizeof(tempstring2)-4)
689 memcpy (tempstring2, name, sizeof(tempstring2)-4);
690 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
691 tempstring2[sizeof(tempstring2)-1] = 0;
694 strlcat(tempstring, name, sizeof(tempstring));
695 strlcat(tempstring, "\n", sizeof(tempstring));
696 if (strlen(tempstring) >= sizeof(tempstring)/2)
698 Con_Print(tempstring);
703 Con_Print(tempstring);
713 extern cvar_t developer_entityparsing;
714 void PRVM_ED_Write (qfile_t *f, prvm_edict_t *ed)
724 if (ed->priv.required->free)
730 for (i = 1;i < prog->numfielddefs;i++)
732 d = &prog->fielddefs[i];
733 name = PRVM_GetString(d->s_name);
735 if(developer_entityparsing.integer)
736 Con_Printf("PRVM_ED_Write: at entity %d field %s\n", PRVM_NUM_FOR_EDICT(ed), name);
738 //if(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
739 if(strlen(name) > 1 && name[strlen(name)-2] == '_')
740 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?)
742 v = (int *)(ed->fields.vp + d->ofs);
744 // if the value is still all 0, skip the field
745 type = d->type & ~DEF_SAVEGLOBAL;
746 for (j=0 ; j<prvm_type_size[type] ; j++)
749 if (j == prvm_type_size[type])
752 FS_Printf(f,"\"%s\" ",name);
753 prog->statestring = va("PRVM_ED_Write, ent=%d, name=%s", i, name);
754 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString((etype_t)d->type, (prvm_eval_t *)v));
755 prog->statestring = NULL;
761 void PRVM_ED_PrintNum (int ent, const char *wildcard_fieldname)
763 PRVM_ED_Print(PRVM_EDICT_NUM(ent), wildcard_fieldname);
768 PRVM_ED_PrintEdicts_f
770 For debugging, prints all the entities in the current server
773 void PRVM_ED_PrintEdicts_f (void)
776 const char *wildcard_fieldname;
778 if(Cmd_Argc() < 2 || Cmd_Argc() > 3)
780 Con_Print("prvm_edicts <program name> <optional field name wildcard>\n");
785 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
789 wildcard_fieldname = Cmd_Argv(2);
791 wildcard_fieldname = NULL;
793 Con_Printf("%s: %i entities\n", PRVM_NAME, prog->num_edicts);
794 for (i=0 ; i<prog->num_edicts ; i++)
795 PRVM_ED_PrintNum (i, wildcard_fieldname);
804 For debugging, prints a single edict
807 void PRVM_ED_PrintEdict_f (void)
810 const char *wildcard_fieldname;
812 if(Cmd_Argc() < 3 || Cmd_Argc() > 4)
814 Con_Print("prvm_edict <program name> <edict number> <optional field name wildcard>\n");
819 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
822 i = atoi (Cmd_Argv(2));
823 if (i >= prog->num_edicts)
825 Con_Print("Bad edict number\n");
830 // Optional Wildcard Provided
831 wildcard_fieldname = Cmd_Argv(3);
834 wildcard_fieldname = NULL;
835 PRVM_ED_PrintNum (i, wildcard_fieldname);
847 // 2 possibilities : 1. just displaying the active edict count
848 // 2. making a function pointer [x]
849 void PRVM_ED_Count_f (void)
857 Con_Print("prvm_count <program name>\n");
862 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
865 if(prog->count_edicts)
866 prog->count_edicts();
870 for (i=0 ; i<prog->num_edicts ; i++)
872 ent = PRVM_EDICT_NUM(i);
873 if (ent->priv.required->free)
878 Con_Printf("num_edicts:%3i\n", prog->num_edicts);
879 Con_Printf("active :%3i\n", active);
886 ==============================================================================
890 FIXME: need to tag constants, doesn't really work
891 ==============================================================================
899 void PRVM_ED_WriteGlobals (qfile_t *f)
907 for (i = 0;i < prog->numglobaldefs;i++)
909 def = &prog->globaldefs[i];
911 if ( !(def->type & DEF_SAVEGLOBAL) )
913 type &= ~DEF_SAVEGLOBAL;
915 if (type != ev_string && type != ev_float && type != ev_entity)
918 name = PRVM_GetString(def->s_name);
920 if(developer_entityparsing.integer)
921 Con_Printf("PRVM_ED_WriteGlobals: at global %s\n", name);
923 prog->statestring = va("PRVM_ED_WriteGlobals, name=%s", name);
924 FS_Printf(f,"\"%s\" ", name);
925 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString((etype_t)type, (prvm_eval_t *)&prog->globals.generic[def->ofs]));
926 prog->statestring = NULL;
936 void PRVM_ED_ParseGlobals (const char *data)
938 char keyname[MAX_INPUTLINE];
944 if (!COM_ParseToken_Simple(&data, false, false))
945 PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace");
946 if (com_token[0] == '}')
949 if (developer_entityparsing.integer)
950 Con_Printf("Key: \"%s\"", com_token);
952 strlcpy (keyname, com_token, sizeof(keyname));
955 if (!COM_ParseToken_Simple(&data, false, true))
956 PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace");
958 if (developer_entityparsing.integer)
959 Con_Printf(" \"%s\"\n", com_token);
961 if (com_token[0] == '}')
962 PRVM_ERROR ("PRVM_ED_ParseGlobals: closing brace without data");
964 key = PRVM_ED_FindGlobal (keyname);
967 Con_DPrintf("'%s' is not a global on %s\n", keyname, PRVM_NAME);
971 if (!PRVM_ED_ParseEpair(NULL, key, com_token, true))
972 PRVM_ERROR ("PRVM_ED_ParseGlobals: parse error");
976 //============================================================================
983 Can parse either fields or globals
984 returns false if error
987 qboolean PRVM_ED_ParseEpair(prvm_edict_t *ent, ddef_t *key, const char *s, qboolean parsebackslash)
996 val = (prvm_eval_t *)(ent->fields.vp + key->ofs);
998 val = (prvm_eval_t *)(prog->globals.generic + key->ofs);
999 switch (key->type & ~DEF_SAVEGLOBAL)
1002 l = (int)strlen(s) + 1;
1003 val->string = PRVM_AllocString(l, &new_p);
1004 for (i = 0;i < l;i++)
1006 if (s[i] == '\\' && s[i+1] && parsebackslash)
1011 else if (s[i] == 'r')
1022 while (*s && ISWHITESPACE(*s))
1024 val->_float = atof(s);
1028 for (i = 0;i < 3;i++)
1030 while (*s && ISWHITESPACE(*s))
1034 val->vector[i] = atof(s);
1035 while (!ISWHITESPACE(*s))
1043 while (*s && ISWHITESPACE(*s))
1046 if (i >= prog->limit_edicts)
1047 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);
1048 while (i >= prog->max_edicts)
1049 PRVM_MEM_IncreaseEdicts();
1050 // if IncreaseEdicts was called the base pointer needs to be updated
1052 val = (prvm_eval_t *)(ent->fields.vp + key->ofs);
1053 val->edict = PRVM_EDICT_TO_PROG(PRVM_EDICT_NUM((int)i));
1059 Con_DPrintf("PRVM_ED_ParseEpair: Bogus field name %s in %s\n", s, PRVM_NAME);
1062 def = PRVM_ED_FindField(s + 1);
1065 Con_DPrintf("PRVM_ED_ParseEpair: Can't find field %s in %s\n", s, PRVM_NAME);
1068 val->_int = def->ofs;
1072 func = PRVM_ED_FindFunction(s);
1075 Con_Printf("PRVM_ED_ParseEpair: Can't find function %s in %s\n", s, PRVM_NAME);
1078 val->function = func - prog->functions;
1082 Con_Printf("PRVM_ED_ParseEpair: Unknown key->type %i for key \"%s\" on %s\n", key->type, PRVM_GetString(key->s_name), PRVM_NAME);
1092 Console command to send a string to QC function GameCommand of the
1096 sv_cmd adminmsg 3 "do not teamkill"
1097 cl_cmd someclientcommand
1098 menu_cmd somemenucommand
1100 All progs can support this extension; sg calls it in server QC, cg in client
1104 void PRVM_GameCommand(const char *whichprogs, const char *whichcmd)
1108 Con_Printf("%s text...\n", whichcmd);
1113 if(!PRVM_SetProgFromString(whichprogs))
1114 // note: this is not PRVM_SetProg because that one aborts "hard" using PRVM_Error
1115 // also, it makes printing error messages easier!
1117 Con_Printf("%s program not loaded.\n", whichprogs);
1121 if(!prog->funcoffsets.GameCommand)
1123 Con_Printf("%s program do not support GameCommand!\n", whichprogs);
1127 int restorevm_tempstringsbuf_cursize;
1132 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
1133 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(s ? s : "");
1134 PRVM_ExecuteProgram (prog->funcoffsets.GameCommand, "QC function GameCommand is missing");
1135 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1140 void PRVM_GameCommand_Server_f(void)
1142 PRVM_GameCommand("server", "sv_cmd");
1144 void PRVM_GameCommand_Client_f(void)
1146 PRVM_GameCommand("client", "cl_cmd");
1148 void PRVM_GameCommand_Menu_f(void)
1150 PRVM_GameCommand("menu", "menu_cmd");
1157 Console command to load a field of a specified edict
1160 void PRVM_ED_EdictGet_f(void)
1167 if(Cmd_Argc() != 4 && Cmd_Argc() != 5)
1169 Con_Print("prvm_edictget <program name> <edict number> <field> [<cvar>]\n");
1174 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1176 Con_Printf("Wrong program name %s !\n", Cmd_Argv(1));
1180 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1182 if((key = PRVM_ED_FindField(Cmd_Argv(3))) == 0)
1184 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1188 v = (prvm_eval_t *)(ed->fields.vp + key->ofs);
1189 s = PRVM_UglyValueString((etype_t)key->type, v);
1192 cvar_t *cvar = Cvar_FindVar(Cmd_Argv(4));
1193 if (cvar && cvar->flags & CVAR_READONLY)
1195 Con_Printf("prvm_edictget: %s is read-only\n", cvar->name);
1198 Cvar_Get(Cmd_Argv(4), s, 0, NULL);
1201 Con_Printf("%s\n", s);
1207 void PRVM_ED_GlobalGet_f(void)
1213 if(Cmd_Argc() != 3 && Cmd_Argc() != 4)
1215 Con_Print("prvm_globalget <program name> <global> [<cvar>]\n");
1220 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1222 Con_Printf("Wrong program name %s !\n", Cmd_Argv(1));
1226 key = PRVM_ED_FindGlobal(Cmd_Argv(2));
1229 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
1233 v = (prvm_eval_t *) &prog->globals.generic[key->ofs];
1234 s = PRVM_UglyValueString((etype_t)key->type, v);
1237 cvar_t *cvar = Cvar_FindVar(Cmd_Argv(3));
1238 if (cvar && cvar->flags & CVAR_READONLY)
1240 Con_Printf("prvm_globalget: %s is read-only\n", cvar->name);
1243 Cvar_Get(Cmd_Argv(3), s, 0, NULL);
1246 Con_Printf("%s\n", s);
1256 Console command to set a field of a specified edict
1259 void PRVM_ED_EdictSet_f(void)
1266 Con_Print("prvm_edictset <program name> <edict number> <field> <value>\n");
1271 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1273 Con_Printf("Wrong program name %s !\n", Cmd_Argv(1));
1277 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1279 if((key = PRVM_ED_FindField(Cmd_Argv(3))) == 0)
1280 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1282 PRVM_ED_ParseEpair(ed, key, Cmd_Argv(4), true);
1288 ====================
1291 Parses an edict out of the given string, returning the new position
1292 ed should be a properly initialized empty edict.
1293 Used for initial level load and for savegames.
1294 ====================
1296 const char *PRVM_ED_ParseEdict (const char *data, prvm_edict_t *ent)
1306 // go through all the dictionary pairs
1310 if (!COM_ParseToken_Simple(&data, false, false))
1311 PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1312 if (developer_entityparsing.integer)
1313 Con_Printf("Key: \"%s\"", com_token);
1314 if (com_token[0] == '}')
1317 // anglehack is to allow QuakeEd to write single scalar angles
1318 // and allow them to be turned into vectors. (FIXME...)
1319 if (!strcmp(com_token, "angle"))
1321 strlcpy (com_token, "angles", sizeof(com_token));
1327 // FIXME: change light to _light to get rid of this hack
1328 if (!strcmp(com_token, "light"))
1329 strlcpy (com_token, "light_lev", sizeof(com_token)); // hack for single light def
1331 strlcpy (keyname, com_token, sizeof(keyname));
1333 // another hack to fix keynames with trailing spaces
1334 n = strlen(keyname);
1335 while (n && keyname[n-1] == ' ')
1342 if (!COM_ParseToken_Simple(&data, false, false))
1343 PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1344 if (developer_entityparsing.integer)
1345 Con_Printf(" \"%s\"\n", com_token);
1347 if (com_token[0] == '}')
1348 PRVM_ERROR ("PRVM_ED_ParseEdict: closing brace without data");
1352 // ignore attempts to set key "" (this problem occurs in nehahra neh1m8.bsp)
1356 // keynames with a leading underscore are used for utility comments,
1357 // and are immediately discarded by quake
1358 if (keyname[0] == '_')
1361 key = PRVM_ED_FindField (keyname);
1364 Con_DPrintf("%s: '%s' is not a field\n", PRVM_NAME, keyname);
1371 strlcpy (temp, com_token, sizeof(temp));
1372 dpsnprintf (com_token, sizeof(com_token), "0 %s 0", temp);
1375 if (!PRVM_ED_ParseEpair(ent, key, com_token, strcmp(keyname, "wad") != 0))
1376 PRVM_ERROR ("PRVM_ED_ParseEdict: parse error");
1380 ent->priv.required->free = true;
1388 PRVM_ED_LoadFromFile
1390 The entities are directly placed in the array, rather than allocated with
1391 PRVM_ED_Alloc, because otherwise an error loading the map would have entity
1392 number references out of order.
1394 Creates a server's entity / program execution context by
1395 parsing textual entity definitions out of an ent file.
1397 Used for both fresh maps and savegame loads. A fresh map would also need
1398 to call PRVM_ED_CallSpawnFunctions () to let the objects initialize themselves.
1401 void PRVM_ED_LoadFromFile (const char *data)
1404 int parsed, inhibited, spawned, died;
1405 const char *funcname;
1413 prvm_reuseedicts_always_allow = realtime;
1418 // parse the opening brace
1419 if (!COM_ParseToken_Simple(&data, false, false))
1421 if (com_token[0] != '{')
1422 PRVM_ERROR ("PRVM_ED_LoadFromFile: %s: found %s when expecting {", PRVM_NAME, com_token);
1424 // CHANGED: this is not conform to PR_LoadFromFile
1425 if(prog->loadintoworld)
1427 prog->loadintoworld = false;
1428 ent = PRVM_EDICT_NUM(0);
1431 ent = PRVM_ED_Alloc();
1434 if (ent != prog->edicts) // hack
1435 memset (ent->fields.vp, 0, prog->entityfields * 4);
1437 data = PRVM_ED_ParseEdict (data, ent);
1440 // remove the entity ?
1441 if(prog->load_edict && !prog->load_edict(ent))
1448 if (prog->funcoffsets.SV_OnEntityPreSpawnFunction)
1451 PRVM_allglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1452 PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityPreSpawnFunction, "QC function SV_OnEntityPreSpawnFunction is missing");
1455 if(ent->priv.required->free)
1462 // immediately call spawn function, but only if there is a self global and a classname
1464 if(!ent->priv.required->free)
1466 if (!PRVM_alledictstring(ent, classname))
1468 Con_Print("No classname for:\n");
1469 PRVM_ED_Print(ent, NULL);
1474 // look for the spawn function
1475 funcname = PRVM_GetString(PRVM_alledictstring(ent, classname));
1476 func = PRVM_ED_FindFunction (va("spawnfunc_%s", funcname));
1478 if(!PRVM_allglobalfloat(require_spawnfunc_prefix))
1479 func = PRVM_ED_FindFunction (funcname);
1483 // check for OnEntityNoSpawnFunction
1484 if (prog->funcoffsets.SV_OnEntityNoSpawnFunction)
1487 PRVM_allglobaledict(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_allglobaledict(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_allglobaledict(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");
1749 prog->globaloffsets.pmove_onground = PRVM_ED_FindGlobalOffset("pmove_onground");
1750 prog->globaloffsets.pmove_inwater = PRVM_ED_FindGlobalOffset("pmove_inwater");
1752 prog->globaloffsets.particle_type = PRVM_ED_FindGlobalOffset("particle_type");
1753 prog->globaloffsets.particle_blendmode = PRVM_ED_FindGlobalOffset("particle_blendmode");
1754 prog->globaloffsets.particle_orientation = PRVM_ED_FindGlobalOffset("particle_orientation");
1755 prog->globaloffsets.particle_color1 = PRVM_ED_FindGlobalOffset("particle_color1");
1756 prog->globaloffsets.particle_color2 = PRVM_ED_FindGlobalOffset("particle_color2");
1757 prog->globaloffsets.particle_tex = PRVM_ED_FindGlobalOffset("particle_tex");
1758 prog->globaloffsets.particle_size = PRVM_ED_FindGlobalOffset("particle_size");
1759 prog->globaloffsets.particle_sizeincrease = PRVM_ED_FindGlobalOffset("particle_sizeincrease");
1760 prog->globaloffsets.particle_alpha = PRVM_ED_FindGlobalOffset("particle_alpha");
1761 prog->globaloffsets.particle_alphafade = PRVM_ED_FindGlobalOffset("particle_alphafade");
1762 prog->globaloffsets.particle_time = PRVM_ED_FindGlobalOffset("particle_time");
1763 prog->globaloffsets.particle_gravity = PRVM_ED_FindGlobalOffset("particle_gravity");
1764 prog->globaloffsets.particle_bounce = PRVM_ED_FindGlobalOffset("particle_bounce");
1765 prog->globaloffsets.particle_airfriction = PRVM_ED_FindGlobalOffset("particle_airfriction");
1766 prog->globaloffsets.particle_liquidfriction = PRVM_ED_FindGlobalOffset("particle_liquidfriction");
1767 prog->globaloffsets.particle_originjitter = PRVM_ED_FindGlobalOffset("particle_originjitter");
1768 prog->globaloffsets.particle_velocityjitter = PRVM_ED_FindGlobalOffset("particle_velocityjitter");
1769 prog->globaloffsets.particle_qualityreduction = PRVM_ED_FindGlobalOffset("particle_qualityreduction");
1770 prog->globaloffsets.particle_stretch = PRVM_ED_FindGlobalOffset("particle_stretch");
1771 prog->globaloffsets.particle_staincolor1 = PRVM_ED_FindGlobalOffset("particle_staincolor1");
1772 prog->globaloffsets.particle_staincolor2 = PRVM_ED_FindGlobalOffset("particle_staincolor2");
1773 prog->globaloffsets.particle_stainalpha = PRVM_ED_FindGlobalOffset("particle_stainalpha");
1774 prog->globaloffsets.particle_stainsize = PRVM_ED_FindGlobalOffset("particle_stainsize");
1775 prog->globaloffsets.particle_staintex = PRVM_ED_FindGlobalOffset("particle_staintex");
1776 prog->globaloffsets.particle_staintex = PRVM_ED_FindGlobalOffset("particle_staintex");
1777 prog->globaloffsets.particle_delayspawn = PRVM_ED_FindGlobalOffset("particle_delayspawn");
1778 prog->globaloffsets.particle_delaycollision = PRVM_ED_FindGlobalOffset("particle_delaycollision");
1779 prog->globaloffsets.particle_angle = PRVM_ED_FindGlobalOffset("particle_angle");
1780 prog->globaloffsets.particle_spin = PRVM_ED_FindGlobalOffset("particle_spin");
1782 // menu qc only uses some functions, nothing else
1783 prog->funcoffsets.m_draw = PRVM_ED_FindFunctionOffset("m_draw");
1784 prog->funcoffsets.m_init = PRVM_ED_FindFunctionOffset("m_init");
1785 prog->funcoffsets.m_keydown = PRVM_ED_FindFunctionOffset("m_keydown");
1786 prog->funcoffsets.m_keyup = PRVM_ED_FindFunctionOffset("m_keyup");
1787 prog->funcoffsets.m_shutdown = PRVM_ED_FindFunctionOffset("m_shutdown");
1788 prog->funcoffsets.m_toggle = PRVM_ED_FindFunctionOffset("m_toggle");
1789 prog->funcoffsets.m_newmap = PRVM_ED_FindFunctionOffset("m_newmap");
1794 typedef struct dpfield_s
1801 #define DPFIELDS (sizeof(dpfields) / sizeof(dpfield_t))
1803 dpfield_t dpfields[] =
1814 #define PO_HASHSIZE 16384
1815 typedef struct po_string_s
1818 struct po_string_s *nextonhashchain;
1823 po_string_t *hashtable[PO_HASHSIZE];
1826 void PRVM_PO_UnparseString(char *out, const char *in, size_t outsize)
1835 case '\a': if(outsize >= 2) { *out++ = '\\'; *out++ = 'a'; outsize -= 2; } break;
1836 case '\b': if(outsize >= 2) { *out++ = '\\'; *out++ = 'b'; outsize -= 2; } break;
1837 case '\t': if(outsize >= 2) { *out++ = '\\'; *out++ = 't'; outsize -= 2; } break;
1838 case '\r': if(outsize >= 2) { *out++ = '\\'; *out++ = 'r'; outsize -= 2; } break;
1839 case '\n': if(outsize >= 2) { *out++ = '\\'; *out++ = 'n'; outsize -= 2; } break;
1840 case '\\': if(outsize >= 2) { *out++ = '\\'; *out++ = '\\'; outsize -= 2; } break;
1841 case '"': if(outsize >= 2) { *out++ = '\\'; *out++ = '"'; outsize -= 2; } break;
1843 if(*in >= 0 && *in <= 0x1F)
1848 *out++ = '0' + ((*in & 0700) >> 6);
1849 *out++ = '0' + ((*in & 0070) >> 3);
1850 *out++ = '0' + ((*in & 0007));
1867 void PRVM_PO_ParseString(char *out, const char *in, size_t outsize)
1880 case 'a': if(outsize > 0) { *out++ = '\a'; --outsize; } break;
1881 case 'b': if(outsize > 0) { *out++ = '\b'; --outsize; } break;
1882 case 't': if(outsize > 0) { *out++ = '\t'; --outsize; } break;
1883 case 'r': if(outsize > 0) { *out++ = '\r'; --outsize; } break;
1884 case 'n': if(outsize > 0) { *out++ = '\n'; --outsize; } break;
1885 case '\\': if(outsize > 0) { *out++ = '\\'; --outsize; } break;
1886 case '"': if(outsize > 0) { *out++ = '"'; --outsize; } break;
1887 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
1891 if(*in >= '0' && *in <= '7')
1894 *out = (*out << 3) | (*in - '0');
1897 if(*in >= '0' && *in <= '7')
1900 *out = (*out << 3) | (*in - '0');
1911 if(outsize > 0) { *out++ = *in; --outsize; }
1926 po_t *PRVM_PO_Load(const char *filename, mempool_t *pool)
1931 char inbuf[MAX_INPUTLINE];
1932 char decodedbuf[MAX_INPUTLINE];
1935 po_string_t thisstr;
1936 const char *buf = (const char *) FS_LoadFile(filename, pool, true, NULL);
1941 memset(&thisstr, 0, sizeof(thisstr)); // hush compiler warning
1943 po = (po_t *)Mem_Alloc(pool, sizeof(*po));
1944 memset(po, 0, sizeof(*po));
1952 p = strchr(p, '\n');
1958 if(*p == '\r' || *p == '\n')
1963 if(!strncmp(p, "msgid \"", 7))
1968 else if(!strncmp(p, "msgstr \"", 8))
1975 p = strchr(p, '\n');
1985 q = strchr(p, '\n');
1992 if((size_t)(q - p) >= (size_t) sizeof(inbuf))
1994 strlcpy(inbuf, p, q - p); // not - 1, because this adds a NUL
1995 PRVM_PO_ParseString(decodedbuf + decodedpos, inbuf, sizeof(decodedbuf) - decodedpos);
1996 decodedpos += strlen(decodedbuf + decodedpos);
2006 Mem_Free(thisstr.key);
2007 thisstr.key = (char *)Mem_Alloc(pool, decodedpos + 1);
2008 memcpy(thisstr.key, decodedbuf, decodedpos + 1);
2010 else if(decodedpos > 0 && thisstr.key) // skip empty translation results
2012 thisstr.value = (char *)Mem_Alloc(pool, decodedpos + 1);
2013 memcpy(thisstr.value, decodedbuf, decodedpos + 1);
2014 hashindex = CRC_Block((const unsigned char *) thisstr.key, strlen(thisstr.key)) % PO_HASHSIZE;
2015 thisstr.nextonhashchain = po->hashtable[hashindex];
2016 po->hashtable[hashindex] = (po_string_t *)Mem_Alloc(pool, sizeof(thisstr));
2017 memcpy(po->hashtable[hashindex], &thisstr, sizeof(thisstr));
2018 memset(&thisstr, 0, sizeof(thisstr));
2022 Mem_Free((char *) buf);
2025 const char *PRVM_PO_Lookup(po_t *po, const char *str)
2027 int hashindex = CRC_Block((const unsigned char *) str, strlen(str)) % PO_HASHSIZE;
2028 po_string_t *p = po->hashtable[hashindex];
2031 if(!strcmp(str, p->key))
2033 p = p->nextonhashchain;
2037 void PRVM_PO_Destroy(po_t *po)
2040 for(i = 0; i < PO_HASHSIZE; ++i)
2042 po_string_t *p = po->hashtable[i];
2046 p = p->nextonhashchain;
2055 void PRVM_LeakTest(void);
2056 void PRVM_ResetProg(void)
2059 PRVM_GCALL(reset_cmd)();
2060 Mem_FreePool(&prog->progs_mempool);
2062 PRVM_PO_Destroy((po_t *) prog->po);
2063 memset(prog,0,sizeof(prvm_prog_t));
2064 prog->starttime = Sys_DoubleTime();
2072 void PRVM_LoadLNO( const char *progname ) {
2073 fs_offset_t filesize;
2075 unsigned int *header;
2078 FS_StripExtension( progname, filename, sizeof( filename ) );
2079 strlcat( filename, ".lno", sizeof( filename ) );
2081 lno = FS_LoadFile( filename, tempmempool, false, &filesize );
2087 <Spike> SafeWrite (h, &lnotype, sizeof(int));
2088 <Spike> SafeWrite (h, &version, sizeof(int));
2089 <Spike> SafeWrite (h, &numglobaldefs, sizeof(int));
2090 <Spike> SafeWrite (h, &numpr_globals, sizeof(int));
2091 <Spike> SafeWrite (h, &numfielddefs, sizeof(int));
2092 <Spike> SafeWrite (h, &numstatements, sizeof(int));
2093 <Spike> SafeWrite (h, statement_linenums, numstatements*sizeof(int));
2095 if ((unsigned int)filesize < (6 + prog->progs_numstatements) * sizeof(int))
2101 header = (unsigned int *) lno;
2102 if( header[ 0 ] == *(unsigned int *) "LNOF" &&
2103 LittleLong( header[ 1 ] ) == 1 &&
2104 (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs_numglobaldefs &&
2105 (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs_numglobals &&
2106 (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs_numfielddefs &&
2107 (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs_numstatements )
2109 prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof( int ) );
2110 memcpy( prog->statement_linenums, (int *) lno + 6, prog->progs_numstatements * sizeof( int ) );
2120 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)
2123 dprograms_t *dprograms;
2124 dstatement_t *instatements;
2125 ddef_t *infielddefs;
2126 ddef_t *inglobaldefs;
2128 dfunction_t *infunctions;
2130 fs_offset_t filesize;
2131 int requiredglobalspace;
2138 PRVM_ERROR ("PRVM_LoadProgs: there is already a %s program loaded!", PRVM_NAME );
2140 dprograms = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
2141 if (dprograms == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
2142 PRVM_ERROR ("PRVM_LoadProgs: couldn't load %s for %s", filename, PRVM_NAME);
2143 // TODO bounds check header fields (e.g. numstatements), they must never go behind end of file
2145 Con_DPrintf("%s programs occupy %iK.\n", PRVM_NAME, (int)(filesize/1024));
2147 requiredglobalspace = 0;
2148 for (i = 0;i < numrequiredglobals;i++)
2149 requiredglobalspace += required_global[i].type == ev_vector ? 3 : 1;
2151 prog->filecrc = CRC_Block((unsigned char *)dprograms, filesize);
2153 // byte swap the header
2154 prog->progs_version = LittleLong(dprograms->version);
2155 prog->progs_crc = LittleLong(dprograms->crc);
2156 if (prog->progs_version != PROG_VERSION)
2157 PRVM_ERROR ("%s: %s has wrong version number (%i should be %i)", PRVM_NAME, filename, prog->progs_version, PROG_VERSION);
2158 if (prog->progs_crc != prog->headercrc && prog->progs_crc != prog->headercrc2)
2159 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);
2160 instatements = (dstatement_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_statements));
2161 prog->progs_numstatements = LittleLong(dprograms->numstatements);
2162 inglobaldefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globaldefs));
2163 prog->progs_numglobaldefs = LittleLong(dprograms->numglobaldefs);
2164 infielddefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_fielddefs));
2165 prog->progs_numfielddefs = LittleLong(dprograms->numfielddefs);
2166 infunctions = (dfunction_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_functions));
2167 prog->progs_numfunctions = LittleLong(dprograms->numfunctions);
2168 instrings = (char *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_strings));
2169 prog->progs_numstrings = LittleLong(dprograms->numstrings);
2170 inglobals = (float *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globals));
2171 prog->progs_numglobals = LittleLong(dprograms->numglobals);
2172 prog->progs_entityfields = LittleLong(dprograms->entityfields);
2174 prog->numstatements = prog->progs_numstatements;
2175 prog->numglobaldefs = prog->progs_numglobaldefs;
2176 prog->numfielddefs = prog->progs_numfielddefs;
2177 prog->numfunctions = prog->progs_numfunctions;
2178 prog->numstrings = prog->progs_numstrings;
2179 prog->numglobals = prog->progs_numglobals;
2180 prog->entityfields = prog->progs_entityfields;
2182 if (LittleLong(dprograms->ofs_strings) + prog->progs_numstrings >= (int)filesize)
2183 PRVM_ERROR ("%s: %s strings go past end of file", PRVM_NAME, filename);
2184 prog->strings = (char *)Mem_Alloc(prog->progs_mempool, prog->progs_numstrings);
2185 memcpy(prog->strings, instrings, prog->progs_numstrings);
2186 prog->stringssize = prog->progs_numstrings;
2188 prog->numknownstrings = 0;
2189 prog->maxknownstrings = 0;
2190 prog->knownstrings = NULL;
2191 prog->knownstrings_freeable = NULL;
2193 Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
2195 // we need to expand the globaldefs and fielddefs to include engine defs
2196 prog->globaldefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobaldefs + numrequiredglobals) * sizeof(ddef_t));
2197 prog->globals.generic = (float *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobals + requiredglobalspace) * sizeof(float));
2198 prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numfielddefs + numrequiredfields) * sizeof(ddef_t));
2199 // we need to convert the statements to our memory format
2200 prog->statements = (mstatement_t *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(mstatement_t));
2201 // allocate space for profiling statement usage
2202 prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(*prog->statement_profile));
2203 // functions need to be converted to the memory format
2204 prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs_numfunctions);
2206 for (i = 0;i < prog->progs_numfunctions;i++)
2208 prog->functions[i].first_statement = LittleLong(infunctions[i].first_statement);
2209 prog->functions[i].parm_start = LittleLong(infunctions[i].parm_start);
2210 prog->functions[i].s_name = LittleLong(infunctions[i].s_name);
2211 prog->functions[i].s_file = LittleLong(infunctions[i].s_file);
2212 prog->functions[i].numparms = LittleLong(infunctions[i].numparms);
2213 prog->functions[i].locals = LittleLong(infunctions[i].locals);
2214 memcpy(prog->functions[i].parm_size, infunctions[i].parm_size, sizeof(infunctions[i].parm_size));
2215 if(prog->functions[i].first_statement >= prog->numstatements)
2216 PRVM_ERROR("PRVM_LoadProgs: out of bounds function statement (function %d) in %s", i, PRVM_NAME);
2217 // TODO bounds check parm_start, s_name, s_file, numparms, locals, parm_size
2220 // copy the globaldefs to the new globaldefs list
2221 for (i=0 ; i<prog->numglobaldefs ; i++)
2223 prog->globaldefs[i].type = LittleShort(inglobaldefs[i].type);
2224 prog->globaldefs[i].ofs = LittleShort(inglobaldefs[i].ofs);
2225 prog->globaldefs[i].s_name = LittleLong(inglobaldefs[i].s_name);
2226 // TODO bounds check ofs, s_name
2229 // append the required globals
2230 for (i = 0;i < numrequiredglobals;i++)
2232 prog->globaldefs[prog->numglobaldefs].type = required_global[i].type;
2233 prog->globaldefs[prog->numglobaldefs].ofs = prog->numglobals;
2234 prog->globaldefs[prog->numglobaldefs].s_name = PRVM_SetEngineString(required_global[i].name);
2235 if (prog->globaldefs[prog->numglobaldefs].type == ev_vector)
2236 prog->numglobals += 3;
2239 prog->numglobaldefs++;
2242 // copy the progs fields to the new fields list
2243 for (i = 0;i < prog->numfielddefs;i++)
2245 prog->fielddefs[i].type = LittleShort(infielddefs[i].type);
2246 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
2247 PRVM_ERROR ("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", PRVM_NAME);
2248 prog->fielddefs[i].ofs = LittleShort(infielddefs[i].ofs);
2249 prog->fielddefs[i].s_name = LittleLong(infielddefs[i].s_name);
2250 // TODO bounds check ofs, s_name
2253 // append the required fields
2254 for (i = 0;i < numrequiredfields;i++)
2256 prog->fielddefs[prog->numfielddefs].type = required_field[i].type;
2257 prog->fielddefs[prog->numfielddefs].ofs = prog->entityfields;
2258 prog->fielddefs[prog->numfielddefs].s_name = PRVM_SetEngineString(required_field[i].name);
2259 if (prog->fielddefs[prog->numfielddefs].type == ev_vector)
2260 prog->entityfields += 3;
2262 prog->entityfields++;
2263 prog->numfielddefs++;
2266 // LordHavoc: TODO: reorder globals to match engine struct
2267 // LordHavoc: TODO: reorder fields to match engine struct
2268 #define remapglobal(index) (index)
2269 #define remapfield(index) (index)
2272 for (i = 0;i < prog->progs_numglobals;i++)
2273 ((int *)prog->globals.generic)[remapglobal(i)] = LittleLong(((int *)inglobals)[i]);
2275 // LordHavoc: TODO: support 32bit progs statement formats
2276 // copy, remap globals in statements, bounds check
2277 for (i = 0;i < prog->progs_numstatements;i++)
2279 op = (opcode_t)LittleShort(instatements[i].op);
2280 a = (unsigned short)LittleShort(instatements[i].a);
2281 b = (unsigned short)LittleShort(instatements[i].b);
2282 c = (unsigned short)LittleShort(instatements[i].c);
2288 if (a >= prog->progs_numglobals || b + i < 0 || b + i >= prog->progs_numstatements)
2289 PRVM_ERROR("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", i, PRVM_NAME);
2290 prog->statements[i].op = op;
2291 prog->statements[i].operand[0] = remapglobal(a);
2292 prog->statements[i].operand[1] = -1;
2293 prog->statements[i].operand[2] = -1;
2294 prog->statements[i].jumpabsolute = i + b;
2298 if (a + i < 0 || a + i >= prog->progs_numstatements)
2299 PRVM_ERROR("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, PRVM_NAME);
2300 prog->statements[i].op = op;
2301 prog->statements[i].operand[0] = -1;
2302 prog->statements[i].operand[1] = -1;
2303 prog->statements[i].operand[2] = -1;
2304 prog->statements[i].jumpabsolute = i + a;
2307 Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", (int)op, i, PRVM_NAME);
2308 // global global global
2343 if (a >= prog->progs_numglobals || b >= prog->progs_numglobals || c >= prog->progs_numglobals)
2344 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
2345 prog->statements[i].op = op;
2346 prog->statements[i].operand[0] = remapglobal(a);
2347 prog->statements[i].operand[1] = remapglobal(b);
2348 prog->statements[i].operand[2] = remapglobal(c);
2349 prog->statements[i].jumpabsolute = -1;
2351 // global none global
2357 if (a >= prog->progs_numglobals || c >= prog->progs_numglobals)
2358 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
2359 prog->statements[i].op = op;
2360 prog->statements[i].operand[0] = remapglobal(a);
2361 prog->statements[i].operand[1] = -1;
2362 prog->statements[i].operand[2] = remapglobal(c);
2363 prog->statements[i].jumpabsolute = -1;
2379 if (a >= prog->progs_numglobals || b >= prog->progs_numglobals)
2380 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
2381 prog->statements[i].op = op;
2382 prog->statements[i].operand[0] = remapglobal(a);
2383 prog->statements[i].operand[1] = remapglobal(b);
2384 prog->statements[i].operand[2] = -1;
2385 prog->statements[i].jumpabsolute = -1;
2399 if ( a >= prog->progs_numglobals)
2400 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
2401 prog->statements[i].op = op;
2402 prog->statements[i].operand[0] = remapglobal(a);
2403 prog->statements[i].operand[1] = -1;
2404 prog->statements[i].operand[2] = -1;
2405 prog->statements[i].jumpabsolute = -1;
2409 if(prog->numstatements < 1)
2411 PRVM_ERROR("PRVM_LoadProgs: empty program in %s", PRVM_NAME);
2413 else switch(prog->statements[prog->numstatements - 1].op)
2420 PRVM_ERROR("PRVM_LoadProgs: program may fall off the edge (does not end with RETURN, GOTO or DONE) in %s", PRVM_NAME);
2424 // we're done with the file now
2425 Mem_Free(dprograms);
2428 // check required functions
2429 for(i=0 ; i < numrequiredfunc ; i++)
2430 if(PRVM_ED_FindFunction(required_func[i]) == 0)
2431 PRVM_ERROR("%s: %s not found in %s",PRVM_NAME, required_func[i], filename);
2433 PRVM_LoadLNO(filename);
2437 if(*prvm_language.string)
2438 // in CSQC we really shouldn't be able to change how stuff works... sorry for now
2439 // later idea: include a list of authorized .po file checksums with the csprogs
2441 qboolean deftrans = !!strcmp(PRVM_NAME, "client");
2442 const char *realfilename = (strcmp(PRVM_NAME, "client") ? filename : csqc_progname.string);
2443 if(deftrans) // once we have dotranslate_ strings, ALWAYS use the opt-in method!
2445 for (i=0 ; i<prog->numglobaldefs ; i++)
2448 name = PRVM_GetString(prog->globaldefs[i].s_name);
2449 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2450 if(name && !strncmp(name, "dotranslate_", 12))
2457 if(!strcmp(prvm_language.string, "dump"))
2459 qfile_t *f = FS_OpenRealFile(va("%s.pot", realfilename), "w", false);
2460 Con_Printf("Dumping to %s.pot\n", realfilename);
2463 for (i=0 ; i<prog->numglobaldefs ; i++)
2466 name = PRVM_GetString(prog->globaldefs[i].s_name);
2467 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2468 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2470 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2471 const char *value = PRVM_GetString(val->string);
2474 char buf[MAX_INPUTLINE];
2475 PRVM_PO_UnparseString(buf, value, sizeof(buf));
2476 FS_Printf(f, "msgid \"%s\"\nmsgstr \"\"\n\n", buf);
2485 po_t *po = PRVM_PO_Load(va("%s.%s.po", realfilename, prvm_language.string), prog->progs_mempool);
2488 for (i=0 ; i<prog->numglobaldefs ; i++)
2491 name = PRVM_GetString(prog->globaldefs[i].s_name);
2492 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2493 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2495 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2496 const char *value = PRVM_GetString(val->string);
2499 value = PRVM_PO_Lookup(po, value);
2501 val->string = PRVM_SetEngineString(value);
2509 for (i=0 ; i<prog->numglobaldefs ; i++)
2512 name = PRVM_GetString(prog->globaldefs[i].s_name);
2513 //Con_Printf("found var %s\n", name);
2515 && !strncmp(name, "autocvar_", 9)
2516 && !(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
2519 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2520 cvar_t *cvar = Cvar_FindVar(name + 9);
2521 //Con_Printf("PRVM_LoadProgs: autocvar global %s in %s, processing...\n", name, PRVM_NAME);
2526 Con_DPrintf("PRVM_LoadProgs: no cvar for autocvar global %s in %s, creating...\n", name, PRVM_NAME);
2527 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2530 if((float)((int)(val->_float)) == val->_float)
2531 dpsnprintf(buf, sizeof(buf), "%i", (int)(val->_float));
2533 dpsnprintf(buf, sizeof(buf), "%.9g", val->_float);
2537 dpsnprintf(buf, sizeof(buf), "%.9g %.9g %.9g", val->vector[0], val->vector[1], val->vector[2]); value = buf;
2540 value = PRVM_GetString(val->string);
2543 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, PRVM_NAME);
2546 cvar = Cvar_Get(name + 9, value, 0, NULL);
2547 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2549 val->string = PRVM_SetEngineString(cvar->string);
2550 cvar->globaldefindex_stringno[prog - prog_list] = val->string;
2553 PRVM_ERROR("PRVM_LoadProgs: could not create cvar for autocvar global %s in %s", name, PRVM_NAME);
2554 cvar->globaldefindex_progid[prog - prog_list] = prog->id;
2555 cvar->globaldefindex[prog - prog_list] = i;
2557 else if((cvar->flags & CVAR_PRIVATE) == 0)
2559 // MUST BE SYNCED WITH cvar.c Cvar_Set
2562 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2565 val->_float = cvar->value;
2569 VectorClear(val->vector);
2570 for (j = 0;j < 3;j++)
2572 while (*s && ISWHITESPACE(*s))
2576 val->vector[j] = atof(s);
2577 while (!ISWHITESPACE(*s))
2584 val->string = PRVM_SetEngineString(cvar->string);
2585 cvar->globaldefindex_stringno[prog - prog_list] = val->string;
2588 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, PRVM_NAME);
2591 cvar->globaldefindex_progid[prog - prog_list] = prog->id;
2592 cvar->globaldefindex[prog - prog_list] = i;
2595 Con_Printf("PRVM_LoadProgs: private cvar for autocvar global %s in %s\n", name, PRVM_NAME);
2601 prog->loaded = TRUE;
2603 // set flags & ddef_ts in prog
2609 PRVM_GCALL(init_cmd)();
2616 void PRVM_Fields_f (void)
2618 int i, j, ednum, used, usedamount;
2620 char tempstring[MAX_INPUTLINE], tempstring2[260];
2630 Con_Print("no progs loaded\n");
2637 Con_Print("prvm_fields <program name>\n");
2642 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
2645 counts = (int *)Mem_Alloc(tempmempool, prog->numfielddefs * sizeof(int));
2646 for (ednum = 0;ednum < prog->max_edicts;ednum++)
2648 ed = PRVM_EDICT_NUM(ednum);
2649 if (ed->priv.required->free)
2651 for (i = 1;i < prog->numfielddefs;i++)
2653 d = &prog->fielddefs[i];
2654 name = PRVM_GetString(d->s_name);
2655 if (name[strlen(name)-2] == '_')
2656 continue; // skip _x, _y, _z vars
2657 v = (int *)(ed->fields.vp + d->ofs);
2658 // if the value is still all 0, skip the field
2659 for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
2672 for (i = 0;i < prog->numfielddefs;i++)
2674 d = &prog->fielddefs[i];
2675 name = PRVM_GetString(d->s_name);
2676 if (name[strlen(name)-2] == '_')
2677 continue; // skip _x, _y, _z vars
2678 switch(d->type & ~DEF_SAVEGLOBAL)
2681 strlcat(tempstring, "string ", sizeof(tempstring));
2684 strlcat(tempstring, "entity ", sizeof(tempstring));
2687 strlcat(tempstring, "function ", sizeof(tempstring));
2690 strlcat(tempstring, "field ", sizeof(tempstring));
2693 strlcat(tempstring, "void ", sizeof(tempstring));
2696 strlcat(tempstring, "float ", sizeof(tempstring));
2699 strlcat(tempstring, "vector ", sizeof(tempstring));
2702 strlcat(tempstring, "pointer ", sizeof(tempstring));
2705 dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
2706 strlcat(tempstring, tempstring2, sizeof(tempstring));
2709 if (strlen(name) > sizeof(tempstring2)-4)
2711 memcpy (tempstring2, name, sizeof(tempstring2)-4);
2712 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
2713 tempstring2[sizeof(tempstring2)-1] = 0;
2716 strlcat(tempstring, name, sizeof(tempstring));
2717 for (j = (int)strlen(name);j < 25;j++)
2718 strlcat(tempstring, " ", sizeof(tempstring));
2719 dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
2720 strlcat(tempstring, tempstring2, sizeof(tempstring));
2721 strlcat(tempstring, "\n", sizeof(tempstring));
2722 if (strlen(tempstring) >= sizeof(tempstring)/2)
2724 Con_Print(tempstring);
2730 usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
2734 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);
2739 void PRVM_Globals_f (void)
2742 const char *wildcard;
2748 Con_Print("no progs loaded\n");
2751 if(Cmd_Argc () < 2 || Cmd_Argc() > 3)
2753 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
2758 if(!PRVM_SetProgFromString (Cmd_Argv (1)))
2761 if( Cmd_Argc() == 3)
2762 wildcard = Cmd_Argv(2);
2766 Con_Printf("%s :", PRVM_NAME);
2768 for (i = 0;i < prog->numglobaldefs;i++)
2771 if( !matchpattern( PRVM_GetString(prog->globaldefs[i].s_name), wildcard, 1) )
2776 Con_Printf("%s\n", PRVM_GetString(prog->globaldefs[i].s_name));
2778 Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->numglobals, numculled, prog->numglobals * 4);
2788 void PRVM_Global_f(void)
2791 if( Cmd_Argc() != 3 ) {
2792 Con_Printf( "prvm_global <program name> <global name>\n" );
2797 if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
2800 global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
2802 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2804 Con_Printf( "%s: %s\n", Cmd_Argv(2), PRVM_ValueString( (etype_t)global->type, PRVM_GLOBALFIELDVALUE(global->ofs) ) );
2813 void PRVM_GlobalSet_f(void)
2816 if( Cmd_Argc() != 4 ) {
2817 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2822 if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
2825 global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
2827 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2829 PRVM_ED_ParseEpair( NULL, global, Cmd_Argv(3), true );
2838 void PRVM_Init (void)
2840 Cmd_AddCommand ("prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2841 Cmd_AddCommand ("prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2842 Cmd_AddCommand ("prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2843 Cmd_AddCommand ("prvm_profile", PRVM_Profile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu)");
2844 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");
2845 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)");
2846 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)");
2847 Cmd_AddCommand ("prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2848 Cmd_AddCommand ("prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2849 Cmd_AddCommand ("prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2850 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)");
2851 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");
2852 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");
2853 Cmd_AddCommand ("prvm_printfunction", PRVM_PrintFunction_f, "prints a disassembly (QuakeC instructions) of the specified function in the selected VM (server, client, menu)");
2854 Cmd_AddCommand ("cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2855 Cmd_AddCommand ("menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2856 Cmd_AddCommand ("sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2858 Cvar_RegisterVariable (&prvm_language);
2859 Cvar_RegisterVariable (&prvm_traceqc);
2860 Cvar_RegisterVariable (&prvm_statementprofiling);
2861 Cvar_RegisterVariable (&prvm_timeprofiling);
2862 Cvar_RegisterVariable (&prvm_backtraceforwarnings);
2863 Cvar_RegisterVariable (&prvm_leaktest);
2864 Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames);
2865 Cvar_RegisterVariable (&prvm_errordump);
2866 Cvar_RegisterVariable (&prvm_reuseedicts_startuptime);
2867 Cvar_RegisterVariable (&prvm_reuseedicts_neverinsameframe);
2869 // COMMANDLINEOPTION: PRVM: -norunaway disables the runaway loop check (it might be impossible to exit DarkPlaces if used!)
2870 prvm_runawaycheck = !COM_CheckParm("-norunaway");
2880 void PRVM_InitProg(int prognr)
2882 static unsigned int progid = 0;
2884 if(prognr < 0 || prognr >= PRVM_MAXPROGS)
2885 Sys_Error("PRVM_InitProg: Invalid program number %i",prognr);
2887 prog = &prog_list[prognr];
2892 memset(prog, 0, sizeof(prvm_prog_t));
2893 prog->starttime = Sys_DoubleTime();
2894 prog->id = ++progid;
2896 prog->error_cmd = Host_Error;
2897 prog->leaktest_active = prvm_leaktest.integer != 0;
2900 int PRVM_GetProgNr(void)
2902 return prog - prog_list;
2905 void *_PRVM_Alloc(size_t buffersize, const char *filename, int fileline)
2907 return _Mem_Alloc(prog->progs_mempool, NULL, buffersize, 16, filename, fileline);
2910 void _PRVM_Free(void *buffer, const char *filename, int fileline)
2912 _Mem_Free(buffer, filename, fileline);
2915 void _PRVM_FreeAll(const char *filename, int fileline)
2917 prog->functions = NULL;
2918 prog->strings = NULL;
2919 prog->fielddefs = NULL;
2920 prog->globaldefs = NULL;
2921 prog->statements = NULL;
2922 // FIXME: what about knownstrings?
2923 _Mem_EmptyPool(prog->progs_mempool, filename, fileline);
2926 // LordHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
2927 unsigned int PRVM_EDICT_NUM_ERROR(unsigned int n, const char *filename, int fileline)
2929 PRVM_ERROR ("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", PRVM_NAME, n, filename, fileline);
2933 sizebuf_t vm_tempstringsbuf;
2934 #define PRVM_KNOWNSTRINGBASE 0x40000000
2936 const char *PRVM_GetString(int num)
2941 VM_Warning("PRVM_GetString: Invalid string offset (%i < 0)\n", num);
2944 else if (num < prog->stringssize)
2946 // constant string from progs.dat
2947 return prog->strings + num;
2949 else if (num <= prog->stringssize + vm_tempstringsbuf.maxsize)
2951 // tempstring returned by engine to QC (becomes invalid after returning to engine)
2952 num -= prog->stringssize;
2953 if (num < vm_tempstringsbuf.cursize)
2954 return (char *)vm_tempstringsbuf.data + num;
2957 VM_Warning("PRVM_GetString: Invalid temp-string offset (%i >= %i vm_tempstringsbuf.cursize)\n", num, vm_tempstringsbuf.cursize);
2961 else if (num & PRVM_KNOWNSTRINGBASE)
2964 num = num - PRVM_KNOWNSTRINGBASE;
2965 if (num >= 0 && num < prog->numknownstrings)
2967 if (!prog->knownstrings[num])
2969 VM_Warning("PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
2972 return prog->knownstrings[num];
2976 VM_Warning("PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
2982 // invalid string offset
2983 VM_Warning("PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
2988 const char *PRVM_ChangeEngineString(int i, const char *s)
2991 i = i - PRVM_KNOWNSTRINGBASE;
2992 if(i < 0 || i >= prog->numknownstrings)
2993 PRVM_ERROR("PRVM_ChangeEngineString: s is not an engine string");
2994 old = prog->knownstrings[i];
2995 prog->knownstrings[i] = s;
2999 int PRVM_SetEngineString(const char *s)
3004 if (s >= prog->strings && s <= prog->strings + prog->stringssize)
3005 PRVM_ERROR("PRVM_SetEngineString: s in prog->strings area");
3006 // if it's in the tempstrings area, use a reserved range
3007 // (otherwise we'd get millions of useless string offsets cluttering the database)
3008 if (s >= (char *)vm_tempstringsbuf.data && s < (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.maxsize)
3010 return prog->stringssize + (s - (char *)vm_tempstringsbuf.data);
3012 // see if it's a known string address
3013 for (i = 0;i < prog->numknownstrings;i++)
3014 if (prog->knownstrings[i] == s)
3015 return PRVM_KNOWNSTRINGBASE + i;
3016 // new unknown engine string
3017 if (developer_insane.integer)
3018 Con_DPrintf("new engine string %p = \"%s\"\n", s, s);
3019 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3020 if (!prog->knownstrings[i])
3022 if (i >= prog->numknownstrings)
3024 if (i >= prog->maxknownstrings)
3026 const char **oldstrings = prog->knownstrings;
3027 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
3028 const char **oldstrings_origin = prog->knownstrings_origin;
3029 prog->maxknownstrings += 128;
3030 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3031 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
3032 if(prog->leaktest_active)
3033 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3034 if (prog->numknownstrings)
3036 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
3037 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
3038 if(prog->leaktest_active)
3039 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
3042 prog->numknownstrings++;
3044 prog->firstfreeknownstring = i + 1;
3045 prog->knownstrings[i] = s;
3046 prog->knownstrings_freeable[i] = false;
3047 if(prog->leaktest_active)
3048 prog->knownstrings_origin[i] = NULL;
3049 return PRVM_KNOWNSTRINGBASE + i;
3052 // temp string handling
3054 // all tempstrings go into this buffer consecutively, and it is reset
3055 // whenever PRVM_ExecuteProgram returns to the engine
3056 // (technically each PRVM_ExecuteProgram call saves the cursize value and
3057 // restores it on return, so multiple recursive calls can share the same
3059 // the buffer size is automatically grown as needed
3061 int PRVM_SetTempString(const char *s)
3067 size = (int)strlen(s) + 1;
3068 if (developer_insane.integer)
3069 Con_DPrintf("PRVM_SetTempString: cursize %i, size %i\n", vm_tempstringsbuf.cursize, size);
3070 if (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
3072 sizebuf_t old = vm_tempstringsbuf;
3073 if (vm_tempstringsbuf.cursize + size >= 1<<28)
3074 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);
3075 vm_tempstringsbuf.maxsize = max(vm_tempstringsbuf.maxsize, 65536);
3076 while (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
3077 vm_tempstringsbuf.maxsize *= 2;
3078 if (vm_tempstringsbuf.maxsize != old.maxsize || vm_tempstringsbuf.data == NULL)
3080 Con_DPrintf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, vm_tempstringsbuf.maxsize/1024);
3081 vm_tempstringsbuf.data = (unsigned char *) Mem_Alloc(sv_mempool, vm_tempstringsbuf.maxsize);
3083 memcpy(vm_tempstringsbuf.data, old.data, old.cursize);
3088 t = (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.cursize;
3090 vm_tempstringsbuf.cursize += size;
3091 return PRVM_SetEngineString(t);
3094 int PRVM_AllocString(size_t bufferlength, char **pointer)
3099 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3100 if (!prog->knownstrings[i])
3102 if (i >= prog->numknownstrings)
3104 if (i >= prog->maxknownstrings)
3106 const char **oldstrings = prog->knownstrings;
3107 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
3108 const char **oldstrings_origin = prog->knownstrings_origin;
3109 prog->maxknownstrings += 128;
3110 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3111 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
3112 if(prog->leaktest_active)
3113 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3114 if (prog->numknownstrings)
3116 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
3117 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
3118 if(prog->leaktest_active)
3119 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
3122 Mem_Free((char **)oldstrings);
3123 if (oldstrings_freeable)
3124 Mem_Free((unsigned char *)oldstrings_freeable);
3125 if (oldstrings_origin)
3126 Mem_Free((char **)oldstrings_origin);
3128 prog->numknownstrings++;
3130 prog->firstfreeknownstring = i + 1;
3131 prog->knownstrings[i] = (char *)PRVM_Alloc(bufferlength);
3132 prog->knownstrings_freeable[i] = true;
3133 if(prog->leaktest_active)
3134 prog->knownstrings_origin[i] = PRVM_AllocationOrigin();
3136 *pointer = (char *)(prog->knownstrings[i]);
3137 return PRVM_KNOWNSTRINGBASE + i;
3140 void PRVM_FreeString(int num)
3143 PRVM_ERROR("PRVM_FreeString: attempt to free a NULL string");
3144 else if (num >= 0 && num < prog->stringssize)
3145 PRVM_ERROR("PRVM_FreeString: attempt to free a constant string");
3146 else if (num >= PRVM_KNOWNSTRINGBASE && num < PRVM_KNOWNSTRINGBASE + prog->numknownstrings)
3148 num = num - PRVM_KNOWNSTRINGBASE;
3149 if (!prog->knownstrings[num])
3150 PRVM_ERROR("PRVM_FreeString: attempt to free a non-existent or already freed string");
3151 if (!prog->knownstrings_freeable[num])
3152 PRVM_ERROR("PRVM_FreeString: attempt to free a string owned by the engine");
3153 PRVM_Free((char *)prog->knownstrings[num]);
3154 if(prog->leaktest_active)
3155 if(prog->knownstrings_origin[num])
3156 PRVM_Free((char *)prog->knownstrings_origin[num]);
3157 prog->knownstrings[num] = NULL;
3158 prog->knownstrings_freeable[num] = false;
3159 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
3162 PRVM_ERROR("PRVM_FreeString: invalid string offset %i", num);
3165 static qboolean PRVM_IsStringReferenced(string_t string)
3169 for (i = 0;i < prog->numglobaldefs;i++)
3171 ddef_t *d = &prog->globaldefs[i];
3172 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3174 if(string == PRVM_GLOBALFIELDSTRING(d->ofs))
3178 for(j = 0; j < prog->num_edicts; ++j)
3180 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3181 if (ed->priv.required->free)
3183 for (i=0; i<prog->numfielddefs; ++i)
3185 ddef_t *d = &prog->fielddefs[i];
3186 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3188 if(string == PRVM_EDICTFIELDSTRING(ed, d->ofs))
3196 static qboolean PRVM_IsEdictRelevant(prvm_edict_t *edict)
3198 if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
3199 return true; // world or clients
3200 switch(prog - prog_list)
3202 case PRVM_SERVERPROG:
3204 entvars_t *ev = edict->fields.server;
3205 if(ev->solid) // can block other stuff, or is a trigger?
3207 if(ev->modelindex) // visible ent?
3209 if(ev->effects) // particle effect?
3211 if(ev->think) // has a think function?
3212 if(ev->nextthink > 0) // that actually will eventually run?
3216 if(*prvm_leaktest_ignore_classnames.string)
3218 if(strstr(va(" %s ", prvm_leaktest_ignore_classnames.string), va(" %s ", PRVM_GetString(ev->classname))))
3223 case PRVM_CLIENTPROG:
3225 // TODO someone add more stuff here
3226 cl_entvars_t *ev = edict->fields.client;
3227 if(ev->entnum) // csqc networked
3229 if(ev->modelindex) // visible ent?
3231 if(ev->effects) // particle effect?
3233 if(ev->think) // has a think function?
3234 if(ev->nextthink > 0) // that actually will eventually run?
3236 if(*prvm_leaktest_ignore_classnames.string)
3238 if(strstr(va(" %s ", prvm_leaktest_ignore_classnames.string), va(" %s ", PRVM_GetString(ev->classname))))
3244 // menu prog does not have classnames
3250 static qboolean PRVM_IsEdictReferenced(prvm_edict_t *edict, int mark)
3253 int edictnum = PRVM_NUM_FOR_EDICT(edict);
3254 const char *targetname = NULL;
3256 switch(prog - prog_list)
3258 case PRVM_SERVERPROG:
3259 targetname = PRVM_GetString(edict->fields.server->targetname);
3264 if(!*targetname) // ""
3267 for (i = 0;i < prog->numglobaldefs;i++)
3269 ddef_t *d = &prog->globaldefs[i];
3270 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3272 if(edictnum == PRVM_GLOBALFIELDEDICT(d->ofs))
3276 for(j = 0; j < prog->num_edicts; ++j)
3278 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3279 if (ed->priv.required->mark < mark)
3285 const char *target = PRVM_GetString(ed->fields.server->target);
3287 if(!strcmp(target, targetname))
3290 for (i=0; i<prog->numfielddefs; ++i)
3292 ddef_t *d = &prog->fielddefs[i];
3293 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3295 if(edictnum == PRVM_EDICTFIELDEDICT(ed, d->ofs))
3303 static void PRVM_MarkReferencedEdicts(void)
3309 for(j = 0; j < prog->num_edicts; ++j)
3311 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3312 if(ed->priv.required->free)
3314 ed->priv.required->mark = PRVM_IsEdictRelevant(ed) ? 1 : 0;
3321 for(j = 0; j < prog->num_edicts; ++j)
3323 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3324 if(ed->priv.required->free)
3326 if(ed->priv.required->mark)
3328 if(PRVM_IsEdictReferenced(ed, stage))
3330 ed->priv.required->mark = stage + 1;
3337 Con_DPrintf("leak check used %d stages to find all references\n", stage);
3340 void PRVM_LeakTest(void)
3343 qboolean leaked = false;
3345 if(!prog->leaktest_active)
3349 for (i = 0; i < prog->numknownstrings; ++i)
3351 if(prog->knownstrings[i])
3352 if(prog->knownstrings_freeable[i])
3353 if(prog->knownstrings_origin[i])
3354 if(!PRVM_IsStringReferenced(PRVM_KNOWNSTRINGBASE + i))
3356 Con_Printf("Unreferenced string found!\n Value: %s\n Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
3362 PRVM_MarkReferencedEdicts();
3363 for(j = 0; j < prog->num_edicts; ++j)
3365 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3366 if(ed->priv.required->free)
3368 if(!ed->priv.required->mark)
3369 if(ed->priv.required->allocation_origin)
3371 Con_Printf("Unreferenced edict found!\n Allocated at: %s\n", ed->priv.required->allocation_origin);
3372 PRVM_ED_Print(ed, NULL);
3378 for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
3380 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
3382 if(stringbuffer->origin)
3384 Con_Printf("Open string buffer handle found!\n Allocated at: %s\n", stringbuffer->origin);
3389 for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
3391 if(prog->openfiles[i])
3392 if(prog->openfiles_origin[i])
3394 Con_Printf("Open file handle found!\n Allocated at: %s\n", prog->openfiles_origin[i]);
3399 for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
3401 if(prog->opensearches[i])
3402 if(prog->opensearches_origin[i])
3404 Con_Printf("Open search handle found!\n Allocated at: %s\n", prog->opensearches_origin[i]);
3410 Con_Printf("Congratulations. No leaks found.\n");