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.
26 prvm_prog_t prvm_prog_list[PRVM_PROG_MAX];
28 int prvm_type_size[8] = {1,sizeof(string_t)/4,1,3,1,1,sizeof(func_t)/4,sizeof(void *)/4};
30 prvm_eval_t prvm_badvalue; // used only for error returns
32 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"};
33 // LordHavoc: prints every opcode as it executes - warning: this is significant spew
34 cvar_t prvm_traceqc = {0, "prvm_traceqc", "0", "prints every QuakeC statement as it is executed (only for really thorough debugging!)"};
35 // LordHavoc: counts usage of each QuakeC statement
36 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)"};
37 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)"};
38 cvar_t prvm_backtraceforwarnings = {0, "prvm_backtraceforwarnings", "0", "print a backtrace for warnings too"};
39 cvar_t prvm_leaktest = {0, "prvm_leaktest", "0", "try to detect memory leaks in strings or entities"};
40 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)"};
41 cvar_t prvm_errordump = {0, "prvm_errordump", "0", "write a savegame on crash to crash-server.dmp"};
42 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)"};
43 cvar_t prvm_reuseedicts_neverinsameframe = {0, "prvm_reuseedicts_neverinsameframe", "1", "never allows re-use of freed entity slots during same frame"};
45 static double prvm_reuseedicts_always_allow = 0;
46 qboolean prvm_runawaycheck = true;
48 //============================================================================
56 static void PRVM_MEM_Alloc(prvm_prog_t *prog)
60 // reserve space for the null entity aka world
61 // check bound of max_edicts
62 prog->max_edicts = bound(1 + prog->reserved_edicts, prog->max_edicts, prog->limit_edicts);
63 prog->num_edicts = bound(1 + prog->reserved_edicts, prog->num_edicts, prog->max_edicts);
65 // edictprivate_size has to be min as big prvm_edict_private_t
66 prog->edictprivate_size = max(prog->edictprivate_size,(int)sizeof(prvm_edict_private_t));
69 prog->edicts = (prvm_edict_t *)Mem_Alloc(prog->progs_mempool,prog->limit_edicts * sizeof(prvm_edict_t));
71 // alloc edict private space
72 prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size);
75 prog->entityfieldsarea = prog->entityfields * prog->max_edicts;
76 prog->edictsfields = (prvm_vec_t *)Mem_Alloc(prog->progs_mempool, prog->entityfieldsarea * sizeof(prvm_vec_t));
79 for(i = 0; i < prog->max_edicts; i++)
81 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
82 prog->edicts[i].fields.fp = prog->edictsfields + i * prog->entityfields;
88 PRVM_MEM_IncreaseEdicts
91 void PRVM_MEM_IncreaseEdicts(prvm_prog_t *prog)
95 if(prog->max_edicts >= prog->limit_edicts)
98 prog->begin_increase_edicts(prog);
101 prog->max_edicts = min(prog->max_edicts + 256, prog->limit_edicts);
103 prog->entityfieldsarea = prog->entityfields * prog->max_edicts;
104 prog->edictsfields = (prvm_vec_t*)Mem_Realloc(prog->progs_mempool, (void *)prog->edictsfields, prog->entityfieldsarea * sizeof(prvm_vec_t));
105 prog->edictprivate = (void *)Mem_Realloc(prog->progs_mempool, (void *)prog->edictprivate, prog->max_edicts * prog->edictprivate_size);
107 //set e and v pointers
108 for(i = 0; i < prog->max_edicts; i++)
110 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
111 prog->edicts[i].fields.fp = prog->edictsfields + i * prog->entityfields;
114 prog->end_increase_edicts(prog);
117 //============================================================================
120 int PRVM_ED_FindFieldOffset(prvm_prog_t *prog, const char *field)
123 d = PRVM_ED_FindField(prog, field);
129 int PRVM_ED_FindGlobalOffset(prvm_prog_t *prog, const char *global)
132 d = PRVM_ED_FindGlobal(prog, global);
138 func_t PRVM_ED_FindFunctionOffset(prvm_prog_t *prog, const char *function)
141 f = PRVM_ED_FindFunction(prog, function);
144 return (func_t)(f - prog->functions);
152 prvm_prog_t *PRVM_ProgFromString(const char *str)
154 if (!strcmp(str, "server"))
156 if (!strcmp(str, "client"))
158 if (!strcmp(str, "menu"))
165 PRVM_FriendlyProgFromString
168 prvm_prog_t *PRVM_FriendlyProgFromString(const char *str)
170 prvm_prog_t *prog = PRVM_ProgFromString(str);
173 Con_Printf("%s: unknown program name\n", str);
178 Con_Printf("%s: program is not loaded\n", str);
188 Sets everything to NULL
191 void PRVM_ED_ClearEdict(prvm_prog_t *prog, prvm_edict_t *e)
193 memset(e->fields.fp, 0, prog->entityfields * sizeof(prvm_vec_t));
194 e->priv.required->free = false;
196 // AK: Let the init_edict function determine if something needs to be initialized
197 prog->init_edict(prog, e);
200 const char *PRVM_AllocationOrigin(prvm_prog_t *prog)
203 if(prog->leaktest_active)
204 if(prog->depth > 0) // actually in QC code and not just parsing the entities block of a map/savegame
206 buf = (char *)PRVM_Alloc(128);
207 PRVM_ShortStackTrace(prog, buf, 128);
216 Returns if this particular edict could get allocated by PRVM_ED_Alloc
219 qboolean PRVM_ED_CanAlloc(prvm_prog_t *prog, prvm_edict_t *e)
221 if(!e->priv.required->free)
223 if(prvm_reuseedicts_always_allow == realtime)
225 if(realtime <= e->priv.required->freetime + 0.1 && prvm_reuseedicts_neverinsameframe.integer)
226 return false; // never allow reuse in same frame (causes networking trouble)
227 if(e->priv.required->freetime < prog->starttime + prvm_reuseedicts_startuptime.value)
229 if(realtime > e->priv.required->freetime + 1)
231 return false; // entity slot still blocked because the entity was freed less than one second ago
238 Either finds a free edict, or allocates a new one.
239 Try to avoid reusing an entity that was recently freed, because it
240 can cause the client to think the entity morphed into something else
241 instead of being removed and recreated, which can cause interpolated
242 angles and bad trails.
245 prvm_edict_t *PRVM_ED_Alloc(prvm_prog_t *prog)
250 // the client qc dont need maxclients
251 // thus it doesnt need to use svs.maxclients
252 // AK: changed i=svs.maxclients+1
253 // AK: changed so the edict 0 wont spawn -> used as reserved/world entity
254 // although the menu/client has no world
255 for (i = prog->reserved_edicts + 1;i < prog->num_edicts;i++)
257 e = PRVM_EDICT_NUM(i);
258 if(PRVM_ED_CanAlloc(prog, e))
260 PRVM_ED_ClearEdict (prog, e);
261 e->priv.required->allocation_origin = PRVM_AllocationOrigin(prog);
266 if (i == prog->limit_edicts)
267 prog->error_cmd("%s: PRVM_ED_Alloc: no free edicts", prog->name);
270 if (prog->num_edicts >= prog->max_edicts)
271 PRVM_MEM_IncreaseEdicts(prog);
273 e = PRVM_EDICT_NUM(i);
274 PRVM_ED_ClearEdict(prog, e);
276 e->priv.required->allocation_origin = PRVM_AllocationOrigin(prog);
285 Marks the edict as free
286 FIXME: walk all entities and NULL out references to this entity
289 void PRVM_ED_Free(prvm_prog_t *prog, prvm_edict_t *ed)
291 // dont delete the null entity (world) or reserved edicts
292 if (ed - prog->edicts <= prog->reserved_edicts)
295 prog->free_edict(prog, ed);
297 ed->priv.required->free = true;
298 ed->priv.required->freetime = realtime;
299 if(ed->priv.required->allocation_origin)
301 Mem_Free((char *)ed->priv.required->allocation_origin);
302 ed->priv.required->allocation_origin = NULL;
306 //===========================================================================
313 static ddef_t *PRVM_ED_GlobalAtOfs (prvm_prog_t *prog, int ofs)
318 for (i = 0;i < prog->numglobaldefs;i++)
320 def = &prog->globaldefs[i];
332 ddef_t *PRVM_ED_FieldAtOfs (prvm_prog_t *prog, int ofs)
337 for (i = 0;i < prog->numfielddefs;i++)
339 def = &prog->fielddefs[i];
351 ddef_t *PRVM_ED_FindField (prvm_prog_t *prog, const char *name)
356 for (i = 0;i < prog->numfielddefs;i++)
358 def = &prog->fielddefs[i];
359 if (!strcmp(PRVM_GetString(prog, def->s_name), name))
370 ddef_t *PRVM_ED_FindGlobal (prvm_prog_t *prog, const char *name)
375 for (i = 0;i < prog->numglobaldefs;i++)
377 def = &prog->globaldefs[i];
378 if (!strcmp(PRVM_GetString(prog, def->s_name), name))
390 mfunction_t *PRVM_ED_FindFunction (prvm_prog_t *prog, const char *name)
395 for (i = 0;i < prog->numfunctions;i++)
397 func = &prog->functions[i];
398 if (!strcmp(PRVM_GetString(prog, func->s_name), name))
409 Returns a string describing *data in a type specific manner
412 static char *PRVM_ValueString (prvm_prog_t *prog, etype_t type, prvm_eval_t *val, char *line, size_t linelength)
418 type = (etype_t)((int) type & ~DEF_SAVEGLOBAL);
423 strlcpy (line, PRVM_GetString (prog, val->string), linelength);
427 if (n < 0 || n >= prog->max_edicts)
428 dpsnprintf (line, linelength, "entity %i (invalid!)", n);
430 dpsnprintf (line, linelength, "entity %i", n);
433 f = prog->functions + val->function;
434 dpsnprintf (line, linelength, "%s()", PRVM_GetString(prog, f->s_name));
437 def = PRVM_ED_FieldAtOfs ( prog, val->_int );
438 dpsnprintf (line, linelength, ".%s", PRVM_GetString(prog, def->s_name));
441 dpsnprintf (line, linelength, "void");
444 // LordHavoc: changed from %5.1f to %10.4f
445 dpsnprintf (line, linelength, FLOAT_LOSSLESS_FORMAT, val->_float);
448 // LordHavoc: changed from %5.1f to %10.4f
449 dpsnprintf (line, linelength, "'" VECTOR_LOSSLESS_FORMAT "'", val->vector[0], val->vector[1], val->vector[2]);
452 dpsnprintf (line, linelength, "pointer");
455 dpsnprintf (line, linelength, "bad type %i", (int) type);
466 Returns a string describing *data in a type specific manner
467 Easier to parse than PR_ValueString
470 char *PRVM_UglyValueString (prvm_prog_t *prog, etype_t type, prvm_eval_t *val, char *line, size_t linelength)
477 type = (etype_t)((int)type & ~DEF_SAVEGLOBAL);
482 // Parse the string a bit to turn special characters
483 // (like newline, specifically) into escape codes,
484 // this fixes saving games from various mods
485 s = PRVM_GetString (prog, val->string);
486 for (i = 0;i < (int)linelength - 2 && *s;)
515 dpsnprintf (line, linelength, "%i", PRVM_NUM_FOR_EDICT(PRVM_PROG_TO_EDICT(val->edict)));
518 f = prog->functions + val->function;
519 strlcpy (line, PRVM_GetString (prog, f->s_name), linelength);
522 def = PRVM_ED_FieldAtOfs ( prog, val->_int );
523 dpsnprintf (line, linelength, ".%s", PRVM_GetString(prog, def->s_name));
526 dpsnprintf (line, linelength, "void");
529 dpsnprintf (line, linelength, FLOAT_LOSSLESS_FORMAT, val->_float);
532 dpsnprintf (line, linelength, VECTOR_LOSSLESS_FORMAT, val->vector[0], val->vector[1], val->vector[2]);
535 dpsnprintf (line, linelength, "bad type %i", type);
546 Returns a string with a description and the contents of a global,
547 padded to 20 field width
550 char *PRVM_GlobalString (prvm_prog_t *prog, int ofs, char *line, size_t linelength)
556 char valuebuf[MAX_INPUTLINE];
558 val = (prvm_eval_t *)&prog->globals.fp[ofs];
559 def = PRVM_ED_GlobalAtOfs(prog, ofs);
561 dpsnprintf (line, linelength, "GLOBAL%i", ofs);
564 s = PRVM_ValueString (prog, (etype_t)def->type, val, valuebuf, sizeof(valuebuf));
565 dpsnprintf (line, linelength, "%s (=%s)", PRVM_GetString(prog, def->s_name), s);
569 //for ( ; i<20 ; i++)
570 // strcat (line," ");
576 char *PRVM_GlobalStringNoContents (prvm_prog_t *prog, int ofs, char *line, size_t linelength)
581 def = PRVM_ED_GlobalAtOfs(prog, ofs);
583 dpsnprintf (line, linelength, "GLOBAL%i", ofs);
585 dpsnprintf (line, linelength, "%s", PRVM_GetString(prog, def->s_name));
588 //for ( ; i<20 ; i++)
589 // strcat (line," ");
603 // LordHavoc: optimized this to print out much more quickly (tempstring)
604 // LordHavoc: changed to print out every 4096 characters (incase there are a lot of fields to print)
605 void PRVM_ED_Print(prvm_prog_t *prog, prvm_edict_t *ed, const char *wildcard_fieldname)
613 char tempstring[MAX_INPUTLINE], tempstring2[260]; // temporary string buffers
614 char valuebuf[MAX_INPUTLINE];
616 if (ed->priv.required->free)
618 Con_Printf("%s: FREE\n",prog->name);
623 dpsnprintf(tempstring, sizeof(tempstring), "\n%s EDICT %i:\n", prog->name, PRVM_NUM_FOR_EDICT(ed));
624 for (i = 1;i < prog->numfielddefs;i++)
626 d = &prog->fielddefs[i];
627 name = PRVM_GetString(prog, d->s_name);
628 if(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
629 continue; // skip _x, _y, _z vars
631 // Check Field Name Wildcard
632 if(wildcard_fieldname)
633 if( !matchpattern(name, wildcard_fieldname, 1) )
634 // Didn't match; skip
637 val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
639 // if the value is still all 0, skip the field
640 type = d->type & ~DEF_SAVEGLOBAL;
642 for (j=0 ; j<prvm_type_size[type] ; j++)
645 if (j == prvm_type_size[type])
648 if (strlen(name) > sizeof(tempstring2)-4)
650 memcpy (tempstring2, name, sizeof(tempstring2)-4);
651 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
652 tempstring2[sizeof(tempstring2)-1] = 0;
655 strlcat(tempstring, name, sizeof(tempstring));
656 for (l = strlen(name);l < 14;l++)
657 strlcat(tempstring, " ", sizeof(tempstring));
658 strlcat(tempstring, " ", sizeof(tempstring));
660 name = PRVM_ValueString(prog, (etype_t)d->type, val, valuebuf, sizeof(valuebuf));
661 if (strlen(name) > sizeof(tempstring2)-4)
663 memcpy (tempstring2, name, sizeof(tempstring2)-4);
664 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
665 tempstring2[sizeof(tempstring2)-1] = 0;
668 strlcat(tempstring, name, sizeof(tempstring));
669 strlcat(tempstring, "\n", sizeof(tempstring));
670 if (strlen(tempstring) >= sizeof(tempstring)/2)
672 Con_Print(tempstring);
677 Con_Print(tempstring);
687 extern cvar_t developer_entityparsing;
688 void PRVM_ED_Write (prvm_prog_t *prog, qfile_t *f, prvm_edict_t *ed)
696 char valuebuf[MAX_INPUTLINE];
700 if (ed->priv.required->free)
706 for (i = 1;i < prog->numfielddefs;i++)
708 d = &prog->fielddefs[i];
709 name = PRVM_GetString(prog, d->s_name);
711 if(developer_entityparsing.integer)
712 Con_Printf("PRVM_ED_Write: at entity %d field %s\n", PRVM_NUM_FOR_EDICT(ed), name);
714 //if(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
715 if(strlen(name) > 1 && name[strlen(name)-2] == '_')
716 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?)
718 val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
720 // if the value is still all 0, skip the field
721 type = d->type & ~DEF_SAVEGLOBAL;
722 for (j=0 ; j<prvm_type_size[type] ; j++)
725 if (j == prvm_type_size[type])
728 FS_Printf(f,"\"%s\" ",name);
729 prog->statestring = va(vabuf, sizeof(vabuf), "PRVM_ED_Write, ent=%d, name=%s", i, name);
730 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString(prog, (etype_t)d->type, val, valuebuf, sizeof(valuebuf)));
731 prog->statestring = NULL;
737 void PRVM_ED_PrintNum (prvm_prog_t *prog, int ent, const char *wildcard_fieldname)
739 PRVM_ED_Print(prog, PRVM_EDICT_NUM(ent), wildcard_fieldname);
744 PRVM_ED_PrintEdicts_f
746 For debugging, prints all the entities in the current server
749 void PRVM_ED_PrintEdicts_f (void)
753 const char *wildcard_fieldname;
755 if(Cmd_Argc() < 2 || Cmd_Argc() > 3)
757 Con_Print("prvm_edicts <program name> <optional field name wildcard>\n");
761 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
765 wildcard_fieldname = Cmd_Argv(2);
767 wildcard_fieldname = NULL;
769 Con_Printf("%s: %i entities\n", prog->name, prog->num_edicts);
770 for (i=0 ; i<prog->num_edicts ; i++)
771 PRVM_ED_PrintNum (prog, i, wildcard_fieldname);
778 For debugging, prints a single edict
781 static void PRVM_ED_PrintEdict_f (void)
785 const char *wildcard_fieldname;
787 if(Cmd_Argc() < 3 || Cmd_Argc() > 4)
789 Con_Print("prvm_edict <program name> <edict number> <optional field name wildcard>\n");
793 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
796 i = atoi (Cmd_Argv(2));
797 if (i >= prog->num_edicts)
799 Con_Print("Bad edict number\n");
803 // Optional Wildcard Provided
804 wildcard_fieldname = Cmd_Argv(3);
807 wildcard_fieldname = NULL;
808 PRVM_ED_PrintNum (prog, i, wildcard_fieldname);
818 // 2 possibilities : 1. just displaying the active edict count
819 // 2. making a function pointer [x]
820 static void PRVM_ED_Count_f (void)
826 Con_Print("prvm_count <program name>\n");
830 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
833 prog->count_edicts(prog);
837 ==============================================================================
841 FIXME: need to tag constants, doesn't really work
842 ==============================================================================
850 void PRVM_ED_WriteGlobals (prvm_prog_t *prog, qfile_t *f)
857 char valuebuf[MAX_INPUTLINE];
860 for (i = 0;i < prog->numglobaldefs;i++)
862 def = &prog->globaldefs[i];
864 if ( !(def->type & DEF_SAVEGLOBAL) )
866 type &= ~DEF_SAVEGLOBAL;
868 if (type != ev_string && type != ev_float && type != ev_entity)
871 name = PRVM_GetString(prog, def->s_name);
873 if(developer_entityparsing.integer)
874 Con_Printf("PRVM_ED_WriteGlobals: at global %s\n", name);
876 prog->statestring = va(vabuf, sizeof(vabuf), "PRVM_ED_WriteGlobals, name=%s", name);
877 FS_Printf(f,"\"%s\" ", name);
878 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString(prog, (etype_t)type, (prvm_eval_t *)&prog->globals.fp[def->ofs], valuebuf, sizeof(valuebuf)));
879 prog->statestring = NULL;
889 void PRVM_ED_ParseGlobals (prvm_prog_t *prog, const char *data)
891 char keyname[MAX_INPUTLINE];
897 if (!COM_ParseToken_Simple(&data, false, false, true))
898 prog->error_cmd("PRVM_ED_ParseGlobals: EOF without closing brace");
899 if (com_token[0] == '}')
902 if (developer_entityparsing.integer)
903 Con_Printf("Key: \"%s\"", com_token);
905 strlcpy (keyname, com_token, sizeof(keyname));
908 if (!COM_ParseToken_Simple(&data, false, true, true))
909 prog->error_cmd("PRVM_ED_ParseGlobals: EOF without closing brace");
911 if (developer_entityparsing.integer)
912 Con_Printf(" \"%s\"\n", com_token);
914 if (com_token[0] == '}')
915 prog->error_cmd("PRVM_ED_ParseGlobals: closing brace without data");
917 key = PRVM_ED_FindGlobal (prog, keyname);
920 Con_DPrintf("'%s' is not a global on %s\n", keyname, prog->name);
924 if (!PRVM_ED_ParseEpair(prog, NULL, key, com_token, true))
925 prog->error_cmd("PRVM_ED_ParseGlobals: parse error");
929 //============================================================================
936 Can parse either fields or globals
937 returns false if error
940 qboolean PRVM_ED_ParseEpair(prvm_prog_t *prog, prvm_edict_t *ent, ddef_t *key, const char *s, qboolean parsebackslash)
949 val = (prvm_eval_t *)(ent->fields.fp + key->ofs);
951 val = (prvm_eval_t *)(prog->globals.fp + key->ofs);
952 switch (key->type & ~DEF_SAVEGLOBAL)
955 l = (int)strlen(s) + 1;
956 val->string = PRVM_AllocString(prog, l, &new_p);
957 for (i = 0;i < l;i++)
959 if (s[i] == '\\' && s[i+1] && parsebackslash)
964 else if (s[i] == 'r')
975 while (*s && ISWHITESPACE(*s))
977 val->_float = atof(s);
981 for (i = 0;i < 3;i++)
983 while (*s && ISWHITESPACE(*s))
987 val->vector[i] = atof(s);
988 while (!ISWHITESPACE(*s))
996 while (*s && ISWHITESPACE(*s))
999 if (i >= prog->limit_edicts)
1000 Con_Printf("PRVM_ED_ParseEpair: ev_entity reference too large (edict %u >= MAX_EDICTS %u) on %s\n", (unsigned int)i, prog->limit_edicts, prog->name);
1001 while (i >= prog->max_edicts)
1002 PRVM_MEM_IncreaseEdicts(prog);
1003 // if IncreaseEdicts was called the base pointer needs to be updated
1005 val = (prvm_eval_t *)(ent->fields.fp + key->ofs);
1006 val->edict = PRVM_EDICT_TO_PROG(PRVM_EDICT_NUM((int)i));
1012 Con_DPrintf("PRVM_ED_ParseEpair: Bogus field name %s in %s\n", s, prog->name);
1015 def = PRVM_ED_FindField(prog, s + 1);
1018 Con_DPrintf("PRVM_ED_ParseEpair: Can't find field %s in %s\n", s, prog->name);
1021 val->_int = def->ofs;
1025 func = PRVM_ED_FindFunction(prog, s);
1028 Con_Printf("PRVM_ED_ParseEpair: Can't find function %s in %s\n", s, prog->name);
1031 val->function = func - prog->functions;
1035 Con_Printf("PRVM_ED_ParseEpair: Unknown key->type %i for key \"%s\" on %s\n", key->type, PRVM_GetString(prog, key->s_name), prog->name);
1045 Console command to send a string to QC function GameCommand of the
1049 sv_cmd adminmsg 3 "do not teamkill"
1050 cl_cmd someclientcommand
1051 menu_cmd somemenucommand
1053 All progs can support this extension; sg calls it in server QC, cg in client
1057 static void PRVM_GameCommand(const char *whichprogs, const char *whichcmd)
1062 Con_Printf("%s text...\n", whichcmd);
1066 if (!(prog = PRVM_FriendlyProgFromString(whichprogs)))
1069 if(!PRVM_allfunction(GameCommand))
1071 Con_Printf("%s program do not support GameCommand!\n", whichprogs);
1075 int restorevm_tempstringsbuf_cursize;
1080 restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
1081 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(prog, s ? s : "");
1082 prog->ExecuteProgram(prog, PRVM_allfunction(GameCommand), "QC function GameCommand is missing");
1083 prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1086 static void PRVM_GameCommand_Server_f(void)
1088 PRVM_GameCommand("server", "sv_cmd");
1090 static void PRVM_GameCommand_Client_f(void)
1092 PRVM_GameCommand("client", "cl_cmd");
1094 static void PRVM_GameCommand_Menu_f(void)
1096 PRVM_GameCommand("menu", "menu_cmd");
1103 Console command to load a field of a specified edict
1106 static void PRVM_ED_EdictGet_f(void)
1113 char valuebuf[MAX_INPUTLINE];
1115 if(Cmd_Argc() != 4 && Cmd_Argc() != 5)
1117 Con_Print("prvm_edictget <program name> <edict number> <field> [<cvar>]\n");
1121 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
1124 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1126 if((key = PRVM_ED_FindField(prog, Cmd_Argv(3))) == 0)
1128 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1132 v = (prvm_eval_t *)(ed->fields.fp + key->ofs);
1133 s = PRVM_UglyValueString(prog, (etype_t)key->type, v, valuebuf, sizeof(valuebuf));
1136 cvar_t *cvar = Cvar_FindVar(Cmd_Argv(4));
1137 if (cvar && cvar->flags & CVAR_READONLY)
1139 Con_Printf("prvm_edictget: %s is read-only\n", cvar->name);
1142 Cvar_Get(Cmd_Argv(4), s, 0, NULL);
1145 Con_Printf("%s\n", s);
1151 static void PRVM_ED_GlobalGet_f(void)
1157 char valuebuf[MAX_INPUTLINE];
1159 if(Cmd_Argc() != 3 && Cmd_Argc() != 4)
1161 Con_Print("prvm_globalget <program name> <global> [<cvar>]\n");
1165 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
1168 key = PRVM_ED_FindGlobal(prog, Cmd_Argv(2));
1171 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
1175 v = (prvm_eval_t *) &prog->globals.fp[key->ofs];
1176 s = PRVM_UglyValueString(prog, (etype_t)key->type, v, valuebuf, sizeof(valuebuf));
1179 cvar_t *cvar = Cvar_FindVar(Cmd_Argv(3));
1180 if (cvar && cvar->flags & CVAR_READONLY)
1182 Con_Printf("prvm_globalget: %s is read-only\n", cvar->name);
1185 Cvar_Get(Cmd_Argv(3), s, 0, NULL);
1188 Con_Printf("%s\n", s);
1198 Console command to set a field of a specified edict
1201 static void PRVM_ED_EdictSet_f(void)
1209 Con_Print("prvm_edictset <program name> <edict number> <field> <value>\n");
1213 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
1216 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1218 if((key = PRVM_ED_FindField(prog, Cmd_Argv(3))) == 0)
1219 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1221 PRVM_ED_ParseEpair(prog, ed, key, Cmd_Argv(4), true);
1225 ====================
1228 Parses an edict out of the given string, returning the new position
1229 ed should be a properly initialized empty edict.
1230 Used for initial level load and for savegames.
1231 ====================
1233 const char *PRVM_ED_ParseEdict (prvm_prog_t *prog, const char *data, prvm_edict_t *ent)
1243 // go through all the dictionary pairs
1247 if (!COM_ParseToken_Simple(&data, false, false, true))
1248 prog->error_cmd("PRVM_ED_ParseEdict: EOF without closing brace");
1249 if (developer_entityparsing.integer)
1250 Con_Printf("Key: \"%s\"", com_token);
1251 if (com_token[0] == '}')
1254 // anglehack is to allow QuakeEd to write single scalar angles
1255 // and allow them to be turned into vectors. (FIXME...)
1256 if (!strcmp(com_token, "angle"))
1258 strlcpy (com_token, "angles", sizeof(com_token));
1264 // FIXME: change light to _light to get rid of this hack
1265 if (!strcmp(com_token, "light"))
1266 strlcpy (com_token, "light_lev", sizeof(com_token)); // hack for single light def
1268 strlcpy (keyname, com_token, sizeof(keyname));
1270 // another hack to fix keynames with trailing spaces
1271 n = strlen(keyname);
1272 while (n && keyname[n-1] == ' ')
1279 if (!COM_ParseToken_Simple(&data, false, false, true))
1280 prog->error_cmd("PRVM_ED_ParseEdict: EOF without closing brace");
1281 if (developer_entityparsing.integer)
1282 Con_Printf(" \"%s\"\n", com_token);
1284 if (com_token[0] == '}')
1285 prog->error_cmd("PRVM_ED_ParseEdict: closing brace without data");
1289 // ignore attempts to set key "" (this problem occurs in nehahra neh1m8.bsp)
1293 // keynames with a leading underscore are used for utility comments,
1294 // and are immediately discarded by quake
1295 if (keyname[0] == '_')
1298 key = PRVM_ED_FindField (prog, keyname);
1301 Con_DPrintf("%s: '%s' is not a field\n", prog->name, keyname);
1308 strlcpy (temp, com_token, sizeof(temp));
1309 dpsnprintf (com_token, sizeof(com_token), "0 %s 0", temp);
1312 if (!PRVM_ED_ParseEpair(prog, ent, key, com_token, strcmp(keyname, "wad") != 0))
1313 prog->error_cmd("PRVM_ED_ParseEdict: parse error");
1317 ent->priv.required->free = true;
1325 PRVM_ED_LoadFromFile
1327 The entities are directly placed in the array, rather than allocated with
1328 PRVM_ED_Alloc, because otherwise an error loading the map would have entity
1329 number references out of order.
1331 Creates a server's entity / program execution context by
1332 parsing textual entity definitions out of an ent file.
1334 Used for both fresh maps and savegame loads. A fresh map would also need
1335 to call PRVM_ED_CallSpawnFunctions () to let the objects initialize themselves.
1338 void PRVM_ED_LoadFromFile (prvm_prog_t *prog, const char *data)
1341 int parsed, inhibited, spawned, died;
1342 const char *funcname;
1351 prvm_reuseedicts_always_allow = realtime;
1356 // parse the opening brace
1357 if (!COM_ParseToken_Simple(&data, false, false, true))
1359 if (com_token[0] != '{')
1360 prog->error_cmd("PRVM_ED_LoadFromFile: %s: found %s when expecting {", prog->name, com_token);
1362 // CHANGED: this is not conform to PR_LoadFromFile
1363 if(prog->loadintoworld)
1365 prog->loadintoworld = false;
1366 ent = PRVM_EDICT_NUM(0);
1369 ent = PRVM_ED_Alloc(prog);
1372 if (ent != prog->edicts) // hack
1373 memset (ent->fields.fp, 0, prog->entityfields * sizeof(prvm_vec_t));
1375 data = PRVM_ED_ParseEdict (prog, data, ent);
1378 // remove the entity ?
1379 if(!prog->load_edict(prog, ent))
1381 PRVM_ED_Free(prog, ent);
1386 if (PRVM_serverfunction(SV_OnEntityPreSpawnFunction))
1389 PRVM_serverglobalfloat(time) = sv.time;
1390 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1391 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityPreSpawnFunction), "QC function SV_OnEntityPreSpawnFunction is missing");
1394 if(ent->priv.required->free)
1401 // immediately call spawn function, but only if there is a self global and a classname
1403 if(!ent->priv.required->free)
1405 if (!PRVM_alledictstring(ent, classname))
1407 Con_Print("No classname for:\n");
1408 PRVM_ED_Print(prog, ent, NULL);
1409 PRVM_ED_Free (prog, ent);
1413 // look for the spawn function
1414 funcname = PRVM_GetString(prog, PRVM_alledictstring(ent, classname));
1415 func = PRVM_ED_FindFunction (prog, va(vabuf, sizeof(vabuf), "spawnfunc_%s", funcname));
1417 if(!PRVM_allglobalfloat(require_spawnfunc_prefix))
1418 func = PRVM_ED_FindFunction (prog, funcname);
1422 // check for OnEntityNoSpawnFunction
1423 if (PRVM_serverfunction(SV_OnEntityNoSpawnFunction))
1426 PRVM_serverglobalfloat(time) = sv.time;
1427 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1428 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityNoSpawnFunction), "QC function SV_OnEntityNoSpawnFunction is missing");
1432 if (developer.integer > 0) // don't confuse non-developers with errors
1434 Con_Print("No spawn function for:\n");
1435 PRVM_ED_Print(prog, ent, NULL);
1437 PRVM_ED_Free (prog, ent);
1438 continue; // not included in "inhibited" count
1444 PRVM_serverglobalfloat(time) = sv.time;
1445 PRVM_allglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1446 prog->ExecuteProgram(prog, func - prog->functions, "");
1450 if(!ent->priv.required->free)
1451 if (PRVM_serverfunction(SV_OnEntityPostSpawnFunction))
1454 PRVM_serverglobalfloat(time) = sv.time;
1455 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1456 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityPostSpawnFunction), "QC function SV_OnEntityPostSpawnFunction is missing");
1460 if (ent->priv.required->free)
1464 Con_DPrintf("%s: %i new entities parsed, %i new inhibited, %i (%i new) spawned (whereas %i removed self, %i stayed)\n", prog->name, parsed, inhibited, prog->num_edicts, spawned, died, spawned - died);
1466 prvm_reuseedicts_always_allow = 0;
1469 static void PRVM_FindOffsets(prvm_prog_t *prog)
1471 // field and global searches use -1 for NULL
1472 memset(&prog->fieldoffsets, -1, sizeof(prog->fieldoffsets));
1473 memset(&prog->globaloffsets, -1, sizeof(prog->globaloffsets));
1474 // function searches use 0 for NULL
1475 memset(&prog->funcoffsets, 0, sizeof(prog->funcoffsets));
1476 #define PRVM_DECLARE_serverglobalfloat(x)
1477 #define PRVM_DECLARE_serverglobalvector(x)
1478 #define PRVM_DECLARE_serverglobalstring(x)
1479 #define PRVM_DECLARE_serverglobaledict(x)
1480 #define PRVM_DECLARE_serverglobalfunction(x)
1481 #define PRVM_DECLARE_clientglobalfloat(x)
1482 #define PRVM_DECLARE_clientglobalvector(x)
1483 #define PRVM_DECLARE_clientglobalstring(x)
1484 #define PRVM_DECLARE_clientglobaledict(x)
1485 #define PRVM_DECLARE_clientglobalfunction(x)
1486 #define PRVM_DECLARE_menuglobalfloat(x)
1487 #define PRVM_DECLARE_menuglobalvector(x)
1488 #define PRVM_DECLARE_menuglobalstring(x)
1489 #define PRVM_DECLARE_menuglobaledict(x)
1490 #define PRVM_DECLARE_menuglobalfunction(x)
1491 #define PRVM_DECLARE_serverfieldfloat(x)
1492 #define PRVM_DECLARE_serverfieldvector(x)
1493 #define PRVM_DECLARE_serverfieldstring(x)
1494 #define PRVM_DECLARE_serverfieldedict(x)
1495 #define PRVM_DECLARE_serverfieldfunction(x)
1496 #define PRVM_DECLARE_clientfieldfloat(x)
1497 #define PRVM_DECLARE_clientfieldvector(x)
1498 #define PRVM_DECLARE_clientfieldstring(x)
1499 #define PRVM_DECLARE_clientfieldedict(x)
1500 #define PRVM_DECLARE_clientfieldfunction(x)
1501 #define PRVM_DECLARE_menufieldfloat(x)
1502 #define PRVM_DECLARE_menufieldvector(x)
1503 #define PRVM_DECLARE_menufieldstring(x)
1504 #define PRVM_DECLARE_menufieldedict(x)
1505 #define PRVM_DECLARE_menufieldfunction(x)
1506 #define PRVM_DECLARE_serverfunction(x)
1507 #define PRVM_DECLARE_clientfunction(x)
1508 #define PRVM_DECLARE_menufunction(x)
1509 #define PRVM_DECLARE_field(x) prog->fieldoffsets.x = PRVM_ED_FindFieldOffset(prog, #x);
1510 #define PRVM_DECLARE_global(x) prog->globaloffsets.x = PRVM_ED_FindGlobalOffset(prog, #x);
1511 #define PRVM_DECLARE_function(x) prog->funcoffsets.x = PRVM_ED_FindFunctionOffset(prog, #x);
1512 #include "prvm_offsets.h"
1513 #undef PRVM_DECLARE_serverglobalfloat
1514 #undef PRVM_DECLARE_serverglobalvector
1515 #undef PRVM_DECLARE_serverglobalstring
1516 #undef PRVM_DECLARE_serverglobaledict
1517 #undef PRVM_DECLARE_serverglobalfunction
1518 #undef PRVM_DECLARE_clientglobalfloat
1519 #undef PRVM_DECLARE_clientglobalvector
1520 #undef PRVM_DECLARE_clientglobalstring
1521 #undef PRVM_DECLARE_clientglobaledict
1522 #undef PRVM_DECLARE_clientglobalfunction
1523 #undef PRVM_DECLARE_menuglobalfloat
1524 #undef PRVM_DECLARE_menuglobalvector
1525 #undef PRVM_DECLARE_menuglobalstring
1526 #undef PRVM_DECLARE_menuglobaledict
1527 #undef PRVM_DECLARE_menuglobalfunction
1528 #undef PRVM_DECLARE_serverfieldfloat
1529 #undef PRVM_DECLARE_serverfieldvector
1530 #undef PRVM_DECLARE_serverfieldstring
1531 #undef PRVM_DECLARE_serverfieldedict
1532 #undef PRVM_DECLARE_serverfieldfunction
1533 #undef PRVM_DECLARE_clientfieldfloat
1534 #undef PRVM_DECLARE_clientfieldvector
1535 #undef PRVM_DECLARE_clientfieldstring
1536 #undef PRVM_DECLARE_clientfieldedict
1537 #undef PRVM_DECLARE_clientfieldfunction
1538 #undef PRVM_DECLARE_menufieldfloat
1539 #undef PRVM_DECLARE_menufieldvector
1540 #undef PRVM_DECLARE_menufieldstring
1541 #undef PRVM_DECLARE_menufieldedict
1542 #undef PRVM_DECLARE_menufieldfunction
1543 #undef PRVM_DECLARE_serverfunction
1544 #undef PRVM_DECLARE_clientfunction
1545 #undef PRVM_DECLARE_menufunction
1546 #undef PRVM_DECLARE_field
1547 #undef PRVM_DECLARE_global
1548 #undef PRVM_DECLARE_function
1553 typedef struct dpfield_s
1560 #define DPFIELDS (sizeof(dpfields) / sizeof(dpfield_t))
1562 dpfield_t dpfields[] =
1573 #define PO_HASHSIZE 16384
1574 typedef struct po_string_s
1577 struct po_string_s *nextonhashchain;
1582 po_string_t *hashtable[PO_HASHSIZE];
1585 static void PRVM_PO_UnparseString(char *out, const char *in, size_t outsize)
1594 case '\a': if(outsize >= 2) { *out++ = '\\'; *out++ = 'a'; outsize -= 2; } break;
1595 case '\b': if(outsize >= 2) { *out++ = '\\'; *out++ = 'b'; outsize -= 2; } break;
1596 case '\t': if(outsize >= 2) { *out++ = '\\'; *out++ = 't'; outsize -= 2; } break;
1597 case '\r': if(outsize >= 2) { *out++ = '\\'; *out++ = 'r'; outsize -= 2; } break;
1598 case '\n': if(outsize >= 2) { *out++ = '\\'; *out++ = 'n'; outsize -= 2; } break;
1599 case '\\': if(outsize >= 2) { *out++ = '\\'; *out++ = '\\'; outsize -= 2; } break;
1600 case '"': if(outsize >= 2) { *out++ = '\\'; *out++ = '"'; outsize -= 2; } break;
1602 if(*in >= 0 && *in <= 0x1F)
1607 *out++ = '0' + ((*in & 0700) >> 6);
1608 *out++ = '0' + ((*in & 0070) >> 3);
1609 *out++ = '0' + (*in & 0007) ;
1626 static void PRVM_PO_ParseString(char *out, const char *in, size_t outsize)
1639 case 'a': if(outsize > 0) { *out++ = '\a'; --outsize; } break;
1640 case 'b': if(outsize > 0) { *out++ = '\b'; --outsize; } break;
1641 case 't': if(outsize > 0) { *out++ = '\t'; --outsize; } break;
1642 case 'r': if(outsize > 0) { *out++ = '\r'; --outsize; } break;
1643 case 'n': if(outsize > 0) { *out++ = '\n'; --outsize; } break;
1644 case '\\': if(outsize > 0) { *out++ = '\\'; --outsize; } break;
1645 case '"': if(outsize > 0) { *out++ = '"'; --outsize; } break;
1646 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
1650 if(*in >= '0' && *in <= '7')
1653 *out = (*out << 3) | (*in - '0');
1656 if(*in >= '0' && *in <= '7')
1659 *out = (*out << 3) | (*in - '0');
1670 if(outsize > 0) { *out++ = *in; --outsize; }
1685 static po_t *PRVM_PO_Load(const char *filename, mempool_t *pool)
1690 char inbuf[MAX_INPUTLINE];
1691 char decodedbuf[MAX_INPUTLINE];
1694 po_string_t thisstr;
1695 const char *buf = (const char *) FS_LoadFile(filename, pool, true, NULL);
1700 memset(&thisstr, 0, sizeof(thisstr)); // hush compiler warning
1702 po = (po_t *)Mem_Alloc(pool, sizeof(*po));
1703 memset(po, 0, sizeof(*po));
1711 p = strchr(p, '\n');
1717 if(*p == '\r' || *p == '\n')
1722 if(!strncmp(p, "msgid \"", 7))
1727 else if(!strncmp(p, "msgstr \"", 8))
1734 p = strchr(p, '\n');
1744 q = strchr(p, '\n');
1751 if((size_t)(q - p) >= (size_t) sizeof(inbuf))
1753 strlcpy(inbuf, p, q - p); // not - 1, because this adds a NUL
1754 PRVM_PO_ParseString(decodedbuf + decodedpos, inbuf, sizeof(decodedbuf) - decodedpos);
1755 decodedpos += strlen(decodedbuf + decodedpos);
1765 Mem_Free(thisstr.key);
1766 thisstr.key = (char *)Mem_Alloc(pool, decodedpos + 1);
1767 memcpy(thisstr.key, decodedbuf, decodedpos + 1);
1769 else if(decodedpos > 0 && thisstr.key) // skip empty translation results
1771 thisstr.value = (char *)Mem_Alloc(pool, decodedpos + 1);
1772 memcpy(thisstr.value, decodedbuf, decodedpos + 1);
1773 hashindex = CRC_Block((const unsigned char *) thisstr.key, strlen(thisstr.key)) % PO_HASHSIZE;
1774 thisstr.nextonhashchain = po->hashtable[hashindex];
1775 po->hashtable[hashindex] = (po_string_t *)Mem_Alloc(pool, sizeof(thisstr));
1776 memcpy(po->hashtable[hashindex], &thisstr, sizeof(thisstr));
1777 memset(&thisstr, 0, sizeof(thisstr));
1781 Mem_Free((char *) buf);
1784 static const char *PRVM_PO_Lookup(po_t *po, const char *str)
1786 int hashindex = CRC_Block((const unsigned char *) str, strlen(str)) % PO_HASHSIZE;
1787 po_string_t *p = po->hashtable[hashindex];
1790 if(!strcmp(str, p->key))
1792 p = p->nextonhashchain;
1796 static void PRVM_PO_Destroy(po_t *po)
1799 for(i = 0; i < PO_HASHSIZE; ++i)
1801 po_string_t *p = po->hashtable[i];
1805 p = p->nextonhashchain;
1814 void PRVM_LeakTest(prvm_prog_t *prog);
1815 void PRVM_Prog_Reset(prvm_prog_t *prog)
1817 PRVM_LeakTest(prog);
1818 prog->reset_cmd(prog);
1819 Mem_FreePool(&prog->progs_mempool);
1821 PRVM_PO_Destroy((po_t *) prog->po);
1822 memset(prog,0,sizeof(prvm_prog_t));
1830 static void PRVM_LoadLNO( prvm_prog_t *prog, const char *progname ) {
1831 fs_offset_t filesize;
1833 unsigned int *header;
1836 FS_StripExtension( progname, filename, sizeof( filename ) );
1837 strlcat( filename, ".lno", sizeof( filename ) );
1839 lno = FS_LoadFile( filename, tempmempool, false, &filesize );
1845 <Spike> SafeWrite (h, &lnotype, sizeof(int));
1846 <Spike> SafeWrite (h, &version, sizeof(int));
1847 <Spike> SafeWrite (h, &numglobaldefs, sizeof(int));
1848 <Spike> SafeWrite (h, &numpr_globals, sizeof(int));
1849 <Spike> SafeWrite (h, &numfielddefs, sizeof(int));
1850 <Spike> SafeWrite (h, &numstatements, sizeof(int));
1851 <Spike> SafeWrite (h, statement_linenums, numstatements*sizeof(int));
1853 if ((unsigned int)filesize < (6 + prog->progs_numstatements) * sizeof(int))
1859 header = (unsigned int *) lno;
1860 if( header[ 0 ] == *(unsigned int *) "LNOF" &&
1861 LittleLong( header[ 1 ] ) == 1 &&
1862 (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs_numglobaldefs &&
1863 (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs_numglobals &&
1864 (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs_numfielddefs &&
1865 (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs_numstatements )
1867 prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof( int ) );
1868 memcpy( prog->statement_linenums, (int *) lno + 6, prog->progs_numstatements * sizeof( int ) );
1878 void PRVM_Prog_Load(prvm_prog_t *prog, 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)
1881 dprograms_t *dprograms;
1882 dstatement_t *instatements;
1883 ddef_t *infielddefs;
1884 ddef_t *inglobaldefs;
1886 dfunction_t *infunctions;
1888 fs_offset_t filesize;
1889 int requiredglobalspace;
1904 prog->error_cmd("PRVM_LoadProgs: there is already a %s program loaded!", prog->name );
1906 Host_LockSession(); // all progs can use the session cvar
1907 Crypto_LoadKeys(); // all progs might use the keys at init time
1909 dprograms = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
1910 if (dprograms == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
1911 prog->error_cmd("PRVM_LoadProgs: couldn't load %s for %s", filename, prog->name);
1912 // TODO bounds check header fields (e.g. numstatements), they must never go behind end of file
1914 prog->profiletime = Sys_DirtyTime();
1915 prog->starttime = realtime;
1917 Con_DPrintf("%s programs occupy %iK.\n", prog->name, (int)(filesize/1024));
1919 requiredglobalspace = 0;
1920 for (i = 0;i < numrequiredglobals;i++)
1921 requiredglobalspace += required_global[i].type == ev_vector ? 3 : 1;
1923 prog->filecrc = CRC_Block((unsigned char *)dprograms, filesize);
1925 // byte swap the header
1926 prog->progs_version = LittleLong(dprograms->version);
1927 prog->progs_crc = LittleLong(dprograms->crc);
1928 if (prog->progs_version != PROG_VERSION)
1929 prog->error_cmd("%s: %s has wrong version number (%i should be %i)", prog->name, filename, prog->progs_version, PROG_VERSION);
1930 instatements = (dstatement_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_statements));
1931 prog->progs_numstatements = LittleLong(dprograms->numstatements);
1932 inglobaldefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globaldefs));
1933 prog->progs_numglobaldefs = LittleLong(dprograms->numglobaldefs);
1934 infielddefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_fielddefs));
1935 prog->progs_numfielddefs = LittleLong(dprograms->numfielddefs);
1936 infunctions = (dfunction_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_functions));
1937 prog->progs_numfunctions = LittleLong(dprograms->numfunctions);
1938 instrings = (char *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_strings));
1939 prog->progs_numstrings = LittleLong(dprograms->numstrings);
1940 inglobals = (int *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globals));
1941 prog->progs_numglobals = LittleLong(dprograms->numglobals);
1942 prog->progs_entityfields = LittleLong(dprograms->entityfields);
1944 prog->numstatements = prog->progs_numstatements;
1945 prog->numglobaldefs = prog->progs_numglobaldefs;
1946 prog->numfielddefs = prog->progs_numfielddefs;
1947 prog->numfunctions = prog->progs_numfunctions;
1948 prog->numstrings = prog->progs_numstrings;
1949 prog->numglobals = prog->progs_numglobals;
1950 prog->entityfields = prog->progs_entityfields;
1952 if (LittleLong(dprograms->ofs_strings) + prog->progs_numstrings > (int)filesize)
1953 prog->error_cmd("%s: %s strings go past end of file", prog->name, filename);
1954 prog->strings = (char *)Mem_Alloc(prog->progs_mempool, prog->progs_numstrings);
1955 memcpy(prog->strings, instrings, prog->progs_numstrings);
1956 prog->stringssize = prog->progs_numstrings;
1958 prog->numknownstrings = 0;
1959 prog->maxknownstrings = 0;
1960 prog->knownstrings = NULL;
1961 prog->knownstrings_freeable = NULL;
1963 Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
1965 // we need to expand the globaldefs and fielddefs to include engine defs
1966 prog->globaldefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobaldefs + numrequiredglobals) * sizeof(ddef_t));
1967 prog->globals.fp = (prvm_vec_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobals + requiredglobalspace + 2) * sizeof(prvm_vec_t));
1968 // + 2 is because of an otherwise occurring overrun in RETURN instruction
1969 // when trying to return the last or second-last global
1970 // (RETURN always returns a vector, there is no RETURN_F instruction)
1971 prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numfielddefs + numrequiredfields) * sizeof(ddef_t));
1972 // we need to convert the statements to our memory format
1973 prog->statements = (mstatement_t *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(mstatement_t));
1974 // allocate space for profiling statement usage
1975 prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(*prog->statement_profile));
1976 // functions need to be converted to the memory format
1977 prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs_numfunctions);
1979 for (i = 0;i < prog->progs_numfunctions;i++)
1981 prog->functions[i].first_statement = LittleLong(infunctions[i].first_statement);
1982 prog->functions[i].parm_start = LittleLong(infunctions[i].parm_start);
1983 prog->functions[i].s_name = LittleLong(infunctions[i].s_name);
1984 prog->functions[i].s_file = LittleLong(infunctions[i].s_file);
1985 prog->functions[i].numparms = LittleLong(infunctions[i].numparms);
1986 prog->functions[i].locals = LittleLong(infunctions[i].locals);
1987 memcpy(prog->functions[i].parm_size, infunctions[i].parm_size, sizeof(infunctions[i].parm_size));
1988 if(prog->functions[i].first_statement >= prog->numstatements)
1989 prog->error_cmd("PRVM_LoadProgs: out of bounds function statement (function %d) in %s", i, prog->name);
1990 // TODO bounds check parm_start, s_name, s_file, numparms, locals, parm_size
1993 // copy the globaldefs to the new globaldefs list
1994 for (i=0 ; i<prog->numglobaldefs ; i++)
1996 prog->globaldefs[i].type = LittleShort(inglobaldefs[i].type);
1997 prog->globaldefs[i].ofs = LittleShort(inglobaldefs[i].ofs);
1998 prog->globaldefs[i].s_name = LittleLong(inglobaldefs[i].s_name);
1999 // TODO bounds check ofs, s_name
2002 // append the required globals
2003 for (i = 0;i < numrequiredglobals;i++)
2005 prog->globaldefs[prog->numglobaldefs].type = required_global[i].type;
2006 prog->globaldefs[prog->numglobaldefs].ofs = prog->numglobals;
2007 prog->globaldefs[prog->numglobaldefs].s_name = PRVM_SetEngineString(prog, required_global[i].name);
2008 if (prog->globaldefs[prog->numglobaldefs].type == ev_vector)
2009 prog->numglobals += 3;
2012 prog->numglobaldefs++;
2015 // copy the progs fields to the new fields list
2016 for (i = 0;i < prog->numfielddefs;i++)
2018 prog->fielddefs[i].type = LittleShort(infielddefs[i].type);
2019 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
2020 prog->error_cmd("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", prog->name);
2021 prog->fielddefs[i].ofs = LittleShort(infielddefs[i].ofs);
2022 prog->fielddefs[i].s_name = LittleLong(infielddefs[i].s_name);
2023 // TODO bounds check ofs, s_name
2026 // append the required fields
2027 for (i = 0;i < numrequiredfields;i++)
2029 prog->fielddefs[prog->numfielddefs].type = required_field[i].type;
2030 prog->fielddefs[prog->numfielddefs].ofs = prog->entityfields;
2031 prog->fielddefs[prog->numfielddefs].s_name = PRVM_SetEngineString(prog, required_field[i].name);
2032 if (prog->fielddefs[prog->numfielddefs].type == ev_vector)
2033 prog->entityfields += 3;
2035 prog->entityfields++;
2036 prog->numfielddefs++;
2039 // LordHavoc: TODO: reorder globals to match engine struct
2040 // LordHavoc: TODO: reorder fields to match engine struct
2041 #define remapglobal(index) (index)
2042 #define remapfield(index) (index)
2045 // FIXME: LordHavoc: this uses a crude way to identify integer constants, rather than checking for matching globaldefs and checking their type
2046 for (i = 0;i < prog->progs_numglobals;i++)
2048 u.i = LittleLong(inglobals[i]);
2049 // most globals are 0, we only need to deal with the ones that are not
2052 d = u.i & 0xFF800000;
2053 if ((d == 0xFF800000) || (d == 0))
2055 // Looks like an integer (expand to int64)
2056 prog->globals.ip[remapglobal(i)] = u.i;
2060 // Looks like a float (expand to double)
2061 prog->globals.fp[remapglobal(i)] = u.f;
2066 // LordHavoc: TODO: support 32bit progs statement formats
2067 // copy, remap globals in statements, bounds check
2068 for (i = 0;i < prog->progs_numstatements;i++)
2070 op = (opcode_t)LittleShort(instatements[i].op);
2071 a = (unsigned short)LittleShort(instatements[i].a);
2072 b = (unsigned short)LittleShort(instatements[i].b);
2073 c = (unsigned short)LittleShort(instatements[i].c);
2079 if (a >= prog->progs_numglobals || b + i < 0 || b + i >= prog->progs_numstatements)
2080 prog->error_cmd("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", i, prog->name);
2081 prog->statements[i].op = op;
2082 prog->statements[i].operand[0] = remapglobal(a);
2083 prog->statements[i].operand[1] = -1;
2084 prog->statements[i].operand[2] = -1;
2085 prog->statements[i].jumpabsolute = i + b;
2089 if (a + i < 0 || a + i >= prog->progs_numstatements)
2090 prog->error_cmd("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, prog->name);
2091 prog->statements[i].op = op;
2092 prog->statements[i].operand[0] = -1;
2093 prog->statements[i].operand[1] = -1;
2094 prog->statements[i].operand[2] = -1;
2095 prog->statements[i].jumpabsolute = i + a;
2098 Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", (int)op, i, prog->name);
2099 // global global global
2134 if (a >= prog->progs_numglobals || b >= prog->progs_numglobals || c >= prog->progs_numglobals)
2135 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
2136 prog->statements[i].op = op;
2137 prog->statements[i].operand[0] = remapglobal(a);
2138 prog->statements[i].operand[1] = remapglobal(b);
2139 prog->statements[i].operand[2] = remapglobal(c);
2140 prog->statements[i].jumpabsolute = -1;
2142 // global none global
2148 if (a >= prog->progs_numglobals || c >= prog->progs_numglobals)
2149 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2150 prog->statements[i].op = op;
2151 prog->statements[i].operand[0] = remapglobal(a);
2152 prog->statements[i].operand[1] = -1;
2153 prog->statements[i].operand[2] = remapglobal(c);
2154 prog->statements[i].jumpabsolute = -1;
2170 if (a >= prog->progs_numglobals || b >= prog->progs_numglobals)
2171 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2172 prog->statements[i].op = op;
2173 prog->statements[i].operand[0] = remapglobal(a);
2174 prog->statements[i].operand[1] = remapglobal(b);
2175 prog->statements[i].operand[2] = -1;
2176 prog->statements[i].jumpabsolute = -1;
2190 if ( a >= prog->progs_numglobals)
2191 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2192 prog->statements[i].op = op;
2193 prog->statements[i].operand[0] = remapglobal(a);
2194 prog->statements[i].operand[1] = -1;
2195 prog->statements[i].operand[2] = -1;
2196 prog->statements[i].jumpabsolute = -1;
2200 if(prog->numstatements < 1)
2202 prog->error_cmd("PRVM_LoadProgs: empty program in %s", prog->name);
2204 else switch(prog->statements[prog->numstatements - 1].op)
2211 prog->error_cmd("PRVM_LoadProgs: program may fall off the edge (does not end with RETURN, GOTO or DONE) in %s", prog->name);
2215 // we're done with the file now
2216 Mem_Free(dprograms);
2219 // check required functions
2220 for(i=0 ; i < numrequiredfunc ; i++)
2221 if(PRVM_ED_FindFunction(prog, required_func[i]) == 0)
2222 prog->error_cmd("%s: %s not found in %s",prog->name, required_func[i], filename);
2224 PRVM_LoadLNO(prog, filename);
2226 PRVM_Init_Exec(prog);
2228 if(*prvm_language.string)
2229 // in CSQC we really shouldn't be able to change how stuff works... sorry for now
2230 // later idea: include a list of authorized .po file checksums with the csprogs
2232 qboolean deftrans = prog == CLVM_prog;
2233 const char *realfilename = (prog != CLVM_prog ? filename : csqc_progname.string);
2234 if(deftrans) // once we have dotranslate_ strings, ALWAYS use the opt-in method!
2236 for (i=0 ; i<prog->numglobaldefs ; i++)
2239 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2240 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2241 if(name && !strncmp(name, "dotranslate_", 12))
2248 if(!strcmp(prvm_language.string, "dump"))
2250 qfile_t *f = FS_OpenRealFile(va(vabuf, sizeof(vabuf), "%s.pot", realfilename), "w", false);
2251 Con_Printf("Dumping to %s.pot\n", realfilename);
2254 for (i=0 ; i<prog->numglobaldefs ; i++)
2257 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2258 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2259 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2261 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2262 const char *value = PRVM_GetString(prog, val->string);
2265 char buf[MAX_INPUTLINE];
2266 PRVM_PO_UnparseString(buf, value, sizeof(buf));
2267 FS_Printf(f, "msgid \"%s\"\nmsgstr \"\"\n\n", buf);
2276 po_t *po = PRVM_PO_Load(va(vabuf, sizeof(vabuf), "%s.%s.po", realfilename, prvm_language.string), prog->progs_mempool);
2279 for (i=0 ; i<prog->numglobaldefs ; i++)
2282 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2283 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2284 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2286 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2287 const char *value = PRVM_GetString(prog, val->string);
2290 value = PRVM_PO_Lookup(po, value);
2292 val->string = PRVM_SetEngineString(prog, value);
2300 for (i=0 ; i<prog->numglobaldefs ; i++)
2303 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2304 //Con_Printf("found var %s\n", name);
2306 && !strncmp(name, "autocvar_", 9)
2307 && !(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
2310 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2311 cvar_t *cvar = Cvar_FindVar(name + 9);
2312 //Con_Printf("PRVM_LoadProgs: autocvar global %s in %s, processing...\n", name, prog->name);
2317 Con_DPrintf("PRVM_LoadProgs: no cvar for autocvar global %s in %s, creating...\n", name, prog->name);
2318 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2321 if((float)((int)(val->_float)) == val->_float)
2322 dpsnprintf(buf, sizeof(buf), "%i", (int)(val->_float));
2324 dpsnprintf(buf, sizeof(buf), "%.9g", val->_float);
2328 dpsnprintf(buf, sizeof(buf), "%.9g %.9g %.9g", val->vector[0], val->vector[1], val->vector[2]); value = buf;
2331 value = PRVM_GetString(prog, val->string);
2334 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2337 cvar = Cvar_Get(name + 9, value, 0, NULL);
2338 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2340 val->string = PRVM_SetEngineString(prog, cvar->string);
2341 cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2344 prog->error_cmd("PRVM_LoadProgs: could not create cvar for autocvar global %s in %s", name, prog->name);
2345 cvar->globaldefindex_progid[prog - prvm_prog_list] = prog->id;
2346 cvar->globaldefindex[prog - prvm_prog_list] = i;
2348 else if((cvar->flags & CVAR_PRIVATE) == 0)
2350 // MUST BE SYNCED WITH cvar.c Cvar_Set
2353 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2356 val->_float = cvar->value;
2360 VectorClear(val->vector);
2361 for (j = 0;j < 3;j++)
2363 while (*s && ISWHITESPACE(*s))
2367 val->vector[j] = atof(s);
2368 while (!ISWHITESPACE(*s))
2375 val->string = PRVM_SetEngineString(prog, cvar->string);
2376 cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2379 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2382 cvar->globaldefindex_progid[prog - prvm_prog_list] = prog->id;
2383 cvar->globaldefindex[prog - prvm_prog_list] = i;
2386 Con_Printf("PRVM_LoadProgs: private cvar for autocvar global %s in %s\n", name, prog->name);
2392 prog->loaded = TRUE;
2394 // set flags & ddef_ts in prog
2398 PRVM_FindOffsets(prog);
2400 prog->init_cmd(prog);
2403 PRVM_MEM_Alloc(prog);
2407 static void PRVM_Fields_f (void)
2410 int i, j, ednum, used, usedamount;
2412 char tempstring[MAX_INPUTLINE], tempstring2[260];
2422 Con_Print("no progs loaded\n");
2429 Con_Print("prvm_fields <program name>\n");
2433 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2436 counts = (int *)Mem_Alloc(tempmempool, prog->numfielddefs * sizeof(int));
2437 for (ednum = 0;ednum < prog->max_edicts;ednum++)
2439 ed = PRVM_EDICT_NUM(ednum);
2440 if (ed->priv.required->free)
2442 for (i = 1;i < prog->numfielddefs;i++)
2444 d = &prog->fielddefs[i];
2445 name = PRVM_GetString(prog, d->s_name);
2446 if (name[strlen(name)-2] == '_')
2447 continue; // skip _x, _y, _z vars
2448 val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
2449 // if the value is still all 0, skip the field
2450 for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
2452 if (val->ivector[j])
2463 for (i = 0;i < prog->numfielddefs;i++)
2465 d = &prog->fielddefs[i];
2466 name = PRVM_GetString(prog, d->s_name);
2467 if (name[strlen(name)-2] == '_')
2468 continue; // skip _x, _y, _z vars
2469 switch(d->type & ~DEF_SAVEGLOBAL)
2472 strlcat(tempstring, "string ", sizeof(tempstring));
2475 strlcat(tempstring, "entity ", sizeof(tempstring));
2478 strlcat(tempstring, "function ", sizeof(tempstring));
2481 strlcat(tempstring, "field ", sizeof(tempstring));
2484 strlcat(tempstring, "void ", sizeof(tempstring));
2487 strlcat(tempstring, "float ", sizeof(tempstring));
2490 strlcat(tempstring, "vector ", sizeof(tempstring));
2493 strlcat(tempstring, "pointer ", sizeof(tempstring));
2496 dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
2497 strlcat(tempstring, tempstring2, sizeof(tempstring));
2500 if (strlen(name) > sizeof(tempstring2)-4)
2502 memcpy (tempstring2, name, sizeof(tempstring2)-4);
2503 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
2504 tempstring2[sizeof(tempstring2)-1] = 0;
2507 strlcat(tempstring, name, sizeof(tempstring));
2508 for (j = (int)strlen(name);j < 25;j++)
2509 strlcat(tempstring, " ", sizeof(tempstring));
2510 dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
2511 strlcat(tempstring, tempstring2, sizeof(tempstring));
2512 strlcat(tempstring, "\n", sizeof(tempstring));
2513 if (strlen(tempstring) >= sizeof(tempstring)/2)
2515 Con_Print(tempstring);
2521 usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
2525 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", prog->name, prog->entityfields, used, prog->entityfields * 4, usedamount * 4, prog->max_edicts, prog->entityfields * 4 * prog->max_edicts, usedamount * 4 * prog->max_edicts);
2528 static void PRVM_Globals_f (void)
2532 const char *wildcard;
2538 Con_Print("no progs loaded\n");
2541 if(Cmd_Argc () < 2 || Cmd_Argc() > 3)
2543 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
2547 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2550 if( Cmd_Argc() == 3)
2551 wildcard = Cmd_Argv(2);
2555 Con_Printf("%s :", prog->name);
2557 for (i = 0;i < prog->numglobaldefs;i++)
2560 if( !matchpattern( PRVM_GetString(prog, prog->globaldefs[i].s_name), wildcard, 1) )
2565 Con_Printf("%s\n", PRVM_GetString(prog, prog->globaldefs[i].s_name));
2567 Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->numglobals, numculled, prog->numglobals * 4);
2575 static void PRVM_Global_f(void)
2579 char valuebuf[MAX_INPUTLINE];
2580 if( Cmd_Argc() != 3 ) {
2581 Con_Printf( "prvm_global <program name> <global name>\n" );
2585 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2588 global = PRVM_ED_FindGlobal( prog, Cmd_Argv(2) );
2590 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2592 Con_Printf( "%s: %s\n", Cmd_Argv(2), PRVM_ValueString( prog, (etype_t)global->type, PRVM_GLOBALFIELDVALUE(global->ofs), valuebuf, sizeof(valuebuf) ) );
2600 static void PRVM_GlobalSet_f(void)
2604 if( Cmd_Argc() != 4 ) {
2605 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2609 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2612 global = PRVM_ED_FindGlobal( prog, Cmd_Argv(2) );
2614 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2616 PRVM_ED_ParseEpair( prog, NULL, global, Cmd_Argv(3), true );
2624 void PRVM_Init (void)
2626 Cmd_AddCommand ("prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2627 Cmd_AddCommand ("prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2628 Cmd_AddCommand ("prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2629 Cmd_AddCommand ("prvm_profile", PRVM_Profile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu)");
2630 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");
2631 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)");
2632 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)");
2633 Cmd_AddCommand ("prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2634 Cmd_AddCommand ("prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2635 Cmd_AddCommand ("prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2636 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)");
2637 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");
2638 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");
2639 Cmd_AddCommand ("prvm_printfunction", PRVM_PrintFunction_f, "prints a disassembly (QuakeC instructions) of the specified function in the selected VM (server, client, menu)");
2640 Cmd_AddCommand ("cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2641 Cmd_AddCommand ("menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2642 Cmd_AddCommand ("sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2644 Cvar_RegisterVariable (&prvm_language);
2645 Cvar_RegisterVariable (&prvm_traceqc);
2646 Cvar_RegisterVariable (&prvm_statementprofiling);
2647 Cvar_RegisterVariable (&prvm_timeprofiling);
2648 Cvar_RegisterVariable (&prvm_backtraceforwarnings);
2649 Cvar_RegisterVariable (&prvm_leaktest);
2650 Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames);
2651 Cvar_RegisterVariable (&prvm_errordump);
2652 Cvar_RegisterVariable (&prvm_reuseedicts_startuptime);
2653 Cvar_RegisterVariable (&prvm_reuseedicts_neverinsameframe);
2655 // COMMANDLINEOPTION: PRVM: -norunaway disables the runaway loop check (it might be impossible to exit DarkPlaces if used!)
2656 prvm_runawaycheck = !COM_CheckParm("-norunaway");
2666 void PRVM_Prog_Init(prvm_prog_t *prog)
2669 PRVM_Prog_Reset(prog);
2671 memset(prog, 0, sizeof(prvm_prog_t));
2672 prog->leaktest_active = prvm_leaktest.integer != 0;
2675 // LordHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
2676 unsigned int PRVM_EDICT_NUM_ERROR(prvm_prog_t *prog, unsigned int n, const char *filename, int fileline)
2678 prog->error_cmd("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", prog->name, n, filename, fileline);
2682 #define PRVM_KNOWNSTRINGBASE 0x40000000
2684 const char *PRVM_GetString(prvm_prog_t *prog, int num)
2689 VM_Warning(prog, "PRVM_GetString: Invalid string offset (%i < 0)\n", num);
2692 else if (num < prog->stringssize)
2694 // constant string from progs.dat
2695 return prog->strings + num;
2697 else if (num <= prog->stringssize + prog->tempstringsbuf.maxsize)
2699 // tempstring returned by engine to QC (becomes invalid after returning to engine)
2700 num -= prog->stringssize;
2701 if (num < prog->tempstringsbuf.cursize)
2702 return (char *)prog->tempstringsbuf.data + num;
2705 VM_Warning(prog, "PRVM_GetString: Invalid temp-string offset (%i >= %i prog->tempstringsbuf.cursize)\n", num, prog->tempstringsbuf.cursize);
2709 else if (num & PRVM_KNOWNSTRINGBASE)
2712 num = num - PRVM_KNOWNSTRINGBASE;
2713 if (num >= 0 && num < prog->numknownstrings)
2715 if (!prog->knownstrings[num])
2717 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
2720 return prog->knownstrings[num];
2724 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
2730 // invalid string offset
2731 VM_Warning(prog, "PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
2736 const char *PRVM_ChangeEngineString(prvm_prog_t *prog, int i, const char *s)
2739 i = i - PRVM_KNOWNSTRINGBASE;
2740 if(i < 0 || i >= prog->numknownstrings)
2741 prog->error_cmd("PRVM_ChangeEngineString: s is not an engine string");
2742 old = prog->knownstrings[i];
2743 prog->knownstrings[i] = s;
2747 int PRVM_SetEngineString(prvm_prog_t *prog, const char *s)
2752 if (s >= prog->strings && s <= prog->strings + prog->stringssize)
2753 prog->error_cmd("PRVM_SetEngineString: s in prog->strings area");
2754 // if it's in the tempstrings area, use a reserved range
2755 // (otherwise we'd get millions of useless string offsets cluttering the database)
2756 if (s >= (char *)prog->tempstringsbuf.data && s < (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.maxsize)
2757 return prog->stringssize + (s - (char *)prog->tempstringsbuf.data);
2758 // see if it's a known string address
2759 for (i = 0;i < prog->numknownstrings;i++)
2760 if (prog->knownstrings[i] == s)
2761 return PRVM_KNOWNSTRINGBASE + i;
2762 // new unknown engine string
2763 if (developer_insane.integer)
2764 Con_DPrintf("new engine string %p = \"%s\"\n", s, s);
2765 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2766 if (!prog->knownstrings[i])
2768 if (i >= prog->numknownstrings)
2770 if (i >= prog->maxknownstrings)
2772 const char **oldstrings = prog->knownstrings;
2773 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2774 const char **oldstrings_origin = prog->knownstrings_origin;
2775 prog->maxknownstrings += 128;
2776 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2777 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2778 if(prog->leaktest_active)
2779 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2780 if (prog->numknownstrings)
2782 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2783 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2784 if(prog->leaktest_active)
2785 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
2788 prog->numknownstrings++;
2790 prog->firstfreeknownstring = i + 1;
2791 prog->knownstrings[i] = s;
2792 prog->knownstrings_freeable[i] = false;
2793 if(prog->leaktest_active)
2794 prog->knownstrings_origin[i] = NULL;
2795 return PRVM_KNOWNSTRINGBASE + i;
2798 // temp string handling
2800 // all tempstrings go into this buffer consecutively, and it is reset
2801 // whenever PRVM_ExecuteProgram returns to the engine
2802 // (technically each PRVM_ExecuteProgram call saves the cursize value and
2803 // restores it on return, so multiple recursive calls can share the same
2805 // the buffer size is automatically grown as needed
2807 int PRVM_SetTempString(prvm_prog_t *prog, const char *s)
2813 size = (int)strlen(s) + 1;
2814 if (developer_insane.integer)
2815 Con_DPrintf("PRVM_SetTempString: cursize %i, size %i\n", prog->tempstringsbuf.cursize, size);
2816 if (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
2818 sizebuf_t old = prog->tempstringsbuf;
2819 if (prog->tempstringsbuf.cursize + size >= 1<<28)
2820 prog->error_cmd("PRVM_SetTempString: ran out of tempstring memory! (refusing to grow tempstring buffer over 256MB, cursize %i, size %i)\n", prog->tempstringsbuf.cursize, size);
2821 prog->tempstringsbuf.maxsize = max(prog->tempstringsbuf.maxsize, 65536);
2822 while (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
2823 prog->tempstringsbuf.maxsize *= 2;
2824 if (prog->tempstringsbuf.maxsize != old.maxsize || prog->tempstringsbuf.data == NULL)
2826 Con_DPrintf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, prog->tempstringsbuf.maxsize/1024);
2827 prog->tempstringsbuf.data = (unsigned char *) Mem_Alloc(prog->progs_mempool, prog->tempstringsbuf.maxsize);
2829 memcpy(prog->tempstringsbuf.data, old.data, old.cursize);
2834 t = (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.cursize;
2836 prog->tempstringsbuf.cursize += size;
2837 return PRVM_SetEngineString(prog, t);
2840 int PRVM_AllocString(prvm_prog_t *prog, size_t bufferlength, char **pointer)
2845 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2846 if (!prog->knownstrings[i])
2848 if (i >= prog->numknownstrings)
2850 if (i >= prog->maxknownstrings)
2852 const char **oldstrings = prog->knownstrings;
2853 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2854 const char **oldstrings_origin = prog->knownstrings_origin;
2855 prog->maxknownstrings += 128;
2856 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2857 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2858 if(prog->leaktest_active)
2859 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2860 if (prog->numknownstrings)
2862 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2863 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2864 if(prog->leaktest_active)
2865 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
2868 Mem_Free((char **)oldstrings);
2869 if (oldstrings_freeable)
2870 Mem_Free((unsigned char *)oldstrings_freeable);
2871 if (oldstrings_origin)
2872 Mem_Free((char **)oldstrings_origin);
2874 prog->numknownstrings++;
2876 prog->firstfreeknownstring = i + 1;
2877 prog->knownstrings[i] = (char *)PRVM_Alloc(bufferlength);
2878 prog->knownstrings_freeable[i] = true;
2879 if(prog->leaktest_active)
2880 prog->knownstrings_origin[i] = PRVM_AllocationOrigin(prog);
2882 *pointer = (char *)(prog->knownstrings[i]);
2883 return PRVM_KNOWNSTRINGBASE + i;
2886 void PRVM_FreeString(prvm_prog_t *prog, int num)
2889 prog->error_cmd("PRVM_FreeString: attempt to free a NULL string");
2890 else if (num >= 0 && num < prog->stringssize)
2891 prog->error_cmd("PRVM_FreeString: attempt to free a constant string");
2892 else if (num >= PRVM_KNOWNSTRINGBASE && num < PRVM_KNOWNSTRINGBASE + prog->numknownstrings)
2894 num = num - PRVM_KNOWNSTRINGBASE;
2895 if (!prog->knownstrings[num])
2896 prog->error_cmd("PRVM_FreeString: attempt to free a non-existent or already freed string");
2897 if (!prog->knownstrings_freeable[num])
2898 prog->error_cmd("PRVM_FreeString: attempt to free a string owned by the engine");
2899 PRVM_Free((char *)prog->knownstrings[num]);
2900 if(prog->leaktest_active)
2901 if(prog->knownstrings_origin[num])
2902 PRVM_Free((char *)prog->knownstrings_origin[num]);
2903 prog->knownstrings[num] = NULL;
2904 prog->knownstrings_freeable[num] = false;
2905 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
2908 prog->error_cmd("PRVM_FreeString: invalid string offset %i", num);
2911 static qboolean PRVM_IsStringReferenced(prvm_prog_t *prog, string_t string)
2915 for (i = 0;i < prog->numglobaldefs;i++)
2917 ddef_t *d = &prog->globaldefs[i];
2918 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
2920 if(string == PRVM_GLOBALFIELDSTRING(d->ofs))
2924 for(j = 0; j < prog->num_edicts; ++j)
2926 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2927 if (ed->priv.required->free)
2929 for (i=0; i<prog->numfielddefs; ++i)
2931 ddef_t *d = &prog->fielddefs[i];
2932 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
2934 if(string == PRVM_EDICTFIELDSTRING(ed, d->ofs))
2942 static qboolean PRVM_IsEdictRelevant(prvm_prog_t *prog, prvm_edict_t *edict)
2946 if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
2947 return true; // world or clients
2948 if (prog == SVVM_prog)
2950 if(PRVM_serveredictfloat(edict, solid)) // can block other stuff, or is a trigger?
2952 if(PRVM_serveredictfloat(edict, modelindex)) // visible ent?
2954 if(PRVM_serveredictfloat(edict, effects)) // particle effect?
2956 if(PRVM_serveredictfunction(edict, think)) // has a think function?
2957 if(PRVM_serveredictfloat(edict, nextthink) > 0) // that actually will eventually run?
2959 if(PRVM_serveredictfloat(edict, takedamage))
2961 if(*prvm_leaktest_ignore_classnames.string)
2963 if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_serveredictstring(edict, classname)))))
2967 else if (prog == CLVM_prog)
2969 // TODO someone add more stuff here
2970 if(PRVM_clientedictfloat(edict, entnum)) // csqc networked
2972 if(PRVM_clientedictfloat(edict, modelindex)) // visible ent?
2974 if(PRVM_clientedictfloat(edict, effects)) // particle effect?
2976 if(PRVM_clientedictfunction(edict, think)) // has a think function?
2977 if(PRVM_clientedictfloat(edict, nextthink) > 0) // that actually will eventually run?
2979 if(*prvm_leaktest_ignore_classnames.string)
2981 if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_clientedictstring(edict, classname)))))
2987 // menu prog does not have classnames
2992 static qboolean PRVM_IsEdictReferenced(prvm_prog_t *prog, prvm_edict_t *edict, int mark)
2995 int edictnum = PRVM_NUM_FOR_EDICT(edict);
2996 const char *targetname = NULL;
2998 if (prog == SVVM_prog)
2999 targetname = PRVM_GetString(prog, PRVM_serveredictstring(edict, targetname));
3002 if(!*targetname) // ""
3007 for (i = 0;i < prog->numglobaldefs;i++)
3009 ddef_t *d = &prog->globaldefs[i];
3010 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3012 if(edictnum == PRVM_GLOBALFIELDEDICT(d->ofs))
3017 for(j = 0; j < prog->num_edicts; ++j)
3019 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3020 if (ed->priv.required->mark < mark)
3026 const char *target = PRVM_GetString(prog, PRVM_serveredictstring(ed, target));
3028 if(!strcmp(target, targetname))
3031 for (i=0; i<prog->numfielddefs; ++i)
3033 ddef_t *d = &prog->fielddefs[i];
3034 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3036 if(edictnum == PRVM_EDICTFIELDEDICT(ed, d->ofs))
3044 static void PRVM_MarkReferencedEdicts(prvm_prog_t *prog)
3050 for(j = 0; j < prog->num_edicts; ++j)
3052 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3053 if(ed->priv.required->free)
3055 ed->priv.required->mark = PRVM_IsEdictRelevant(prog, ed) ? 1 : 0;
3062 for(j = 0; j < prog->num_edicts; ++j)
3064 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3065 if(ed->priv.required->free)
3067 if(ed->priv.required->mark)
3069 if(PRVM_IsEdictReferenced(prog, ed, stage))
3071 ed->priv.required->mark = stage + 1;
3078 Con_DPrintf("leak check used %d stages to find all references\n", stage);
3081 void PRVM_LeakTest(prvm_prog_t *prog)
3084 qboolean leaked = false;
3086 if(!prog->leaktest_active)
3090 for (i = 0; i < prog->numknownstrings; ++i)
3092 if(prog->knownstrings[i])
3093 if(prog->knownstrings_freeable[i])
3094 if(prog->knownstrings_origin[i])
3095 if(!PRVM_IsStringReferenced(prog, PRVM_KNOWNSTRINGBASE + i))
3097 Con_Printf("Unreferenced string found!\n Value: %s\n Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
3103 PRVM_MarkReferencedEdicts(prog);
3104 for(j = 0; j < prog->num_edicts; ++j)
3106 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3107 if(ed->priv.required->free)
3109 if(!ed->priv.required->mark)
3110 if(ed->priv.required->allocation_origin)
3112 Con_Printf("Unreferenced edict found!\n Allocated at: %s\n", ed->priv.required->allocation_origin);
3113 PRVM_ED_Print(prog, ed, NULL);
3118 ed->priv.required->mark = 0; // clear marks again when done
3121 for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
3123 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
3125 if(stringbuffer->origin)
3127 Con_Printf("Open string buffer handle found!\n Allocated at: %s\n", stringbuffer->origin);
3132 for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
3134 if(prog->openfiles[i])
3135 if(prog->openfiles_origin[i])
3137 Con_Printf("Open file handle found!\n Allocated at: %s\n", prog->openfiles_origin[i]);
3142 for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
3144 if(prog->opensearches[i])
3145 if(prog->opensearches_origin[i])
3147 Con_Printf("Open search handle found!\n Allocated at: %s\n", prog->opensearches_origin[i]);
3153 Con_Printf("Congratulations. No leaks found.\n");