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", 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, unsigned char * data, fs_offset_t size, 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
1911 dprograms = (dprograms_t *) data;
1915 dprograms = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
1916 if (dprograms == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
1917 prog->error_cmd("PRVM_LoadProgs: couldn't load %s for %s", filename, prog->name);
1918 // TODO bounds check header fields (e.g. numstatements), they must never go behind end of file
1920 prog->profiletime = Sys_DirtyTime();
1921 prog->starttime = realtime;
1923 Con_DPrintf("%s programs occupy %iK.\n", prog->name, (int)(filesize/1024));
1925 requiredglobalspace = 0;
1926 for (i = 0;i < numrequiredglobals;i++)
1927 requiredglobalspace += required_global[i].type == ev_vector ? 3 : 1;
1929 prog->filecrc = CRC_Block((unsigned char *)dprograms, filesize);
1931 // byte swap the header
1932 prog->progs_version = LittleLong(dprograms->version);
1933 prog->progs_crc = LittleLong(dprograms->crc);
1934 if (prog->progs_version != PROG_VERSION)
1935 prog->error_cmd("%s: %s has wrong version number (%i should be %i)", prog->name, filename, prog->progs_version, PROG_VERSION);
1936 instatements = (dstatement_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_statements));
1937 prog->progs_numstatements = LittleLong(dprograms->numstatements);
1938 inglobaldefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globaldefs));
1939 prog->progs_numglobaldefs = LittleLong(dprograms->numglobaldefs);
1940 infielddefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_fielddefs));
1941 prog->progs_numfielddefs = LittleLong(dprograms->numfielddefs);
1942 infunctions = (dfunction_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_functions));
1943 prog->progs_numfunctions = LittleLong(dprograms->numfunctions);
1944 instrings = (char *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_strings));
1945 prog->progs_numstrings = LittleLong(dprograms->numstrings);
1946 inglobals = (int *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globals));
1947 prog->progs_numglobals = LittleLong(dprograms->numglobals);
1948 prog->progs_entityfields = LittleLong(dprograms->entityfields);
1950 prog->numstatements = prog->progs_numstatements;
1951 prog->numglobaldefs = prog->progs_numglobaldefs;
1952 prog->numfielddefs = prog->progs_numfielddefs;
1953 prog->numfunctions = prog->progs_numfunctions;
1954 prog->numstrings = prog->progs_numstrings;
1955 prog->numglobals = prog->progs_numglobals;
1956 prog->entityfields = prog->progs_entityfields;
1958 if (LittleLong(dprograms->ofs_strings) + prog->progs_numstrings > (int)filesize)
1959 prog->error_cmd("%s: %s strings go past end of file", prog->name, filename);
1960 prog->strings = (char *)Mem_Alloc(prog->progs_mempool, prog->progs_numstrings);
1961 memcpy(prog->strings, instrings, prog->progs_numstrings);
1962 prog->stringssize = prog->progs_numstrings;
1964 prog->numknownstrings = 0;
1965 prog->maxknownstrings = 0;
1966 prog->knownstrings = NULL;
1967 prog->knownstrings_freeable = NULL;
1969 Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
1971 // we need to expand the globaldefs and fielddefs to include engine defs
1972 prog->globaldefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobaldefs + numrequiredglobals) * sizeof(ddef_t));
1973 prog->globals.fp = (prvm_vec_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobals + requiredglobalspace + 2) * sizeof(prvm_vec_t));
1974 // + 2 is because of an otherwise occurring overrun in RETURN instruction
1975 // when trying to return the last or second-last global
1976 // (RETURN always returns a vector, there is no RETURN_F instruction)
1977 prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numfielddefs + numrequiredfields) * sizeof(ddef_t));
1978 // we need to convert the statements to our memory format
1979 prog->statements = (mstatement_t *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(mstatement_t));
1980 // allocate space for profiling statement usage
1981 prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(*prog->statement_profile));
1982 // functions need to be converted to the memory format
1983 prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs_numfunctions);
1985 for (i = 0;i < prog->progs_numfunctions;i++)
1987 prog->functions[i].first_statement = LittleLong(infunctions[i].first_statement);
1988 prog->functions[i].parm_start = LittleLong(infunctions[i].parm_start);
1989 prog->functions[i].s_name = LittleLong(infunctions[i].s_name);
1990 prog->functions[i].s_file = LittleLong(infunctions[i].s_file);
1991 prog->functions[i].numparms = LittleLong(infunctions[i].numparms);
1992 prog->functions[i].locals = LittleLong(infunctions[i].locals);
1993 memcpy(prog->functions[i].parm_size, infunctions[i].parm_size, sizeof(infunctions[i].parm_size));
1994 if(prog->functions[i].first_statement >= prog->numstatements)
1995 prog->error_cmd("PRVM_LoadProgs: out of bounds function statement (function %d) in %s", i, prog->name);
1996 // TODO bounds check parm_start, s_name, s_file, numparms, locals, parm_size
1999 // copy the globaldefs to the new globaldefs list
2000 for (i=0 ; i<prog->numglobaldefs ; i++)
2002 prog->globaldefs[i].type = LittleShort(inglobaldefs[i].type);
2003 prog->globaldefs[i].ofs = LittleShort(inglobaldefs[i].ofs);
2004 prog->globaldefs[i].s_name = LittleLong(inglobaldefs[i].s_name);
2005 // TODO bounds check ofs, s_name
2008 // append the required globals
2009 for (i = 0;i < numrequiredglobals;i++)
2011 prog->globaldefs[prog->numglobaldefs].type = required_global[i].type;
2012 prog->globaldefs[prog->numglobaldefs].ofs = prog->numglobals;
2013 prog->globaldefs[prog->numglobaldefs].s_name = PRVM_SetEngineString(prog, required_global[i].name);
2014 if (prog->globaldefs[prog->numglobaldefs].type == ev_vector)
2015 prog->numglobals += 3;
2018 prog->numglobaldefs++;
2021 // copy the progs fields to the new fields list
2022 for (i = 0;i < prog->numfielddefs;i++)
2024 prog->fielddefs[i].type = LittleShort(infielddefs[i].type);
2025 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
2026 prog->error_cmd("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", prog->name);
2027 prog->fielddefs[i].ofs = LittleShort(infielddefs[i].ofs);
2028 prog->fielddefs[i].s_name = LittleLong(infielddefs[i].s_name);
2029 // TODO bounds check ofs, s_name
2032 // append the required fields
2033 for (i = 0;i < numrequiredfields;i++)
2035 prog->fielddefs[prog->numfielddefs].type = required_field[i].type;
2036 prog->fielddefs[prog->numfielddefs].ofs = prog->entityfields;
2037 prog->fielddefs[prog->numfielddefs].s_name = PRVM_SetEngineString(prog, required_field[i].name);
2038 if (prog->fielddefs[prog->numfielddefs].type == ev_vector)
2039 prog->entityfields += 3;
2041 prog->entityfields++;
2042 prog->numfielddefs++;
2045 // LordHavoc: TODO: reorder globals to match engine struct
2046 // LordHavoc: TODO: reorder fields to match engine struct
2047 #define remapglobal(index) (index)
2048 #define remapfield(index) (index)
2051 // FIXME: LordHavoc: this uses a crude way to identify integer constants, rather than checking for matching globaldefs and checking their type
2052 for (i = 0;i < prog->progs_numglobals;i++)
2054 u.i = LittleLong(inglobals[i]);
2055 // most globals are 0, we only need to deal with the ones that are not
2058 d = u.i & 0xFF800000;
2059 if ((d == 0xFF800000) || (d == 0))
2061 // Looks like an integer (expand to int64)
2062 prog->globals.ip[remapglobal(i)] = u.i;
2066 // Looks like a float (expand to double)
2067 prog->globals.fp[remapglobal(i)] = u.f;
2072 // LordHavoc: TODO: support 32bit progs statement formats
2073 // copy, remap globals in statements, bounds check
2074 for (i = 0;i < prog->progs_numstatements;i++)
2076 op = (opcode_t)LittleShort(instatements[i].op);
2077 a = (unsigned short)LittleShort(instatements[i].a);
2078 b = (unsigned short)LittleShort(instatements[i].b);
2079 c = (unsigned short)LittleShort(instatements[i].c);
2085 if (a >= prog->progs_numglobals || b + i < 0 || b + i >= prog->progs_numstatements)
2086 prog->error_cmd("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", i, prog->name);
2087 prog->statements[i].op = op;
2088 prog->statements[i].operand[0] = remapglobal(a);
2089 prog->statements[i].operand[1] = -1;
2090 prog->statements[i].operand[2] = -1;
2091 prog->statements[i].jumpabsolute = i + b;
2095 if (a + i < 0 || a + i >= prog->progs_numstatements)
2096 prog->error_cmd("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, prog->name);
2097 prog->statements[i].op = op;
2098 prog->statements[i].operand[0] = -1;
2099 prog->statements[i].operand[1] = -1;
2100 prog->statements[i].operand[2] = -1;
2101 prog->statements[i].jumpabsolute = i + a;
2104 Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", (int)op, i, prog->name);
2105 // global global global
2140 if (a >= prog->progs_numglobals || b >= prog->progs_numglobals || c >= prog->progs_numglobals)
2141 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
2142 prog->statements[i].op = op;
2143 prog->statements[i].operand[0] = remapglobal(a);
2144 prog->statements[i].operand[1] = remapglobal(b);
2145 prog->statements[i].operand[2] = remapglobal(c);
2146 prog->statements[i].jumpabsolute = -1;
2148 // global none global
2154 if (a >= prog->progs_numglobals || c >= prog->progs_numglobals)
2155 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2156 prog->statements[i].op = op;
2157 prog->statements[i].operand[0] = remapglobal(a);
2158 prog->statements[i].operand[1] = -1;
2159 prog->statements[i].operand[2] = remapglobal(c);
2160 prog->statements[i].jumpabsolute = -1;
2176 if (a >= prog->progs_numglobals || b >= prog->progs_numglobals)
2177 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2178 prog->statements[i].op = op;
2179 prog->statements[i].operand[0] = remapglobal(a);
2180 prog->statements[i].operand[1] = remapglobal(b);
2181 prog->statements[i].operand[2] = -1;
2182 prog->statements[i].jumpabsolute = -1;
2196 if ( a >= prog->progs_numglobals)
2197 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2198 prog->statements[i].op = op;
2199 prog->statements[i].operand[0] = remapglobal(a);
2200 prog->statements[i].operand[1] = -1;
2201 prog->statements[i].operand[2] = -1;
2202 prog->statements[i].jumpabsolute = -1;
2206 if(prog->numstatements < 1)
2208 prog->error_cmd("PRVM_LoadProgs: empty program in %s", prog->name);
2210 else switch(prog->statements[prog->numstatements - 1].op)
2217 prog->error_cmd("PRVM_LoadProgs: program may fall off the edge (does not end with RETURN, GOTO or DONE) in %s", prog->name);
2221 // we're done with the file now
2223 Mem_Free(dprograms);
2226 // check required functions
2227 for(i=0 ; i < numrequiredfunc ; i++)
2228 if(PRVM_ED_FindFunction(prog, required_func[i]) == 0)
2229 prog->error_cmd("%s: %s not found in %s",prog->name, required_func[i], filename);
2231 PRVM_LoadLNO(prog, filename);
2233 PRVM_Init_Exec(prog);
2235 if(*prvm_language.string)
2236 // in CSQC we really shouldn't be able to change how stuff works... sorry for now
2237 // later idea: include a list of authorized .po file checksums with the csprogs
2239 qboolean deftrans = prog == CLVM_prog;
2240 const char *realfilename = (prog != CLVM_prog ? filename : csqc_progname.string);
2241 if(deftrans) // once we have dotranslate_ strings, ALWAYS use the opt-in method!
2243 for (i=0 ; i<prog->numglobaldefs ; i++)
2246 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2247 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2248 if(name && !strncmp(name, "dotranslate_", 12))
2255 if(!strcmp(prvm_language.string, "dump"))
2257 qfile_t *f = FS_OpenRealFile(va(vabuf, sizeof(vabuf), "%s.pot", realfilename), "w", false);
2258 Con_Printf("Dumping to %s.pot\n", realfilename);
2261 for (i=0 ; i<prog->numglobaldefs ; i++)
2264 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2265 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2266 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2268 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2269 const char *value = PRVM_GetString(prog, val->string);
2272 char buf[MAX_INPUTLINE];
2273 PRVM_PO_UnparseString(buf, value, sizeof(buf));
2274 FS_Printf(f, "msgid \"%s\"\nmsgstr \"\"\n\n", buf);
2283 po_t *po = PRVM_PO_Load(va(vabuf, sizeof(vabuf), "%s.%s.po", realfilename, prvm_language.string), prog->progs_mempool);
2286 for (i=0 ; i<prog->numglobaldefs ; i++)
2289 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2290 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2291 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2293 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2294 const char *value = PRVM_GetString(prog, val->string);
2297 value = PRVM_PO_Lookup(po, value);
2299 val->string = PRVM_SetEngineString(prog, value);
2307 for (i=0 ; i<prog->numglobaldefs ; i++)
2310 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2311 //Con_Printf("found var %s\n", name);
2313 && !strncmp(name, "autocvar_", 9)
2314 && !(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
2317 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2318 cvar_t *cvar = Cvar_FindVar(name + 9);
2319 //Con_Printf("PRVM_LoadProgs: autocvar global %s in %s, processing...\n", name, prog->name);
2324 Con_DPrintf("PRVM_LoadProgs: no cvar for autocvar global %s in %s, creating...\n", name, prog->name);
2325 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2328 if((float)((int)(val->_float)) == val->_float)
2329 dpsnprintf(buf, sizeof(buf), "%i", (int)(val->_float));
2331 dpsnprintf(buf, sizeof(buf), "%.9g", val->_float);
2335 dpsnprintf(buf, sizeof(buf), "%.9g %.9g %.9g", val->vector[0], val->vector[1], val->vector[2]); value = buf;
2338 value = PRVM_GetString(prog, val->string);
2341 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2344 cvar = Cvar_Get(name + 9, value, 0, NULL);
2345 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2347 val->string = PRVM_SetEngineString(prog, cvar->string);
2348 cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2351 prog->error_cmd("PRVM_LoadProgs: could not create cvar for autocvar global %s in %s", name, prog->name);
2352 cvar->globaldefindex_progid[prog - prvm_prog_list] = prog->id;
2353 cvar->globaldefindex[prog - prvm_prog_list] = i;
2355 else if((cvar->flags & CVAR_PRIVATE) == 0)
2357 // MUST BE SYNCED WITH cvar.c Cvar_Set
2360 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2363 val->_float = cvar->value;
2367 VectorClear(val->vector);
2368 for (j = 0;j < 3;j++)
2370 while (*s && ISWHITESPACE(*s))
2374 val->vector[j] = atof(s);
2375 while (!ISWHITESPACE(*s))
2382 val->string = PRVM_SetEngineString(prog, cvar->string);
2383 cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2386 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2389 cvar->globaldefindex_progid[prog - prvm_prog_list] = prog->id;
2390 cvar->globaldefindex[prog - prvm_prog_list] = i;
2393 Con_Printf("PRVM_LoadProgs: private cvar for autocvar global %s in %s\n", name, prog->name);
2399 prog->loaded = TRUE;
2401 // set flags & ddef_ts in prog
2405 PRVM_FindOffsets(prog);
2407 prog->init_cmd(prog);
2410 PRVM_MEM_Alloc(prog);
2414 static void PRVM_Fields_f (void)
2417 int i, j, ednum, used, usedamount;
2419 char tempstring[MAX_INPUTLINE], tempstring2[260];
2429 Con_Print("no progs loaded\n");
2436 Con_Print("prvm_fields <program name>\n");
2440 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2443 counts = (int *)Mem_Alloc(tempmempool, prog->numfielddefs * sizeof(int));
2444 for (ednum = 0;ednum < prog->max_edicts;ednum++)
2446 ed = PRVM_EDICT_NUM(ednum);
2447 if (ed->priv.required->free)
2449 for (i = 1;i < prog->numfielddefs;i++)
2451 d = &prog->fielddefs[i];
2452 name = PRVM_GetString(prog, d->s_name);
2453 if (name[strlen(name)-2] == '_')
2454 continue; // skip _x, _y, _z vars
2455 val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
2456 // if the value is still all 0, skip the field
2457 for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
2459 if (val->ivector[j])
2470 for (i = 0;i < prog->numfielddefs;i++)
2472 d = &prog->fielddefs[i];
2473 name = PRVM_GetString(prog, d->s_name);
2474 if (name[strlen(name)-2] == '_')
2475 continue; // skip _x, _y, _z vars
2476 switch(d->type & ~DEF_SAVEGLOBAL)
2479 strlcat(tempstring, "string ", sizeof(tempstring));
2482 strlcat(tempstring, "entity ", sizeof(tempstring));
2485 strlcat(tempstring, "function ", sizeof(tempstring));
2488 strlcat(tempstring, "field ", sizeof(tempstring));
2491 strlcat(tempstring, "void ", sizeof(tempstring));
2494 strlcat(tempstring, "float ", sizeof(tempstring));
2497 strlcat(tempstring, "vector ", sizeof(tempstring));
2500 strlcat(tempstring, "pointer ", sizeof(tempstring));
2503 dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
2504 strlcat(tempstring, tempstring2, sizeof(tempstring));
2507 if (strlen(name) > sizeof(tempstring2)-4)
2509 memcpy (tempstring2, name, sizeof(tempstring2)-4);
2510 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
2511 tempstring2[sizeof(tempstring2)-1] = 0;
2514 strlcat(tempstring, name, sizeof(tempstring));
2515 for (j = (int)strlen(name);j < 25;j++)
2516 strlcat(tempstring, " ", sizeof(tempstring));
2517 dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
2518 strlcat(tempstring, tempstring2, sizeof(tempstring));
2519 strlcat(tempstring, "\n", sizeof(tempstring));
2520 if (strlen(tempstring) >= sizeof(tempstring)/2)
2522 Con_Print(tempstring);
2528 usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
2532 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);
2535 static void PRVM_Globals_f (void)
2539 const char *wildcard;
2545 Con_Print("no progs loaded\n");
2548 if(Cmd_Argc () < 2 || Cmd_Argc() > 3)
2550 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
2554 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2557 if( Cmd_Argc() == 3)
2558 wildcard = Cmd_Argv(2);
2562 Con_Printf("%s :", prog->name);
2564 for (i = 0;i < prog->numglobaldefs;i++)
2567 if( !matchpattern( PRVM_GetString(prog, prog->globaldefs[i].s_name), wildcard, 1) )
2572 Con_Printf("%s\n", PRVM_GetString(prog, prog->globaldefs[i].s_name));
2574 Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->numglobals, numculled, prog->numglobals * 4);
2582 static void PRVM_Global_f(void)
2586 char valuebuf[MAX_INPUTLINE];
2587 if( Cmd_Argc() != 3 ) {
2588 Con_Printf( "prvm_global <program name> <global name>\n" );
2592 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2595 global = PRVM_ED_FindGlobal( prog, Cmd_Argv(2) );
2597 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2599 Con_Printf( "%s: %s\n", Cmd_Argv(2), PRVM_ValueString( prog, (etype_t)global->type, PRVM_GLOBALFIELDVALUE(global->ofs), valuebuf, sizeof(valuebuf) ) );
2607 static void PRVM_GlobalSet_f(void)
2611 if( Cmd_Argc() != 4 ) {
2612 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2616 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2619 global = PRVM_ED_FindGlobal( prog, Cmd_Argv(2) );
2621 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2623 PRVM_ED_ParseEpair( prog, NULL, global, Cmd_Argv(3), true );
2631 void PRVM_Init (void)
2633 Cmd_AddCommand ("prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2634 Cmd_AddCommand ("prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2635 Cmd_AddCommand ("prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2636 Cmd_AddCommand ("prvm_profile", PRVM_Profile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu)");
2637 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");
2638 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)");
2639 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)");
2640 Cmd_AddCommand ("prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2641 Cmd_AddCommand ("prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2642 Cmd_AddCommand ("prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2643 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)");
2644 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");
2645 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");
2646 Cmd_AddCommand ("prvm_printfunction", PRVM_PrintFunction_f, "prints a disassembly (QuakeC instructions) of the specified function in the selected VM (server, client, menu)");
2647 Cmd_AddCommand ("cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2648 Cmd_AddCommand ("menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2649 Cmd_AddCommand ("sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2651 Cvar_RegisterVariable (&prvm_language);
2652 Cvar_RegisterVariable (&prvm_traceqc);
2653 Cvar_RegisterVariable (&prvm_statementprofiling);
2654 Cvar_RegisterVariable (&prvm_timeprofiling);
2655 Cvar_RegisterVariable (&prvm_backtraceforwarnings);
2656 Cvar_RegisterVariable (&prvm_leaktest);
2657 Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames);
2658 Cvar_RegisterVariable (&prvm_errordump);
2659 Cvar_RegisterVariable (&prvm_reuseedicts_startuptime);
2660 Cvar_RegisterVariable (&prvm_reuseedicts_neverinsameframe);
2662 // COMMANDLINEOPTION: PRVM: -norunaway disables the runaway loop check (it might be impossible to exit DarkPlaces if used!)
2663 prvm_runawaycheck = !COM_CheckParm("-norunaway");
2673 void PRVM_Prog_Init(prvm_prog_t *prog)
2676 PRVM_Prog_Reset(prog);
2678 memset(prog, 0, sizeof(prvm_prog_t));
2679 prog->leaktest_active = prvm_leaktest.integer != 0;
2682 // LordHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
2683 unsigned int PRVM_EDICT_NUM_ERROR(prvm_prog_t *prog, unsigned int n, const char *filename, int fileline)
2685 prog->error_cmd("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", prog->name, n, filename, fileline);
2689 #define PRVM_KNOWNSTRINGBASE 0x40000000
2691 const char *PRVM_GetString(prvm_prog_t *prog, int num)
2696 VM_Warning(prog, "PRVM_GetString: Invalid string offset (%i < 0)\n", num);
2699 else if (num < prog->stringssize)
2701 // constant string from progs.dat
2702 return prog->strings + num;
2704 else if (num <= prog->stringssize + prog->tempstringsbuf.maxsize)
2706 // tempstring returned by engine to QC (becomes invalid after returning to engine)
2707 num -= prog->stringssize;
2708 if (num < prog->tempstringsbuf.cursize)
2709 return (char *)prog->tempstringsbuf.data + num;
2712 VM_Warning(prog, "PRVM_GetString: Invalid temp-string offset (%i >= %i prog->tempstringsbuf.cursize)\n", num, prog->tempstringsbuf.cursize);
2716 else if (num & PRVM_KNOWNSTRINGBASE)
2719 num = num - PRVM_KNOWNSTRINGBASE;
2720 if (num >= 0 && num < prog->numknownstrings)
2722 if (!prog->knownstrings[num])
2724 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
2727 return prog->knownstrings[num];
2731 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
2737 // invalid string offset
2738 VM_Warning(prog, "PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
2743 const char *PRVM_ChangeEngineString(prvm_prog_t *prog, int i, const char *s)
2746 i = i - PRVM_KNOWNSTRINGBASE;
2747 if(i < 0 || i >= prog->numknownstrings)
2748 prog->error_cmd("PRVM_ChangeEngineString: s is not an engine string");
2749 old = prog->knownstrings[i];
2750 prog->knownstrings[i] = s;
2754 int PRVM_SetEngineString(prvm_prog_t *prog, const char *s)
2759 if (s >= prog->strings && s <= prog->strings + prog->stringssize)
2760 prog->error_cmd("PRVM_SetEngineString: s in prog->strings area");
2761 // if it's in the tempstrings area, use a reserved range
2762 // (otherwise we'd get millions of useless string offsets cluttering the database)
2763 if (s >= (char *)prog->tempstringsbuf.data && s < (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.maxsize)
2764 return prog->stringssize + (s - (char *)prog->tempstringsbuf.data);
2765 // see if it's a known string address
2766 for (i = 0;i < prog->numknownstrings;i++)
2767 if (prog->knownstrings[i] == s)
2768 return PRVM_KNOWNSTRINGBASE + i;
2769 // new unknown engine string
2770 if (developer_insane.integer)
2771 Con_DPrintf("new engine string %p = \"%s\"\n", s, s);
2772 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2773 if (!prog->knownstrings[i])
2775 if (i >= prog->numknownstrings)
2777 if (i >= prog->maxknownstrings)
2779 const char **oldstrings = prog->knownstrings;
2780 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2781 const char **oldstrings_origin = prog->knownstrings_origin;
2782 prog->maxknownstrings += 128;
2783 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2784 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2785 if(prog->leaktest_active)
2786 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2787 if (prog->numknownstrings)
2789 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2790 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2791 if(prog->leaktest_active)
2792 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
2795 prog->numknownstrings++;
2797 prog->firstfreeknownstring = i + 1;
2798 prog->knownstrings[i] = s;
2799 prog->knownstrings_freeable[i] = false;
2800 if(prog->leaktest_active)
2801 prog->knownstrings_origin[i] = NULL;
2802 return PRVM_KNOWNSTRINGBASE + i;
2805 // temp string handling
2807 // all tempstrings go into this buffer consecutively, and it is reset
2808 // whenever PRVM_ExecuteProgram returns to the engine
2809 // (technically each PRVM_ExecuteProgram call saves the cursize value and
2810 // restores it on return, so multiple recursive calls can share the same
2812 // the buffer size is automatically grown as needed
2814 int PRVM_SetTempString(prvm_prog_t *prog, const char *s)
2820 size = (int)strlen(s) + 1;
2821 if (developer_insane.integer)
2822 Con_DPrintf("PRVM_SetTempString: cursize %i, size %i\n", prog->tempstringsbuf.cursize, size);
2823 if (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
2825 sizebuf_t old = prog->tempstringsbuf;
2826 if (prog->tempstringsbuf.cursize + size >= 1<<28)
2827 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);
2828 prog->tempstringsbuf.maxsize = max(prog->tempstringsbuf.maxsize, 65536);
2829 while (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
2830 prog->tempstringsbuf.maxsize *= 2;
2831 if (prog->tempstringsbuf.maxsize != old.maxsize || prog->tempstringsbuf.data == NULL)
2833 Con_DPrintf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, prog->tempstringsbuf.maxsize/1024);
2834 prog->tempstringsbuf.data = (unsigned char *) Mem_Alloc(prog->progs_mempool, prog->tempstringsbuf.maxsize);
2836 memcpy(prog->tempstringsbuf.data, old.data, old.cursize);
2841 t = (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.cursize;
2843 prog->tempstringsbuf.cursize += size;
2844 return PRVM_SetEngineString(prog, t);
2847 int PRVM_AllocString(prvm_prog_t *prog, size_t bufferlength, char **pointer)
2852 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2853 if (!prog->knownstrings[i])
2855 if (i >= prog->numknownstrings)
2857 if (i >= prog->maxknownstrings)
2859 const char **oldstrings = prog->knownstrings;
2860 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2861 const char **oldstrings_origin = prog->knownstrings_origin;
2862 prog->maxknownstrings += 128;
2863 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2864 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2865 if(prog->leaktest_active)
2866 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2867 if (prog->numknownstrings)
2869 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2870 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2871 if(prog->leaktest_active)
2872 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
2875 Mem_Free((char **)oldstrings);
2876 if (oldstrings_freeable)
2877 Mem_Free((unsigned char *)oldstrings_freeable);
2878 if (oldstrings_origin)
2879 Mem_Free((char **)oldstrings_origin);
2881 prog->numknownstrings++;
2883 prog->firstfreeknownstring = i + 1;
2884 prog->knownstrings[i] = (char *)PRVM_Alloc(bufferlength);
2885 prog->knownstrings_freeable[i] = true;
2886 if(prog->leaktest_active)
2887 prog->knownstrings_origin[i] = PRVM_AllocationOrigin(prog);
2889 *pointer = (char *)(prog->knownstrings[i]);
2890 return PRVM_KNOWNSTRINGBASE + i;
2893 void PRVM_FreeString(prvm_prog_t *prog, int num)
2896 prog->error_cmd("PRVM_FreeString: attempt to free a NULL string");
2897 else if (num >= 0 && num < prog->stringssize)
2898 prog->error_cmd("PRVM_FreeString: attempt to free a constant string");
2899 else if (num >= PRVM_KNOWNSTRINGBASE && num < PRVM_KNOWNSTRINGBASE + prog->numknownstrings)
2901 num = num - PRVM_KNOWNSTRINGBASE;
2902 if (!prog->knownstrings[num])
2903 prog->error_cmd("PRVM_FreeString: attempt to free a non-existent or already freed string");
2904 if (!prog->knownstrings_freeable[num])
2905 prog->error_cmd("PRVM_FreeString: attempt to free a string owned by the engine");
2906 PRVM_Free((char *)prog->knownstrings[num]);
2907 if(prog->leaktest_active)
2908 if(prog->knownstrings_origin[num])
2909 PRVM_Free((char *)prog->knownstrings_origin[num]);
2910 prog->knownstrings[num] = NULL;
2911 prog->knownstrings_freeable[num] = false;
2912 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
2915 prog->error_cmd("PRVM_FreeString: invalid string offset %i", num);
2918 static qboolean PRVM_IsStringReferenced(prvm_prog_t *prog, string_t string)
2922 for (i = 0;i < prog->numglobaldefs;i++)
2924 ddef_t *d = &prog->globaldefs[i];
2925 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
2927 if(string == PRVM_GLOBALFIELDSTRING(d->ofs))
2931 for(j = 0; j < prog->num_edicts; ++j)
2933 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2934 if (ed->priv.required->free)
2936 for (i=0; i<prog->numfielddefs; ++i)
2938 ddef_t *d = &prog->fielddefs[i];
2939 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
2941 if(string == PRVM_EDICTFIELDSTRING(ed, d->ofs))
2949 static qboolean PRVM_IsEdictRelevant(prvm_prog_t *prog, prvm_edict_t *edict)
2953 if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
2954 return true; // world or clients
2955 if (prog == SVVM_prog)
2957 if(PRVM_serveredictfloat(edict, solid)) // can block other stuff, or is a trigger?
2959 if(PRVM_serveredictfloat(edict, modelindex)) // visible ent?
2961 if(PRVM_serveredictfloat(edict, effects)) // particle effect?
2963 if(PRVM_serveredictfunction(edict, think)) // has a think function?
2964 if(PRVM_serveredictfloat(edict, nextthink) > 0) // that actually will eventually run?
2966 if(PRVM_serveredictfloat(edict, takedamage))
2968 if(*prvm_leaktest_ignore_classnames.string)
2970 if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_serveredictstring(edict, classname)))))
2974 else if (prog == CLVM_prog)
2976 // TODO someone add more stuff here
2977 if(PRVM_clientedictfloat(edict, entnum)) // csqc networked
2979 if(PRVM_clientedictfloat(edict, modelindex)) // visible ent?
2981 if(PRVM_clientedictfloat(edict, effects)) // particle effect?
2983 if(PRVM_clientedictfunction(edict, think)) // has a think function?
2984 if(PRVM_clientedictfloat(edict, nextthink) > 0) // that actually will eventually run?
2986 if(*prvm_leaktest_ignore_classnames.string)
2988 if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_clientedictstring(edict, classname)))))
2994 // menu prog does not have classnames
2999 static qboolean PRVM_IsEdictReferenced(prvm_prog_t *prog, prvm_edict_t *edict, int mark)
3002 int edictnum = PRVM_NUM_FOR_EDICT(edict);
3003 const char *targetname = NULL;
3005 if (prog == SVVM_prog)
3006 targetname = PRVM_GetString(prog, PRVM_serveredictstring(edict, targetname));
3009 if(!*targetname) // ""
3014 for (i = 0;i < prog->numglobaldefs;i++)
3016 ddef_t *d = &prog->globaldefs[i];
3017 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3019 if(edictnum == PRVM_GLOBALFIELDEDICT(d->ofs))
3024 for(j = 0; j < prog->num_edicts; ++j)
3026 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3027 if (ed->priv.required->mark < mark)
3033 const char *target = PRVM_GetString(prog, PRVM_serveredictstring(ed, target));
3035 if(!strcmp(target, targetname))
3038 for (i=0; i<prog->numfielddefs; ++i)
3040 ddef_t *d = &prog->fielddefs[i];
3041 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3043 if(edictnum == PRVM_EDICTFIELDEDICT(ed, d->ofs))
3051 static void PRVM_MarkReferencedEdicts(prvm_prog_t *prog)
3057 for(j = 0; j < prog->num_edicts; ++j)
3059 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3060 if(ed->priv.required->free)
3062 ed->priv.required->mark = PRVM_IsEdictRelevant(prog, ed) ? 1 : 0;
3069 for(j = 0; j < prog->num_edicts; ++j)
3071 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3072 if(ed->priv.required->free)
3074 if(ed->priv.required->mark)
3076 if(PRVM_IsEdictReferenced(prog, ed, stage))
3078 ed->priv.required->mark = stage + 1;
3085 Con_DPrintf("leak check used %d stages to find all references\n", stage);
3088 void PRVM_LeakTest(prvm_prog_t *prog)
3091 qboolean leaked = false;
3093 if(!prog->leaktest_active)
3097 for (i = 0; i < prog->numknownstrings; ++i)
3099 if(prog->knownstrings[i])
3100 if(prog->knownstrings_freeable[i])
3101 if(prog->knownstrings_origin[i])
3102 if(!PRVM_IsStringReferenced(prog, PRVM_KNOWNSTRINGBASE + i))
3104 Con_Printf("Unreferenced string found!\n Value: %s\n Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
3110 PRVM_MarkReferencedEdicts(prog);
3111 for(j = 0; j < prog->num_edicts; ++j)
3113 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3114 if(ed->priv.required->free)
3116 if(!ed->priv.required->mark)
3117 if(ed->priv.required->allocation_origin)
3119 Con_Printf("Unreferenced edict found!\n Allocated at: %s\n", ed->priv.required->allocation_origin);
3120 PRVM_ED_Print(prog, ed, NULL);
3125 ed->priv.required->mark = 0; // clear marks again when done
3128 for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
3130 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
3132 if(stringbuffer->origin)
3134 Con_Printf("Open string buffer handle found!\n Allocated at: %s\n", stringbuffer->origin);
3139 for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
3141 if(prog->openfiles[i])
3142 if(prog->openfiles_origin[i])
3144 Con_Printf("Open file handle found!\n Allocated at: %s\n", prog->openfiles_origin[i]);
3149 for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
3151 if(prog->opensearches[i])
3152 if(prog->opensearches_origin[i])
3154 Con_Printf("Open search handle found!\n Allocated at: %s\n", prog->opensearches_origin[i]);
3160 Con_Printf("Congratulations. No leaks found.\n");