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, true))
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, 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(!PRVM_allfunction(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 (PRVM_allfunction(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, true))
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, true))
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, true))
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 (PRVM_serverfunction(SV_OnEntityPreSpawnFunction))
1451 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1452 PRVM_ExecuteProgram (PRVM_serverfunction(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 (PRVM_serverfunction(SV_OnEntityNoSpawnFunction))
1487 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1488 PRVM_ExecuteProgram (PRVM_serverfunction(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 (PRVM_serverfunction(SV_OnEntityPostSpawnFunction))
1513 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1514 PRVM_ExecuteProgram (PRVM_serverfunction(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 // function searches use 0 for NULL
1533 memset(&prog->funcoffsets, 0, sizeof(prog->funcoffsets));
1534 #define PRVM_DECLARE_serverglobalfloat(x)
1535 #define PRVM_DECLARE_serverglobalvector(x)
1536 #define PRVM_DECLARE_serverglobalstring(x)
1537 #define PRVM_DECLARE_serverglobaledict(x)
1538 #define PRVM_DECLARE_serverglobalfunction(x)
1539 #define PRVM_DECLARE_clientglobalfloat(x)
1540 #define PRVM_DECLARE_clientglobalvector(x)
1541 #define PRVM_DECLARE_clientglobalstring(x)
1542 #define PRVM_DECLARE_clientglobaledict(x)
1543 #define PRVM_DECLARE_clientglobalfunction(x)
1544 #define PRVM_DECLARE_menuglobalfloat(x)
1545 #define PRVM_DECLARE_menuglobalvector(x)
1546 #define PRVM_DECLARE_menuglobalstring(x)
1547 #define PRVM_DECLARE_menuglobaledict(x)
1548 #define PRVM_DECLARE_menuglobalfunction(x)
1549 #define PRVM_DECLARE_serverfieldfloat(x)
1550 #define PRVM_DECLARE_serverfieldvector(x)
1551 #define PRVM_DECLARE_serverfieldstring(x)
1552 #define PRVM_DECLARE_serverfieldedict(x)
1553 #define PRVM_DECLARE_serverfieldfunction(x)
1554 #define PRVM_DECLARE_clientfieldfloat(x)
1555 #define PRVM_DECLARE_clientfieldvector(x)
1556 #define PRVM_DECLARE_clientfieldstring(x)
1557 #define PRVM_DECLARE_clientfieldedict(x)
1558 #define PRVM_DECLARE_clientfieldfunction(x)
1559 #define PRVM_DECLARE_menufieldfloat(x)
1560 #define PRVM_DECLARE_menufieldvector(x)
1561 #define PRVM_DECLARE_menufieldstring(x)
1562 #define PRVM_DECLARE_menufieldedict(x)
1563 #define PRVM_DECLARE_menufieldfunction(x)
1564 #define PRVM_DECLARE_serverfunction(x)
1565 #define PRVM_DECLARE_clientfunction(x)
1566 #define PRVM_DECLARE_menufunction(x)
1567 #define PRVM_DECLARE_field(x) prog->fieldoffsets.x = PRVM_ED_FindFieldOffset(#x);
1568 #define PRVM_DECLARE_global(x) prog->globaloffsets.x = PRVM_ED_FindGlobalOffset(#x);
1569 #define PRVM_DECLARE_function(x) prog->funcoffsets.x = PRVM_ED_FindFunctionOffset(#x);
1570 #include "prvm_offsets.h"
1571 #undef PRVM_DECLARE_serverglobalfloat
1572 #undef PRVM_DECLARE_serverglobalvector
1573 #undef PRVM_DECLARE_serverglobalstring
1574 #undef PRVM_DECLARE_serverglobaledict
1575 #undef PRVM_DECLARE_serverglobalfunction
1576 #undef PRVM_DECLARE_clientglobalfloat
1577 #undef PRVM_DECLARE_clientglobalvector
1578 #undef PRVM_DECLARE_clientglobalstring
1579 #undef PRVM_DECLARE_clientglobaledict
1580 #undef PRVM_DECLARE_clientglobalfunction
1581 #undef PRVM_DECLARE_menuglobalfloat
1582 #undef PRVM_DECLARE_menuglobalvector
1583 #undef PRVM_DECLARE_menuglobalstring
1584 #undef PRVM_DECLARE_menuglobaledict
1585 #undef PRVM_DECLARE_menuglobalfunction
1586 #undef PRVM_DECLARE_serverfieldfloat
1587 #undef PRVM_DECLARE_serverfieldvector
1588 #undef PRVM_DECLARE_serverfieldstring
1589 #undef PRVM_DECLARE_serverfieldedict
1590 #undef PRVM_DECLARE_serverfieldfunction
1591 #undef PRVM_DECLARE_clientfieldfloat
1592 #undef PRVM_DECLARE_clientfieldvector
1593 #undef PRVM_DECLARE_clientfieldstring
1594 #undef PRVM_DECLARE_clientfieldedict
1595 #undef PRVM_DECLARE_clientfieldfunction
1596 #undef PRVM_DECLARE_menufieldfloat
1597 #undef PRVM_DECLARE_menufieldvector
1598 #undef PRVM_DECLARE_menufieldstring
1599 #undef PRVM_DECLARE_menufieldedict
1600 #undef PRVM_DECLARE_menufieldfunction
1601 #undef PRVM_DECLARE_serverfunction
1602 #undef PRVM_DECLARE_clientfunction
1603 #undef PRVM_DECLARE_menufunction
1604 #undef PRVM_DECLARE_field
1605 #undef PRVM_DECLARE_global
1606 #undef PRVM_DECLARE_function
1611 typedef struct dpfield_s
1618 #define DPFIELDS (sizeof(dpfields) / sizeof(dpfield_t))
1620 dpfield_t dpfields[] =
1631 #define PO_HASHSIZE 16384
1632 typedef struct po_string_s
1635 struct po_string_s *nextonhashchain;
1640 po_string_t *hashtable[PO_HASHSIZE];
1643 void PRVM_PO_UnparseString(char *out, const char *in, size_t outsize)
1652 case '\a': if(outsize >= 2) { *out++ = '\\'; *out++ = 'a'; outsize -= 2; } break;
1653 case '\b': if(outsize >= 2) { *out++ = '\\'; *out++ = 'b'; outsize -= 2; } break;
1654 case '\t': if(outsize >= 2) { *out++ = '\\'; *out++ = 't'; outsize -= 2; } break;
1655 case '\r': if(outsize >= 2) { *out++ = '\\'; *out++ = 'r'; outsize -= 2; } break;
1656 case '\n': if(outsize >= 2) { *out++ = '\\'; *out++ = 'n'; outsize -= 2; } break;
1657 case '\\': if(outsize >= 2) { *out++ = '\\'; *out++ = '\\'; outsize -= 2; } break;
1658 case '"': if(outsize >= 2) { *out++ = '\\'; *out++ = '"'; outsize -= 2; } break;
1660 if(*in >= 0 && *in <= 0x1F)
1665 *out++ = '0' + ((*in & 0700) >> 6);
1666 *out++ = '0' + ((*in & 0070) >> 3);
1667 *out++ = '0' + ((*in & 0007));
1684 void PRVM_PO_ParseString(char *out, const char *in, size_t outsize)
1697 case 'a': if(outsize > 0) { *out++ = '\a'; --outsize; } break;
1698 case 'b': if(outsize > 0) { *out++ = '\b'; --outsize; } break;
1699 case 't': if(outsize > 0) { *out++ = '\t'; --outsize; } break;
1700 case 'r': if(outsize > 0) { *out++ = '\r'; --outsize; } break;
1701 case 'n': if(outsize > 0) { *out++ = '\n'; --outsize; } break;
1702 case '\\': if(outsize > 0) { *out++ = '\\'; --outsize; } break;
1703 case '"': if(outsize > 0) { *out++ = '"'; --outsize; } break;
1704 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
1708 if(*in >= '0' && *in <= '7')
1711 *out = (*out << 3) | (*in - '0');
1714 if(*in >= '0' && *in <= '7')
1717 *out = (*out << 3) | (*in - '0');
1728 if(outsize > 0) { *out++ = *in; --outsize; }
1743 po_t *PRVM_PO_Load(const char *filename, mempool_t *pool)
1748 char inbuf[MAX_INPUTLINE];
1749 char decodedbuf[MAX_INPUTLINE];
1752 po_string_t thisstr;
1753 const char *buf = (const char *) FS_LoadFile(filename, pool, true, NULL);
1758 memset(&thisstr, 0, sizeof(thisstr)); // hush compiler warning
1760 po = (po_t *)Mem_Alloc(pool, sizeof(*po));
1761 memset(po, 0, sizeof(*po));
1769 p = strchr(p, '\n');
1775 if(*p == '\r' || *p == '\n')
1780 if(!strncmp(p, "msgid \"", 7))
1785 else if(!strncmp(p, "msgstr \"", 8))
1792 p = strchr(p, '\n');
1802 q = strchr(p, '\n');
1809 if((size_t)(q - p) >= (size_t) sizeof(inbuf))
1811 strlcpy(inbuf, p, q - p); // not - 1, because this adds a NUL
1812 PRVM_PO_ParseString(decodedbuf + decodedpos, inbuf, sizeof(decodedbuf) - decodedpos);
1813 decodedpos += strlen(decodedbuf + decodedpos);
1823 Mem_Free(thisstr.key);
1824 thisstr.key = (char *)Mem_Alloc(pool, decodedpos + 1);
1825 memcpy(thisstr.key, decodedbuf, decodedpos + 1);
1827 else if(decodedpos > 0 && thisstr.key) // skip empty translation results
1829 thisstr.value = (char *)Mem_Alloc(pool, decodedpos + 1);
1830 memcpy(thisstr.value, decodedbuf, decodedpos + 1);
1831 hashindex = CRC_Block((const unsigned char *) thisstr.key, strlen(thisstr.key)) % PO_HASHSIZE;
1832 thisstr.nextonhashchain = po->hashtable[hashindex];
1833 po->hashtable[hashindex] = (po_string_t *)Mem_Alloc(pool, sizeof(thisstr));
1834 memcpy(po->hashtable[hashindex], &thisstr, sizeof(thisstr));
1835 memset(&thisstr, 0, sizeof(thisstr));
1839 Mem_Free((char *) buf);
1842 const char *PRVM_PO_Lookup(po_t *po, const char *str)
1844 int hashindex = CRC_Block((const unsigned char *) str, strlen(str)) % PO_HASHSIZE;
1845 po_string_t *p = po->hashtable[hashindex];
1848 if(!strcmp(str, p->key))
1850 p = p->nextonhashchain;
1854 void PRVM_PO_Destroy(po_t *po)
1857 for(i = 0; i < PO_HASHSIZE; ++i)
1859 po_string_t *p = po->hashtable[i];
1863 p = p->nextonhashchain;
1872 void PRVM_LeakTest(void);
1873 void PRVM_ResetProg(void)
1876 PRVM_GCALL(reset_cmd)();
1877 Mem_FreePool(&prog->progs_mempool);
1879 PRVM_PO_Destroy((po_t *) prog->po);
1880 memset(prog,0,sizeof(prvm_prog_t));
1881 prog->starttime = Sys_DoubleTime();
1889 void PRVM_LoadLNO( const char *progname ) {
1890 fs_offset_t filesize;
1892 unsigned int *header;
1895 FS_StripExtension( progname, filename, sizeof( filename ) );
1896 strlcat( filename, ".lno", sizeof( filename ) );
1898 lno = FS_LoadFile( filename, tempmempool, false, &filesize );
1904 <Spike> SafeWrite (h, &lnotype, sizeof(int));
1905 <Spike> SafeWrite (h, &version, sizeof(int));
1906 <Spike> SafeWrite (h, &numglobaldefs, sizeof(int));
1907 <Spike> SafeWrite (h, &numpr_globals, sizeof(int));
1908 <Spike> SafeWrite (h, &numfielddefs, sizeof(int));
1909 <Spike> SafeWrite (h, &numstatements, sizeof(int));
1910 <Spike> SafeWrite (h, statement_linenums, numstatements*sizeof(int));
1912 if ((unsigned int)filesize < (6 + prog->progs_numstatements) * sizeof(int))
1918 header = (unsigned int *) lno;
1919 if( header[ 0 ] == *(unsigned int *) "LNOF" &&
1920 LittleLong( header[ 1 ] ) == 1 &&
1921 (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs_numglobaldefs &&
1922 (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs_numglobals &&
1923 (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs_numfielddefs &&
1924 (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs_numstatements )
1926 prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof( int ) );
1927 memcpy( prog->statement_linenums, (int *) lno + 6, prog->progs_numstatements * sizeof( int ) );
1937 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)
1940 dprograms_t *dprograms;
1941 dstatement_t *instatements;
1942 ddef_t *infielddefs;
1943 ddef_t *inglobaldefs;
1945 dfunction_t *infunctions;
1947 fs_offset_t filesize;
1948 int requiredglobalspace;
1955 PRVM_ERROR ("PRVM_LoadProgs: there is already a %s program loaded!", PRVM_NAME );
1957 Host_LockSession(); // all progs can use the session cvar
1958 Crypto_LoadKeys(); // all progs might use the keys at init time
1960 dprograms = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
1961 if (dprograms == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
1962 PRVM_ERROR ("PRVM_LoadProgs: couldn't load %s for %s", filename, PRVM_NAME);
1963 // TODO bounds check header fields (e.g. numstatements), they must never go behind end of file
1965 Con_DPrintf("%s programs occupy %iK.\n", PRVM_NAME, (int)(filesize/1024));
1967 requiredglobalspace = 0;
1968 for (i = 0;i < numrequiredglobals;i++)
1969 requiredglobalspace += required_global[i].type == ev_vector ? 3 : 1;
1971 prog->filecrc = CRC_Block((unsigned char *)dprograms, filesize);
1973 // byte swap the header
1974 prog->progs_version = LittleLong(dprograms->version);
1975 prog->progs_crc = LittleLong(dprograms->crc);
1976 if (prog->progs_version != PROG_VERSION)
1977 PRVM_ERROR ("%s: %s has wrong version number (%i should be %i)", PRVM_NAME, filename, prog->progs_version, PROG_VERSION);
1978 instatements = (dstatement_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_statements));
1979 prog->progs_numstatements = LittleLong(dprograms->numstatements);
1980 inglobaldefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globaldefs));
1981 prog->progs_numglobaldefs = LittleLong(dprograms->numglobaldefs);
1982 infielddefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_fielddefs));
1983 prog->progs_numfielddefs = LittleLong(dprograms->numfielddefs);
1984 infunctions = (dfunction_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_functions));
1985 prog->progs_numfunctions = LittleLong(dprograms->numfunctions);
1986 instrings = (char *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_strings));
1987 prog->progs_numstrings = LittleLong(dprograms->numstrings);
1988 inglobals = (float *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globals));
1989 prog->progs_numglobals = LittleLong(dprograms->numglobals);
1990 prog->progs_entityfields = LittleLong(dprograms->entityfields);
1992 prog->numstatements = prog->progs_numstatements;
1993 prog->numglobaldefs = prog->progs_numglobaldefs;
1994 prog->numfielddefs = prog->progs_numfielddefs;
1995 prog->numfunctions = prog->progs_numfunctions;
1996 prog->numstrings = prog->progs_numstrings;
1997 prog->numglobals = prog->progs_numglobals;
1998 prog->entityfields = prog->progs_entityfields;
2000 if (LittleLong(dprograms->ofs_strings) + prog->progs_numstrings >= (int)filesize)
2001 PRVM_ERROR ("%s: %s strings go past end of file", PRVM_NAME, filename);
2002 prog->strings = (char *)Mem_Alloc(prog->progs_mempool, prog->progs_numstrings);
2003 memcpy(prog->strings, instrings, prog->progs_numstrings);
2004 prog->stringssize = prog->progs_numstrings;
2006 prog->numknownstrings = 0;
2007 prog->maxknownstrings = 0;
2008 prog->knownstrings = NULL;
2009 prog->knownstrings_freeable = NULL;
2011 Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
2013 // we need to expand the globaldefs and fielddefs to include engine defs
2014 prog->globaldefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobaldefs + numrequiredglobals) * sizeof(ddef_t));
2015 prog->globals.generic = (float *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobals + requiredglobalspace) * sizeof(float));
2016 prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numfielddefs + numrequiredfields) * sizeof(ddef_t));
2017 // we need to convert the statements to our memory format
2018 prog->statements = (mstatement_t *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(mstatement_t));
2019 // allocate space for profiling statement usage
2020 prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(*prog->statement_profile));
2021 // functions need to be converted to the memory format
2022 prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs_numfunctions);
2024 for (i = 0;i < prog->progs_numfunctions;i++)
2026 prog->functions[i].first_statement = LittleLong(infunctions[i].first_statement);
2027 prog->functions[i].parm_start = LittleLong(infunctions[i].parm_start);
2028 prog->functions[i].s_name = LittleLong(infunctions[i].s_name);
2029 prog->functions[i].s_file = LittleLong(infunctions[i].s_file);
2030 prog->functions[i].numparms = LittleLong(infunctions[i].numparms);
2031 prog->functions[i].locals = LittleLong(infunctions[i].locals);
2032 memcpy(prog->functions[i].parm_size, infunctions[i].parm_size, sizeof(infunctions[i].parm_size));
2033 if(prog->functions[i].first_statement >= prog->numstatements)
2034 PRVM_ERROR("PRVM_LoadProgs: out of bounds function statement (function %d) in %s", i, PRVM_NAME);
2035 // TODO bounds check parm_start, s_name, s_file, numparms, locals, parm_size
2038 // copy the globaldefs to the new globaldefs list
2039 for (i=0 ; i<prog->numglobaldefs ; i++)
2041 prog->globaldefs[i].type = LittleShort(inglobaldefs[i].type);
2042 prog->globaldefs[i].ofs = LittleShort(inglobaldefs[i].ofs);
2043 prog->globaldefs[i].s_name = LittleLong(inglobaldefs[i].s_name);
2044 // TODO bounds check ofs, s_name
2047 // append the required globals
2048 for (i = 0;i < numrequiredglobals;i++)
2050 prog->globaldefs[prog->numglobaldefs].type = required_global[i].type;
2051 prog->globaldefs[prog->numglobaldefs].ofs = prog->numglobals;
2052 prog->globaldefs[prog->numglobaldefs].s_name = PRVM_SetEngineString(required_global[i].name);
2053 if (prog->globaldefs[prog->numglobaldefs].type == ev_vector)
2054 prog->numglobals += 3;
2057 prog->numglobaldefs++;
2060 // copy the progs fields to the new fields list
2061 for (i = 0;i < prog->numfielddefs;i++)
2063 prog->fielddefs[i].type = LittleShort(infielddefs[i].type);
2064 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
2065 PRVM_ERROR ("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", PRVM_NAME);
2066 prog->fielddefs[i].ofs = LittleShort(infielddefs[i].ofs);
2067 prog->fielddefs[i].s_name = LittleLong(infielddefs[i].s_name);
2068 // TODO bounds check ofs, s_name
2071 // append the required fields
2072 for (i = 0;i < numrequiredfields;i++)
2074 prog->fielddefs[prog->numfielddefs].type = required_field[i].type;
2075 prog->fielddefs[prog->numfielddefs].ofs = prog->entityfields;
2076 prog->fielddefs[prog->numfielddefs].s_name = PRVM_SetEngineString(required_field[i].name);
2077 if (prog->fielddefs[prog->numfielddefs].type == ev_vector)
2078 prog->entityfields += 3;
2080 prog->entityfields++;
2081 prog->numfielddefs++;
2084 // LordHavoc: TODO: reorder globals to match engine struct
2085 // LordHavoc: TODO: reorder fields to match engine struct
2086 #define remapglobal(index) (index)
2087 #define remapfield(index) (index)
2090 for (i = 0;i < prog->progs_numglobals;i++)
2091 ((int *)prog->globals.generic)[remapglobal(i)] = LittleLong(((int *)inglobals)[i]);
2093 // LordHavoc: TODO: support 32bit progs statement formats
2094 // copy, remap globals in statements, bounds check
2095 for (i = 0;i < prog->progs_numstatements;i++)
2097 op = (opcode_t)LittleShort(instatements[i].op);
2098 a = (unsigned short)LittleShort(instatements[i].a);
2099 b = (unsigned short)LittleShort(instatements[i].b);
2100 c = (unsigned short)LittleShort(instatements[i].c);
2106 if (a >= prog->progs_numglobals || b + i < 0 || b + i >= prog->progs_numstatements)
2107 PRVM_ERROR("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", i, PRVM_NAME);
2108 prog->statements[i].op = op;
2109 prog->statements[i].operand[0] = remapglobal(a);
2110 prog->statements[i].operand[1] = -1;
2111 prog->statements[i].operand[2] = -1;
2112 prog->statements[i].jumpabsolute = i + b;
2116 if (a + i < 0 || a + i >= prog->progs_numstatements)
2117 PRVM_ERROR("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, PRVM_NAME);
2118 prog->statements[i].op = op;
2119 prog->statements[i].operand[0] = -1;
2120 prog->statements[i].operand[1] = -1;
2121 prog->statements[i].operand[2] = -1;
2122 prog->statements[i].jumpabsolute = i + a;
2125 Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", (int)op, i, PRVM_NAME);
2126 // global global global
2161 if (a >= prog->progs_numglobals || b >= prog->progs_numglobals || c >= prog->progs_numglobals)
2162 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
2163 prog->statements[i].op = op;
2164 prog->statements[i].operand[0] = remapglobal(a);
2165 prog->statements[i].operand[1] = remapglobal(b);
2166 prog->statements[i].operand[2] = remapglobal(c);
2167 prog->statements[i].jumpabsolute = -1;
2169 // global none global
2175 if (a >= prog->progs_numglobals || c >= prog->progs_numglobals)
2176 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
2177 prog->statements[i].op = op;
2178 prog->statements[i].operand[0] = remapglobal(a);
2179 prog->statements[i].operand[1] = -1;
2180 prog->statements[i].operand[2] = remapglobal(c);
2181 prog->statements[i].jumpabsolute = -1;
2197 if (a >= prog->progs_numglobals || b >= prog->progs_numglobals)
2198 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
2199 prog->statements[i].op = op;
2200 prog->statements[i].operand[0] = remapglobal(a);
2201 prog->statements[i].operand[1] = remapglobal(b);
2202 prog->statements[i].operand[2] = -1;
2203 prog->statements[i].jumpabsolute = -1;
2217 if ( a >= prog->progs_numglobals)
2218 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
2219 prog->statements[i].op = op;
2220 prog->statements[i].operand[0] = remapglobal(a);
2221 prog->statements[i].operand[1] = -1;
2222 prog->statements[i].operand[2] = -1;
2223 prog->statements[i].jumpabsolute = -1;
2227 if(prog->numstatements < 1)
2229 PRVM_ERROR("PRVM_LoadProgs: empty program in %s", PRVM_NAME);
2231 else switch(prog->statements[prog->numstatements - 1].op)
2238 PRVM_ERROR("PRVM_LoadProgs: program may fall off the edge (does not end with RETURN, GOTO or DONE) in %s", PRVM_NAME);
2242 // we're done with the file now
2243 Mem_Free(dprograms);
2246 // check required functions
2247 for(i=0 ; i < numrequiredfunc ; i++)
2248 if(PRVM_ED_FindFunction(required_func[i]) == 0)
2249 PRVM_ERROR("%s: %s not found in %s",PRVM_NAME, required_func[i], filename);
2251 PRVM_LoadLNO(filename);
2255 if(*prvm_language.string)
2256 // in CSQC we really shouldn't be able to change how stuff works... sorry for now
2257 // later idea: include a list of authorized .po file checksums with the csprogs
2259 qboolean deftrans = !!strcmp(PRVM_NAME, "client");
2260 const char *realfilename = (strcmp(PRVM_NAME, "client") ? filename : csqc_progname.string);
2261 if(deftrans) // once we have dotranslate_ strings, ALWAYS use the opt-in method!
2263 for (i=0 ; i<prog->numglobaldefs ; i++)
2266 name = PRVM_GetString(prog->globaldefs[i].s_name);
2267 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2268 if(name && !strncmp(name, "dotranslate_", 12))
2275 if(!strcmp(prvm_language.string, "dump"))
2277 qfile_t *f = FS_OpenRealFile(va("%s.pot", realfilename), "w", false);
2278 Con_Printf("Dumping to %s.pot\n", realfilename);
2281 for (i=0 ; i<prog->numglobaldefs ; i++)
2284 name = PRVM_GetString(prog->globaldefs[i].s_name);
2285 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2286 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2288 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2289 const char *value = PRVM_GetString(val->string);
2292 char buf[MAX_INPUTLINE];
2293 PRVM_PO_UnparseString(buf, value, sizeof(buf));
2294 FS_Printf(f, "msgid \"%s\"\nmsgstr \"\"\n\n", buf);
2303 po_t *po = PRVM_PO_Load(va("%s.%s.po", realfilename, prvm_language.string), prog->progs_mempool);
2306 for (i=0 ; i<prog->numglobaldefs ; i++)
2309 name = PRVM_GetString(prog->globaldefs[i].s_name);
2310 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2311 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2313 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2314 const char *value = PRVM_GetString(val->string);
2317 value = PRVM_PO_Lookup(po, value);
2319 val->string = PRVM_SetEngineString(value);
2327 for (i=0 ; i<prog->numglobaldefs ; i++)
2330 name = PRVM_GetString(prog->globaldefs[i].s_name);
2331 //Con_Printf("found var %s\n", name);
2333 && !strncmp(name, "autocvar_", 9)
2334 && !(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
2337 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2338 cvar_t *cvar = Cvar_FindVar(name + 9);
2339 //Con_Printf("PRVM_LoadProgs: autocvar global %s in %s, processing...\n", name, PRVM_NAME);
2344 Con_DPrintf("PRVM_LoadProgs: no cvar for autocvar global %s in %s, creating...\n", name, PRVM_NAME);
2345 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2348 if((float)((int)(val->_float)) == val->_float)
2349 dpsnprintf(buf, sizeof(buf), "%i", (int)(val->_float));
2351 dpsnprintf(buf, sizeof(buf), "%.9g", val->_float);
2355 dpsnprintf(buf, sizeof(buf), "%.9g %.9g %.9g", val->vector[0], val->vector[1], val->vector[2]); value = buf;
2358 value = PRVM_GetString(val->string);
2361 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, PRVM_NAME);
2364 cvar = Cvar_Get(name + 9, value, 0, NULL);
2365 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2367 val->string = PRVM_SetEngineString(cvar->string);
2368 cvar->globaldefindex_stringno[prog - prog_list] = val->string;
2371 PRVM_ERROR("PRVM_LoadProgs: could not create cvar for autocvar global %s in %s", name, PRVM_NAME);
2372 cvar->globaldefindex_progid[prog - prog_list] = prog->id;
2373 cvar->globaldefindex[prog - prog_list] = i;
2375 else if((cvar->flags & CVAR_PRIVATE) == 0)
2377 // MUST BE SYNCED WITH cvar.c Cvar_Set
2380 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2383 val->_float = cvar->value;
2387 VectorClear(val->vector);
2388 for (j = 0;j < 3;j++)
2390 while (*s && ISWHITESPACE(*s))
2394 val->vector[j] = atof(s);
2395 while (!ISWHITESPACE(*s))
2402 val->string = PRVM_SetEngineString(cvar->string);
2403 cvar->globaldefindex_stringno[prog - prog_list] = val->string;
2406 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, PRVM_NAME);
2409 cvar->globaldefindex_progid[prog - prog_list] = prog->id;
2410 cvar->globaldefindex[prog - prog_list] = i;
2413 Con_Printf("PRVM_LoadProgs: private cvar for autocvar global %s in %s\n", name, PRVM_NAME);
2419 prog->loaded = TRUE;
2421 // set flags & ddef_ts in prog
2427 PRVM_GCALL(init_cmd)();
2434 void PRVM_Fields_f (void)
2436 int i, j, ednum, used, usedamount;
2438 char tempstring[MAX_INPUTLINE], tempstring2[260];
2448 Con_Print("no progs loaded\n");
2455 Con_Print("prvm_fields <program name>\n");
2460 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
2463 counts = (int *)Mem_Alloc(tempmempool, prog->numfielddefs * sizeof(int));
2464 for (ednum = 0;ednum < prog->max_edicts;ednum++)
2466 ed = PRVM_EDICT_NUM(ednum);
2467 if (ed->priv.required->free)
2469 for (i = 1;i < prog->numfielddefs;i++)
2471 d = &prog->fielddefs[i];
2472 name = PRVM_GetString(d->s_name);
2473 if (name[strlen(name)-2] == '_')
2474 continue; // skip _x, _y, _z vars
2475 v = (int *)(ed->fields.vp + d->ofs);
2476 // if the value is still all 0, skip the field
2477 for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
2490 for (i = 0;i < prog->numfielddefs;i++)
2492 d = &prog->fielddefs[i];
2493 name = PRVM_GetString(d->s_name);
2494 if (name[strlen(name)-2] == '_')
2495 continue; // skip _x, _y, _z vars
2496 switch(d->type & ~DEF_SAVEGLOBAL)
2499 strlcat(tempstring, "string ", sizeof(tempstring));
2502 strlcat(tempstring, "entity ", sizeof(tempstring));
2505 strlcat(tempstring, "function ", sizeof(tempstring));
2508 strlcat(tempstring, "field ", sizeof(tempstring));
2511 strlcat(tempstring, "void ", sizeof(tempstring));
2514 strlcat(tempstring, "float ", sizeof(tempstring));
2517 strlcat(tempstring, "vector ", sizeof(tempstring));
2520 strlcat(tempstring, "pointer ", sizeof(tempstring));
2523 dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
2524 strlcat(tempstring, tempstring2, sizeof(tempstring));
2527 if (strlen(name) > sizeof(tempstring2)-4)
2529 memcpy (tempstring2, name, sizeof(tempstring2)-4);
2530 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
2531 tempstring2[sizeof(tempstring2)-1] = 0;
2534 strlcat(tempstring, name, sizeof(tempstring));
2535 for (j = (int)strlen(name);j < 25;j++)
2536 strlcat(tempstring, " ", sizeof(tempstring));
2537 dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
2538 strlcat(tempstring, tempstring2, sizeof(tempstring));
2539 strlcat(tempstring, "\n", sizeof(tempstring));
2540 if (strlen(tempstring) >= sizeof(tempstring)/2)
2542 Con_Print(tempstring);
2548 usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
2552 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);
2557 void PRVM_Globals_f (void)
2560 const char *wildcard;
2566 Con_Print("no progs loaded\n");
2569 if(Cmd_Argc () < 2 || Cmd_Argc() > 3)
2571 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
2576 if(!PRVM_SetProgFromString (Cmd_Argv (1)))
2579 if( Cmd_Argc() == 3)
2580 wildcard = Cmd_Argv(2);
2584 Con_Printf("%s :", PRVM_NAME);
2586 for (i = 0;i < prog->numglobaldefs;i++)
2589 if( !matchpattern( PRVM_GetString(prog->globaldefs[i].s_name), wildcard, 1) )
2594 Con_Printf("%s\n", PRVM_GetString(prog->globaldefs[i].s_name));
2596 Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->numglobals, numculled, prog->numglobals * 4);
2606 void PRVM_Global_f(void)
2609 if( Cmd_Argc() != 3 ) {
2610 Con_Printf( "prvm_global <program name> <global name>\n" );
2615 if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
2618 global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
2620 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2622 Con_Printf( "%s: %s\n", Cmd_Argv(2), PRVM_ValueString( (etype_t)global->type, PRVM_GLOBALFIELDVALUE(global->ofs) ) );
2631 void PRVM_GlobalSet_f(void)
2634 if( Cmd_Argc() != 4 ) {
2635 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2640 if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
2643 global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
2645 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2647 PRVM_ED_ParseEpair( NULL, global, Cmd_Argv(3), true );
2656 void PRVM_Init (void)
2658 Cmd_AddCommand ("prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2659 Cmd_AddCommand ("prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2660 Cmd_AddCommand ("prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2661 Cmd_AddCommand ("prvm_profile", PRVM_Profile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu)");
2662 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");
2663 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)");
2664 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)");
2665 Cmd_AddCommand ("prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2666 Cmd_AddCommand ("prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2667 Cmd_AddCommand ("prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2668 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)");
2669 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");
2670 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");
2671 Cmd_AddCommand ("prvm_printfunction", PRVM_PrintFunction_f, "prints a disassembly (QuakeC instructions) of the specified function in the selected VM (server, client, menu)");
2672 Cmd_AddCommand ("cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2673 Cmd_AddCommand ("menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2674 Cmd_AddCommand ("sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2676 Cvar_RegisterVariable (&prvm_language);
2677 Cvar_RegisterVariable (&prvm_traceqc);
2678 Cvar_RegisterVariable (&prvm_statementprofiling);
2679 Cvar_RegisterVariable (&prvm_timeprofiling);
2680 Cvar_RegisterVariable (&prvm_backtraceforwarnings);
2681 Cvar_RegisterVariable (&prvm_leaktest);
2682 Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames);
2683 Cvar_RegisterVariable (&prvm_errordump);
2684 Cvar_RegisterVariable (&prvm_reuseedicts_startuptime);
2685 Cvar_RegisterVariable (&prvm_reuseedicts_neverinsameframe);
2687 // COMMANDLINEOPTION: PRVM: -norunaway disables the runaway loop check (it might be impossible to exit DarkPlaces if used!)
2688 prvm_runawaycheck = !COM_CheckParm("-norunaway");
2698 void PRVM_InitProg(int prognr)
2700 static unsigned int progid = 0;
2702 if(prognr < 0 || prognr >= PRVM_MAXPROGS)
2703 Sys_Error("PRVM_InitProg: Invalid program number %i",prognr);
2705 prog = &prog_list[prognr];
2710 memset(prog, 0, sizeof(prvm_prog_t));
2711 prog->starttime = Sys_DoubleTime();
2712 prog->id = ++progid;
2714 prog->error_cmd = Host_Error;
2715 prog->leaktest_active = prvm_leaktest.integer != 0;
2718 int PRVM_GetProgNr(void)
2720 return prog - prog_list;
2723 void *_PRVM_Alloc(size_t buffersize, const char *filename, int fileline)
2725 return _Mem_Alloc(prog->progs_mempool, NULL, buffersize, 16, filename, fileline);
2728 void _PRVM_Free(void *buffer, const char *filename, int fileline)
2730 _Mem_Free(buffer, filename, fileline);
2733 void _PRVM_FreeAll(const char *filename, int fileline)
2735 prog->functions = NULL;
2736 prog->strings = NULL;
2737 prog->fielddefs = NULL;
2738 prog->globaldefs = NULL;
2739 prog->statements = NULL;
2740 // FIXME: what about knownstrings?
2741 _Mem_EmptyPool(prog->progs_mempool, filename, fileline);
2744 // LordHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
2745 unsigned int PRVM_EDICT_NUM_ERROR(unsigned int n, const char *filename, int fileline)
2747 PRVM_ERROR ("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", PRVM_NAME, n, filename, fileline);
2751 sizebuf_t vm_tempstringsbuf;
2752 #define PRVM_KNOWNSTRINGBASE 0x40000000
2754 const char *PRVM_GetString(int num)
2759 VM_Warning("PRVM_GetString: Invalid string offset (%i < 0)\n", num);
2762 else if (num < prog->stringssize)
2764 // constant string from progs.dat
2765 return prog->strings + num;
2767 else if (num <= prog->stringssize + vm_tempstringsbuf.maxsize)
2769 // tempstring returned by engine to QC (becomes invalid after returning to engine)
2770 num -= prog->stringssize;
2771 if (num < vm_tempstringsbuf.cursize)
2772 return (char *)vm_tempstringsbuf.data + num;
2775 VM_Warning("PRVM_GetString: Invalid temp-string offset (%i >= %i vm_tempstringsbuf.cursize)\n", num, vm_tempstringsbuf.cursize);
2779 else if (num & PRVM_KNOWNSTRINGBASE)
2782 num = num - PRVM_KNOWNSTRINGBASE;
2783 if (num >= 0 && num < prog->numknownstrings)
2785 if (!prog->knownstrings[num])
2787 VM_Warning("PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
2790 return prog->knownstrings[num];
2794 VM_Warning("PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
2800 // invalid string offset
2801 VM_Warning("PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
2806 const char *PRVM_ChangeEngineString(int i, const char *s)
2809 i = i - PRVM_KNOWNSTRINGBASE;
2810 if(i < 0 || i >= prog->numknownstrings)
2811 PRVM_ERROR("PRVM_ChangeEngineString: s is not an engine string");
2812 old = prog->knownstrings[i];
2813 prog->knownstrings[i] = s;
2817 int PRVM_SetEngineString(const char *s)
2822 if (s >= prog->strings && s <= prog->strings + prog->stringssize)
2823 PRVM_ERROR("PRVM_SetEngineString: s in prog->strings area");
2824 // if it's in the tempstrings area, use a reserved range
2825 // (otherwise we'd get millions of useless string offsets cluttering the database)
2826 if (s >= (char *)vm_tempstringsbuf.data && s < (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.maxsize)
2828 return prog->stringssize + (s - (char *)vm_tempstringsbuf.data);
2830 // see if it's a known string address
2831 for (i = 0;i < prog->numknownstrings;i++)
2832 if (prog->knownstrings[i] == s)
2833 return PRVM_KNOWNSTRINGBASE + i;
2834 // new unknown engine string
2835 if (developer_insane.integer)
2836 Con_DPrintf("new engine string %p = \"%s\"\n", s, s);
2837 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2838 if (!prog->knownstrings[i])
2840 if (i >= prog->numknownstrings)
2842 if (i >= prog->maxknownstrings)
2844 const char **oldstrings = prog->knownstrings;
2845 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2846 const char **oldstrings_origin = prog->knownstrings_origin;
2847 prog->maxknownstrings += 128;
2848 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2849 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2850 if(prog->leaktest_active)
2851 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2852 if (prog->numknownstrings)
2854 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2855 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2856 if(prog->leaktest_active)
2857 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
2860 prog->numknownstrings++;
2862 prog->firstfreeknownstring = i + 1;
2863 prog->knownstrings[i] = s;
2864 prog->knownstrings_freeable[i] = false;
2865 if(prog->leaktest_active)
2866 prog->knownstrings_origin[i] = NULL;
2867 return PRVM_KNOWNSTRINGBASE + i;
2870 // temp string handling
2872 // all tempstrings go into this buffer consecutively, and it is reset
2873 // whenever PRVM_ExecuteProgram returns to the engine
2874 // (technically each PRVM_ExecuteProgram call saves the cursize value and
2875 // restores it on return, so multiple recursive calls can share the same
2877 // the buffer size is automatically grown as needed
2879 int PRVM_SetTempString(const char *s)
2885 size = (int)strlen(s) + 1;
2886 if (developer_insane.integer)
2887 Con_DPrintf("PRVM_SetTempString: cursize %i, size %i\n", vm_tempstringsbuf.cursize, size);
2888 if (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
2890 sizebuf_t old = vm_tempstringsbuf;
2891 if (vm_tempstringsbuf.cursize + size >= 1<<28)
2892 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);
2893 vm_tempstringsbuf.maxsize = max(vm_tempstringsbuf.maxsize, 65536);
2894 while (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
2895 vm_tempstringsbuf.maxsize *= 2;
2896 if (vm_tempstringsbuf.maxsize != old.maxsize || vm_tempstringsbuf.data == NULL)
2898 Con_DPrintf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, vm_tempstringsbuf.maxsize/1024);
2899 vm_tempstringsbuf.data = (unsigned char *) Mem_Alloc(sv_mempool, vm_tempstringsbuf.maxsize);
2901 memcpy(vm_tempstringsbuf.data, old.data, old.cursize);
2906 t = (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.cursize;
2908 vm_tempstringsbuf.cursize += size;
2909 return PRVM_SetEngineString(t);
2912 int PRVM_AllocString(size_t bufferlength, char **pointer)
2917 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2918 if (!prog->knownstrings[i])
2920 if (i >= prog->numknownstrings)
2922 if (i >= prog->maxknownstrings)
2924 const char **oldstrings = prog->knownstrings;
2925 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2926 const char **oldstrings_origin = prog->knownstrings_origin;
2927 prog->maxknownstrings += 128;
2928 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2929 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2930 if(prog->leaktest_active)
2931 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2932 if (prog->numknownstrings)
2934 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2935 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2936 if(prog->leaktest_active)
2937 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
2940 Mem_Free((char **)oldstrings);
2941 if (oldstrings_freeable)
2942 Mem_Free((unsigned char *)oldstrings_freeable);
2943 if (oldstrings_origin)
2944 Mem_Free((char **)oldstrings_origin);
2946 prog->numknownstrings++;
2948 prog->firstfreeknownstring = i + 1;
2949 prog->knownstrings[i] = (char *)PRVM_Alloc(bufferlength);
2950 prog->knownstrings_freeable[i] = true;
2951 if(prog->leaktest_active)
2952 prog->knownstrings_origin[i] = PRVM_AllocationOrigin();
2954 *pointer = (char *)(prog->knownstrings[i]);
2955 return PRVM_KNOWNSTRINGBASE + i;
2958 void PRVM_FreeString(int num)
2961 PRVM_ERROR("PRVM_FreeString: attempt to free a NULL string");
2962 else if (num >= 0 && num < prog->stringssize)
2963 PRVM_ERROR("PRVM_FreeString: attempt to free a constant string");
2964 else if (num >= PRVM_KNOWNSTRINGBASE && num < PRVM_KNOWNSTRINGBASE + prog->numknownstrings)
2966 num = num - PRVM_KNOWNSTRINGBASE;
2967 if (!prog->knownstrings[num])
2968 PRVM_ERROR("PRVM_FreeString: attempt to free a non-existent or already freed string");
2969 if (!prog->knownstrings_freeable[num])
2970 PRVM_ERROR("PRVM_FreeString: attempt to free a string owned by the engine");
2971 PRVM_Free((char *)prog->knownstrings[num]);
2972 if(prog->leaktest_active)
2973 if(prog->knownstrings_origin[num])
2974 PRVM_Free((char *)prog->knownstrings_origin[num]);
2975 prog->knownstrings[num] = NULL;
2976 prog->knownstrings_freeable[num] = false;
2977 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
2980 PRVM_ERROR("PRVM_FreeString: invalid string offset %i", num);
2983 static qboolean PRVM_IsStringReferenced(string_t string)
2987 for (i = 0;i < prog->numglobaldefs;i++)
2989 ddef_t *d = &prog->globaldefs[i];
2990 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
2992 if(string == PRVM_GLOBALFIELDSTRING(d->ofs))
2996 for(j = 0; j < prog->num_edicts; ++j)
2998 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2999 if (ed->priv.required->free)
3001 for (i=0; i<prog->numfielddefs; ++i)
3003 ddef_t *d = &prog->fielddefs[i];
3004 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3006 if(string == PRVM_EDICTFIELDSTRING(ed, d->ofs))
3014 static qboolean PRVM_IsEdictRelevant(prvm_edict_t *edict)
3016 if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
3017 return true; // world or clients
3018 switch(prog - prog_list)
3020 case PRVM_SERVERPROG:
3022 if(PRVM_serveredictfloat(edict, solid)) // can block other stuff, or is a trigger?
3024 if(PRVM_serveredictfloat(edict, modelindex)) // visible ent?
3026 if(PRVM_serveredictfloat(edict, effects)) // particle effect?
3028 if(PRVM_serveredictfunction(edict, think)) // has a think function?
3029 if(PRVM_serveredictfloat(edict, nextthink) > 0) // that actually will eventually run?
3031 if(PRVM_serveredictfloat(edict, takedamage))
3033 if(*prvm_leaktest_ignore_classnames.string)
3035 if(strstr(va(" %s ", prvm_leaktest_ignore_classnames.string), va(" %s ", PRVM_GetString(PRVM_serveredictstring(edict, classname)))))
3040 case PRVM_CLIENTPROG:
3042 // TODO someone add more stuff here
3043 if(PRVM_clientedictfloat(edict, entnum)) // csqc networked
3045 if(PRVM_clientedictfloat(edict, modelindex)) // visible ent?
3047 if(PRVM_clientedictfloat(edict, effects)) // particle effect?
3049 if(PRVM_clientedictfunction(edict, think)) // has a think function?
3050 if(PRVM_clientedictfloat(edict, nextthink) > 0) // that actually will eventually run?
3052 if(*prvm_leaktest_ignore_classnames.string)
3054 if(strstr(va(" %s ", prvm_leaktest_ignore_classnames.string), va(" %s ", PRVM_GetString(PRVM_clientedictstring(edict, classname)))))
3060 // menu prog does not have classnames
3066 static qboolean PRVM_IsEdictReferenced(prvm_edict_t *edict, int mark)
3069 int edictnum = PRVM_NUM_FOR_EDICT(edict);
3070 const char *targetname = NULL;
3072 switch(prog - prog_list)
3074 case PRVM_SERVERPROG:
3075 targetname = PRVM_GetString(PRVM_serveredictstring(edict, targetname));
3080 if(!*targetname) // ""
3085 for (i = 0;i < prog->numglobaldefs;i++)
3087 ddef_t *d = &prog->globaldefs[i];
3088 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3090 if(edictnum == PRVM_GLOBALFIELDEDICT(d->ofs))
3095 for(j = 0; j < prog->num_edicts; ++j)
3097 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3098 if (ed->priv.required->mark < mark)
3104 const char *target = PRVM_GetString(PRVM_serveredictstring(ed, target));
3106 if(!strcmp(target, targetname))
3109 for (i=0; i<prog->numfielddefs; ++i)
3111 ddef_t *d = &prog->fielddefs[i];
3112 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3114 if(edictnum == PRVM_EDICTFIELDEDICT(ed, d->ofs))
3122 static void PRVM_MarkReferencedEdicts(void)
3128 for(j = 0; j < prog->num_edicts; ++j)
3130 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3131 if(ed->priv.required->free)
3133 ed->priv.required->mark = PRVM_IsEdictRelevant(ed) ? 1 : 0;
3140 for(j = 0; j < prog->num_edicts; ++j)
3142 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3143 if(ed->priv.required->free)
3145 if(ed->priv.required->mark)
3147 if(PRVM_IsEdictReferenced(ed, stage))
3149 ed->priv.required->mark = stage + 1;
3156 Con_DPrintf("leak check used %d stages to find all references\n", stage);
3159 void PRVM_LeakTest(void)
3162 qboolean leaked = false;
3164 if(!prog->leaktest_active)
3168 for (i = 0; i < prog->numknownstrings; ++i)
3170 if(prog->knownstrings[i])
3171 if(prog->knownstrings_freeable[i])
3172 if(prog->knownstrings_origin[i])
3173 if(!PRVM_IsStringReferenced(PRVM_KNOWNSTRINGBASE + i))
3175 Con_Printf("Unreferenced string found!\n Value: %s\n Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
3181 PRVM_MarkReferencedEdicts();
3182 for(j = 0; j < prog->num_edicts; ++j)
3184 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3185 if(ed->priv.required->free)
3187 if(!ed->priv.required->mark)
3188 if(ed->priv.required->allocation_origin)
3190 Con_Printf("Unreferenced edict found!\n Allocated at: %s\n", ed->priv.required->allocation_origin);
3191 PRVM_ED_Print(ed, NULL);
3196 ed->priv.required->mark = 0; // clear marks again when done
3199 for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
3201 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
3203 if(stringbuffer->origin)
3205 Con_Printf("Open string buffer handle found!\n Allocated at: %s\n", stringbuffer->origin);
3210 for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
3212 if(prog->openfiles[i])
3213 if(prog->openfiles_origin[i])
3215 Con_Printf("Open file handle found!\n Allocated at: %s\n", prog->openfiles_origin[i]);
3220 for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
3222 if(prog->opensearches[i])
3223 if(prog->opensearches_origin[i])
3225 Con_Printf("Open search handle found!\n Allocated at: %s\n", prog->opensearches_origin[i]);
3231 Con_Printf("Congratulations. No leaks found.\n");