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 PROGSFILE.LANGUAGENAME.po and common.LANGUAGENAME.po for string translations; when set to dump, PROGSFILE.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_coverage = {0, "prvm_coverage", "0", "report and count coverage events (1: per-function, 2: coverage() builtin, 4: per-statement)"};
39 cvar_t prvm_backtraceforwarnings = {0, "prvm_backtraceforwarnings", "0", "print a backtrace for warnings too"};
40 cvar_t prvm_leaktest = {0, "prvm_leaktest", "0", "try to detect memory leaks in strings or entities"};
41 cvar_t prvm_leaktest_follow_targetname = {0, "prvm_leaktest_follow_targetname", "0", "if set, target/targetname links are considered when leak testing; this should normally not be required, as entities created during startup - e.g. info_notnull - are never considered leaky"};
42 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)"};
43 cvar_t prvm_errordump = {0, "prvm_errordump", "0", "write a savegame on crash to crash-server.dmp"};
44 cvar_t prvm_breakpointdump = {0, "prvm_breakpointdump", "0", "write a savegame on breakpoint to breakpoint-server.dmp"};
45 cvar_t prvm_reuseedicts_startuptime = {0, "prvm_reuseedicts_startuptime", "2", "allows immediate re-use of freed entity slots during start of new level (value in seconds)"};
46 cvar_t prvm_reuseedicts_neverinsameframe = {0, "prvm_reuseedicts_neverinsameframe", "1", "never allows re-use of freed entity slots during same frame"};
48 static double prvm_reuseedicts_always_allow = 0;
49 qboolean prvm_runawaycheck = true;
51 //============================================================================
59 static void PRVM_MEM_Alloc(prvm_prog_t *prog)
63 // reserve space for the null entity aka world
64 // check bound of max_edicts
65 prog->max_edicts = bound(1 + prog->reserved_edicts, prog->max_edicts, prog->limit_edicts);
66 prog->num_edicts = bound(1 + prog->reserved_edicts, prog->num_edicts, prog->max_edicts);
68 // edictprivate_size has to be min as big prvm_edict_private_t
69 prog->edictprivate_size = max(prog->edictprivate_size,(int)sizeof(prvm_edict_private_t));
72 prog->edicts = (prvm_edict_t *)Mem_Alloc(prog->progs_mempool,prog->limit_edicts * sizeof(prvm_edict_t));
74 // alloc edict private space
75 prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size);
78 prog->entityfieldsarea = prog->entityfields * prog->max_edicts;
79 prog->edictsfields = (prvm_vec_t *)Mem_Alloc(prog->progs_mempool, prog->entityfieldsarea * sizeof(prvm_vec_t));
82 for(i = 0; i < prog->max_edicts; i++)
84 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
85 prog->edicts[i].fields.fp = prog->edictsfields + i * prog->entityfields;
91 PRVM_MEM_IncreaseEdicts
94 void PRVM_MEM_IncreaseEdicts(prvm_prog_t *prog)
98 if(prog->max_edicts >= prog->limit_edicts)
101 prog->begin_increase_edicts(prog);
104 prog->max_edicts = min(prog->max_edicts + 256, prog->limit_edicts);
106 prog->entityfieldsarea = prog->entityfields * prog->max_edicts;
107 prog->edictsfields = (prvm_vec_t*)Mem_Realloc(prog->progs_mempool, (void *)prog->edictsfields, prog->entityfieldsarea * sizeof(prvm_vec_t));
108 prog->edictprivate = (void *)Mem_Realloc(prog->progs_mempool, (void *)prog->edictprivate, prog->max_edicts * prog->edictprivate_size);
110 //set e and v pointers
111 for(i = 0; i < prog->max_edicts; i++)
113 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
114 prog->edicts[i].fields.fp = prog->edictsfields + i * prog->entityfields;
117 prog->end_increase_edicts(prog);
120 //============================================================================
123 int PRVM_ED_FindFieldOffset(prvm_prog_t *prog, const char *field)
126 d = PRVM_ED_FindField(prog, field);
132 int PRVM_ED_FindGlobalOffset(prvm_prog_t *prog, const char *global)
135 d = PRVM_ED_FindGlobal(prog, global);
141 func_t PRVM_ED_FindFunctionOffset(prvm_prog_t *prog, const char *function)
144 f = PRVM_ED_FindFunction(prog, function);
147 return (func_t)(f - prog->functions);
155 prvm_prog_t *PRVM_ProgFromString(const char *str)
157 if (!strcmp(str, "server"))
159 if (!strcmp(str, "client"))
162 if (!strcmp(str, "menu"))
170 PRVM_FriendlyProgFromString
173 prvm_prog_t *PRVM_FriendlyProgFromString(const char *str)
175 prvm_prog_t *prog = PRVM_ProgFromString(str);
178 Con_Printf("%s: unknown program name\n", str);
183 Con_Printf("%s: program is not loaded\n", str);
193 Sets everything to NULL.
195 Nota bene: this also marks the entity as allocated if it has been previously
196 freed and sets the allocation origin.
199 void PRVM_ED_ClearEdict(prvm_prog_t *prog, prvm_edict_t *e)
201 memset(e->fields.fp, 0, prog->entityfields * sizeof(prvm_vec_t));
202 e->priv.required->free = false;
203 e->priv.required->freetime = realtime;
204 if(e->priv.required->allocation_origin)
205 Mem_Free((char *)e->priv.required->allocation_origin);
206 e->priv.required->allocation_origin = PRVM_AllocationOrigin(prog);
208 // AK: Let the init_edict function determine if something needs to be initialized
209 prog->init_edict(prog, e);
212 const char *PRVM_AllocationOrigin(prvm_prog_t *prog)
215 if(prog->leaktest_active)
216 if(prog->depth > 0) // actually in QC code and not just parsing the entities block of a map/savegame
218 buf = (char *)PRVM_Alloc(256);
219 PRVM_ShortStackTrace(prog, buf, 256);
228 Returns if this particular edict could get allocated by PRVM_ED_Alloc
231 qboolean PRVM_ED_CanAlloc(prvm_prog_t *prog, prvm_edict_t *e)
233 if(!e->priv.required->free)
235 if(prvm_reuseedicts_always_allow == realtime)
237 if(realtime <= e->priv.required->freetime + 0.1 && prvm_reuseedicts_neverinsameframe.integer)
238 return false; // never allow reuse in same frame (causes networking trouble)
239 if(e->priv.required->freetime < prog->starttime + prvm_reuseedicts_startuptime.value)
241 if(realtime > e->priv.required->freetime + 1)
243 return false; // entity slot still blocked because the entity was freed less than one second ago
250 Either finds a free edict, or allocates a new one.
251 Try to avoid reusing an entity that was recently freed, because it
252 can cause the client to think the entity morphed into something else
253 instead of being removed and recreated, which can cause interpolated
254 angles and bad trails.
257 prvm_edict_t *PRVM_ED_Alloc(prvm_prog_t *prog)
262 // the client qc dont need maxclients
263 // thus it doesnt need to use svs.maxclients
264 // AK: changed i=svs.maxclients+1
265 // AK: changed so the edict 0 wont spawn -> used as reserved/world entity
266 // although the menu/client has no world
267 for (i = prog->reserved_edicts + 1;i < prog->num_edicts;i++)
269 e = PRVM_EDICT_NUM(i);
270 if(PRVM_ED_CanAlloc(prog, e))
272 PRVM_ED_ClearEdict (prog, e);
277 if (i == prog->limit_edicts)
278 prog->error_cmd("%s: PRVM_ED_Alloc: no free edicts", prog->name);
281 if (prog->num_edicts >= prog->max_edicts)
282 PRVM_MEM_IncreaseEdicts(prog);
284 e = PRVM_EDICT_NUM(i);
286 PRVM_ED_ClearEdict(prog, e);
294 Marks the edict as free
295 FIXME: walk all entities and NULL out references to this entity
298 void PRVM_ED_Free(prvm_prog_t *prog, prvm_edict_t *ed)
300 // dont delete the null entity (world) or reserved edicts
301 if (ed - prog->edicts <= prog->reserved_edicts)
304 prog->free_edict(prog, ed);
306 ed->priv.required->free = true;
307 ed->priv.required->freetime = realtime;
308 if(ed->priv.required->allocation_origin)
310 Mem_Free((char *)ed->priv.required->allocation_origin);
311 ed->priv.required->allocation_origin = NULL;
315 //===========================================================================
322 static ddef_t *PRVM_ED_GlobalAtOfs (prvm_prog_t *prog, int ofs)
327 for (i = 0;i < prog->numglobaldefs;i++)
329 def = &prog->globaldefs[i];
341 ddef_t *PRVM_ED_FieldAtOfs (prvm_prog_t *prog, int ofs)
346 for (i = 0;i < prog->numfielddefs;i++)
348 def = &prog->fielddefs[i];
360 ddef_t *PRVM_ED_FindField (prvm_prog_t *prog, const char *name)
365 for (i = 0;i < prog->numfielddefs;i++)
367 def = &prog->fielddefs[i];
368 if (!strcmp(PRVM_GetString(prog, def->s_name), name))
379 ddef_t *PRVM_ED_FindGlobal (prvm_prog_t *prog, const char *name)
384 for (i = 0;i < prog->numglobaldefs;i++)
386 def = &prog->globaldefs[i];
387 if (!strcmp(PRVM_GetString(prog, def->s_name), name))
399 mfunction_t *PRVM_ED_FindFunction (prvm_prog_t *prog, const char *name)
404 for (i = 0;i < prog->numfunctions;i++)
406 func = &prog->functions[i];
407 if (!strcmp(PRVM_GetString(prog, func->s_name), name))
418 Returns a string describing *data in a type specific manner
421 static char *PRVM_ValueString (prvm_prog_t *prog, etype_t type, prvm_eval_t *val, char *line, size_t linelength)
427 type = (etype_t)((int) type & ~DEF_SAVEGLOBAL);
432 strlcpy (line, PRVM_GetString (prog, val->string), linelength);
436 if (n < 0 || n >= prog->max_edicts)
437 dpsnprintf (line, linelength, "entity %i (invalid!)", n);
439 dpsnprintf (line, linelength, "entity %i", n);
442 f = prog->functions + val->function;
443 dpsnprintf (line, linelength, "%s()", PRVM_GetString(prog, f->s_name));
446 def = PRVM_ED_FieldAtOfs ( prog, val->_int );
447 dpsnprintf (line, linelength, ".%s", PRVM_GetString(prog, def->s_name));
450 dpsnprintf (line, linelength, "void");
453 // LordHavoc: changed from %5.1f to %10.4f
454 dpsnprintf (line, linelength, FLOAT_LOSSLESS_FORMAT, val->_float);
457 // LordHavoc: changed from %5.1f to %10.4f
458 dpsnprintf (line, linelength, "'" VECTOR_LOSSLESS_FORMAT "'", val->vector[0], val->vector[1], val->vector[2]);
461 dpsnprintf (line, linelength, "pointer");
464 dpsnprintf (line, linelength, "bad type %i", (int) type);
475 Returns a string describing *data in a type specific manner
476 Easier to parse than PR_ValueString
479 char *PRVM_UglyValueString (prvm_prog_t *prog, etype_t type, prvm_eval_t *val, char *line, size_t linelength)
486 type = (etype_t)((int)type & ~DEF_SAVEGLOBAL);
491 // Parse the string a bit to turn special characters
492 // (like newline, specifically) into escape codes,
493 // this fixes saving games from various mods
494 s = PRVM_GetString (prog, val->string);
495 for (i = 0;i < (int)linelength - 2 && *s;)
525 dpsnprintf (line, linelength, "%i", i);
528 f = prog->functions + val->function;
529 strlcpy (line, PRVM_GetString (prog, f->s_name), linelength);
532 def = PRVM_ED_FieldAtOfs ( prog, val->_int );
533 dpsnprintf (line, linelength, ".%s", PRVM_GetString(prog, def->s_name));
536 dpsnprintf (line, linelength, "void");
539 dpsnprintf (line, linelength, FLOAT_LOSSLESS_FORMAT, val->_float);
542 dpsnprintf (line, linelength, VECTOR_LOSSLESS_FORMAT, val->vector[0], val->vector[1], val->vector[2]);
545 dpsnprintf (line, linelength, "bad type %i", type);
556 Returns a string with a description and the contents of a global,
557 padded to 20 field width
560 char *PRVM_GlobalString (prvm_prog_t *prog, int ofs, char *line, size_t linelength)
566 char valuebuf[MAX_INPUTLINE];
568 val = (prvm_eval_t *)&prog->globals.fp[ofs];
569 def = PRVM_ED_GlobalAtOfs(prog, ofs);
571 dpsnprintf (line, linelength, "GLOBAL%i", ofs);
574 s = PRVM_ValueString (prog, (etype_t)def->type, val, valuebuf, sizeof(valuebuf));
575 dpsnprintf (line, linelength, "%s (=%s)", PRVM_GetString(prog, def->s_name), s);
579 //for ( ; i<20 ; i++)
580 // strcat (line," ");
586 char *PRVM_GlobalStringNoContents (prvm_prog_t *prog, int ofs, char *line, size_t linelength)
591 def = PRVM_ED_GlobalAtOfs(prog, ofs);
593 dpsnprintf (line, linelength, "GLOBAL%i", ofs);
595 dpsnprintf (line, linelength, "%s", PRVM_GetString(prog, def->s_name));
598 //for ( ; i<20 ; i++)
599 // strcat (line," ");
613 // LordHavoc: optimized this to print out much more quickly (tempstring)
614 // LordHavoc: changed to print out every 4096 characters (incase there are a lot of fields to print)
615 void PRVM_ED_Print(prvm_prog_t *prog, prvm_edict_t *ed, const char *wildcard_fieldname)
623 char tempstring[MAX_INPUTLINE], tempstring2[260]; // temporary string buffers
624 char valuebuf[MAX_INPUTLINE];
626 if (ed->priv.required->free)
628 Con_Printf("%s: FREE\n",prog->name);
633 dpsnprintf(tempstring, sizeof(tempstring), "\n%s EDICT %i:\n", prog->name, PRVM_NUM_FOR_EDICT(ed));
634 for (i = 1;i < prog->numfielddefs;i++)
636 d = &prog->fielddefs[i];
637 name = PRVM_GetString(prog, d->s_name);
638 if(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
639 continue; // skip _x, _y, _z vars
641 // Check Field Name Wildcard
642 if(wildcard_fieldname)
643 if( !matchpattern(name, wildcard_fieldname, 1) )
644 // Didn't match; skip
647 val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
649 // if the value is still all 0, skip the field
650 type = d->type & ~DEF_SAVEGLOBAL;
652 for (j=0 ; j<prvm_type_size[type] ; j++)
655 if (j == prvm_type_size[type])
658 if (strlen(name) > sizeof(tempstring2)-4)
660 memcpy (tempstring2, name, sizeof(tempstring2)-4);
661 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
662 tempstring2[sizeof(tempstring2)-1] = 0;
665 strlcat(tempstring, name, sizeof(tempstring));
666 for (l = strlen(name);l < 14;l++)
667 strlcat(tempstring, " ", sizeof(tempstring));
668 strlcat(tempstring, " ", sizeof(tempstring));
670 name = PRVM_ValueString(prog, (etype_t)d->type, val, valuebuf, sizeof(valuebuf));
671 if (strlen(name) > sizeof(tempstring2)-4)
673 memcpy (tempstring2, name, sizeof(tempstring2)-4);
674 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
675 tempstring2[sizeof(tempstring2)-1] = 0;
678 strlcat(tempstring, name, sizeof(tempstring));
679 strlcat(tempstring, "\n", sizeof(tempstring));
680 if (strlen(tempstring) >= sizeof(tempstring)/2)
682 Con_Print(tempstring);
687 Con_Print(tempstring);
697 extern cvar_t developer_entityparsing;
698 void PRVM_ED_Write (prvm_prog_t *prog, qfile_t *f, prvm_edict_t *ed)
706 char valuebuf[MAX_INPUTLINE];
710 if (ed->priv.required->free)
716 for (i = 1;i < prog->numfielddefs;i++)
718 d = &prog->fielddefs[i];
719 name = PRVM_GetString(prog, d->s_name);
721 if(developer_entityparsing.integer)
722 Con_Printf("PRVM_ED_Write: at entity %d field %s\n", PRVM_NUM_FOR_EDICT(ed), name);
724 //if(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
725 if(strlen(name) > 1 && name[strlen(name)-2] == '_')
726 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?)
728 val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
730 // if the value is still all 0, skip the field
731 type = d->type & ~DEF_SAVEGLOBAL;
732 for (j=0 ; j<prvm_type_size[type] ; j++)
735 if (j == prvm_type_size[type])
738 FS_Printf(f,"\"%s\" ",name);
739 prog->statestring = va(vabuf, sizeof(vabuf), "PRVM_ED_Write, ent=%d, name=%s", i, name);
740 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString(prog, (etype_t)d->type, val, valuebuf, sizeof(valuebuf)));
741 prog->statestring = NULL;
747 void PRVM_ED_PrintNum (prvm_prog_t *prog, int ent, const char *wildcard_fieldname)
749 PRVM_ED_Print(prog, PRVM_EDICT_NUM(ent), wildcard_fieldname);
754 PRVM_ED_PrintEdicts_f
756 For debugging, prints all the entities in the current server
759 void PRVM_ED_PrintEdicts_f (void)
763 const char *wildcard_fieldname;
765 if(Cmd_Argc() < 2 || Cmd_Argc() > 3)
767 Con_Print("prvm_edicts <program name> <optional field name wildcard>\n");
771 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
775 wildcard_fieldname = Cmd_Argv(2);
777 wildcard_fieldname = NULL;
779 Con_Printf("%s: %i entities\n", prog->name, prog->num_edicts);
780 for (i=0 ; i<prog->num_edicts ; i++)
781 PRVM_ED_PrintNum (prog, i, wildcard_fieldname);
788 For debugging, prints a single edict
791 static void PRVM_ED_PrintEdict_f (void)
795 const char *wildcard_fieldname;
797 if(Cmd_Argc() < 3 || Cmd_Argc() > 4)
799 Con_Print("prvm_edict <program name> <edict number> <optional field name wildcard>\n");
803 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
806 i = atoi (Cmd_Argv(2));
807 if (i >= prog->num_edicts)
809 Con_Print("Bad edict number\n");
813 // Optional Wildcard Provided
814 wildcard_fieldname = Cmd_Argv(3);
817 wildcard_fieldname = NULL;
818 PRVM_ED_PrintNum (prog, i, wildcard_fieldname);
828 // 2 possibilities : 1. just displaying the active edict count
829 // 2. making a function pointer [x]
830 static void PRVM_ED_Count_f (void)
836 Con_Print("prvm_count <program name>\n");
840 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
843 prog->count_edicts(prog);
847 ==============================================================================
851 FIXME: need to tag constants, doesn't really work
852 ==============================================================================
860 void PRVM_ED_WriteGlobals (prvm_prog_t *prog, qfile_t *f)
867 char valuebuf[MAX_INPUTLINE];
870 for (i = 0;i < prog->numglobaldefs;i++)
872 def = &prog->globaldefs[i];
874 if ( !(def->type & DEF_SAVEGLOBAL) )
876 type &= ~DEF_SAVEGLOBAL;
878 if (type != ev_string && type != ev_float && type != ev_entity)
881 name = PRVM_GetString(prog, def->s_name);
883 if(developer_entityparsing.integer)
884 Con_Printf("PRVM_ED_WriteGlobals: at global %s\n", name);
886 prog->statestring = va(vabuf, sizeof(vabuf), "PRVM_ED_WriteGlobals, name=%s", name);
887 FS_Printf(f,"\"%s\" ", name);
888 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString(prog, (etype_t)type, (prvm_eval_t *)&prog->globals.fp[def->ofs], valuebuf, sizeof(valuebuf)));
889 prog->statestring = NULL;
899 void PRVM_ED_ParseGlobals (prvm_prog_t *prog, const char *data)
901 char keyname[MAX_INPUTLINE];
907 if (!COM_ParseToken_Simple(&data, false, false, true))
908 prog->error_cmd("PRVM_ED_ParseGlobals: EOF without closing brace");
909 if (com_token[0] == '}')
912 if (developer_entityparsing.integer)
913 Con_Printf("Key: \"%s\"", com_token);
915 strlcpy (keyname, com_token, sizeof(keyname));
918 if (!COM_ParseToken_Simple(&data, false, true, true))
919 prog->error_cmd("PRVM_ED_ParseGlobals: EOF without closing brace");
921 if (developer_entityparsing.integer)
922 Con_Printf(" \"%s\"\n", com_token);
924 if (com_token[0] == '}')
925 prog->error_cmd("PRVM_ED_ParseGlobals: closing brace without data");
927 key = PRVM_ED_FindGlobal (prog, keyname);
930 Con_DPrintf("'%s' is not a global on %s\n", keyname, prog->name);
934 if (!PRVM_ED_ParseEpair(prog, NULL, key, com_token, true))
935 prog->error_cmd("PRVM_ED_ParseGlobals: parse error");
939 //============================================================================
946 Can parse either fields or globals
947 returns false if error
950 qboolean PRVM_ED_ParseEpair(prvm_prog_t *prog, prvm_edict_t *ent, ddef_t *key, const char *s, qboolean parsebackslash)
959 val = (prvm_eval_t *)(ent->fields.fp + key->ofs);
961 val = (prvm_eval_t *)(prog->globals.fp + key->ofs);
962 switch (key->type & ~DEF_SAVEGLOBAL)
965 l = (int)strlen(s) + 1;
966 val->string = PRVM_AllocString(prog, l, &new_p);
967 for (i = 0;i < l;i++)
969 if (s[i] == '\\' && s[i+1] && parsebackslash)
974 else if (s[i] == 'r')
985 while (*s && ISWHITESPACE(*s))
987 val->_float = atof(s);
991 for (i = 0;i < 3;i++)
993 while (*s && ISWHITESPACE(*s))
997 val->vector[i] = atof(s);
998 while (!ISWHITESPACE(*s))
1006 while (*s && ISWHITESPACE(*s))
1009 if (i >= prog->limit_edicts)
1010 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);
1011 while (i >= prog->max_edicts)
1012 PRVM_MEM_IncreaseEdicts(prog);
1013 // if IncreaseEdicts was called the base pointer needs to be updated
1015 val = (prvm_eval_t *)(ent->fields.fp + key->ofs);
1016 val->edict = PRVM_EDICT_TO_PROG(PRVM_EDICT_NUM((int)i));
1022 Con_DPrintf("PRVM_ED_ParseEpair: Bogus field name %s in %s\n", s, prog->name);
1025 def = PRVM_ED_FindField(prog, s + 1);
1028 Con_DPrintf("PRVM_ED_ParseEpair: Can't find field %s in %s\n", s, prog->name);
1031 val->_int = def->ofs;
1035 func = PRVM_ED_FindFunction(prog, s);
1038 Con_Printf("PRVM_ED_ParseEpair: Can't find function %s in %s\n", s, prog->name);
1041 val->function = func - prog->functions;
1045 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);
1055 Console command to send a string to QC function GameCommand of the
1059 sv_cmd adminmsg 3 "do not teamkill"
1060 cl_cmd someclientcommand
1061 menu_cmd somemenucommand
1063 All progs can support this extension; sg calls it in server QC, cg in client
1067 static void PRVM_GameCommand(const char *whichprogs, const char *whichcmd)
1072 Con_Printf("%s text...\n", whichcmd);
1076 if (!(prog = PRVM_FriendlyProgFromString(whichprogs)))
1079 if(!PRVM_allfunction(GameCommand))
1081 Con_Printf("%s program do not support GameCommand!\n", whichprogs);
1085 int restorevm_tempstringsbuf_cursize;
1090 restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
1091 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(prog, s ? s : "");
1092 prog->ExecuteProgram(prog, PRVM_allfunction(GameCommand), "QC function GameCommand is missing");
1093 prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1096 static void PRVM_GameCommand_Server_f(void)
1098 PRVM_GameCommand("server", "sv_cmd");
1100 static void PRVM_GameCommand_Client_f(void)
1102 PRVM_GameCommand("client", "cl_cmd");
1104 static void PRVM_GameCommand_Menu_f(void)
1106 PRVM_GameCommand("menu", "menu_cmd");
1113 Console command to load a field of a specified edict
1116 static void PRVM_ED_EdictGet_f(void)
1123 char valuebuf[MAX_INPUTLINE];
1125 if(Cmd_Argc() != 4 && Cmd_Argc() != 5)
1127 Con_Print("prvm_edictget <program name> <edict number> <field> [<cvar>]\n");
1131 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
1134 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1136 if((key = PRVM_ED_FindField(prog, Cmd_Argv(3))) == 0)
1138 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1142 v = (prvm_eval_t *)(ed->fields.fp + key->ofs);
1143 s = PRVM_UglyValueString(prog, (etype_t)key->type, v, valuebuf, sizeof(valuebuf));
1146 cvar_t *cvar = Cvar_FindVar(Cmd_Argv(4));
1147 if (cvar && cvar->flags & CVAR_READONLY)
1149 Con_Printf("prvm_edictget: %s is read-only\n", cvar->name);
1152 Cvar_Get(Cmd_Argv(4), s, 0, NULL);
1155 Con_Printf("%s\n", s);
1161 static void PRVM_ED_GlobalGet_f(void)
1167 char valuebuf[MAX_INPUTLINE];
1169 if(Cmd_Argc() != 3 && Cmd_Argc() != 4)
1171 Con_Print("prvm_globalget <program name> <global> [<cvar>]\n");
1175 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
1178 key = PRVM_ED_FindGlobal(prog, Cmd_Argv(2));
1181 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
1185 v = (prvm_eval_t *) &prog->globals.fp[key->ofs];
1186 s = PRVM_UglyValueString(prog, (etype_t)key->type, v, valuebuf, sizeof(valuebuf));
1189 cvar_t *cvar = Cvar_FindVar(Cmd_Argv(3));
1190 if (cvar && cvar->flags & CVAR_READONLY)
1192 Con_Printf("prvm_globalget: %s is read-only\n", cvar->name);
1195 Cvar_Get(Cmd_Argv(3), s, 0, NULL);
1198 Con_Printf("%s\n", s);
1208 Console command to set a field of a specified edict
1211 static void PRVM_ED_EdictSet_f(void)
1219 Con_Print("prvm_edictset <program name> <edict number> <field> <value>\n");
1223 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
1226 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1228 if((key = PRVM_ED_FindField(prog, Cmd_Argv(3))) == 0)
1229 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1231 PRVM_ED_ParseEpair(prog, ed, key, Cmd_Argv(4), true);
1235 ====================
1238 Parses an edict out of the given string, returning the new position
1239 ed should be a properly initialized empty edict.
1240 Used for initial level load and for savegames.
1241 ====================
1243 const char *PRVM_ED_ParseEdict (prvm_prog_t *prog, const char *data, prvm_edict_t *ent)
1253 // go through all the dictionary pairs
1257 if (!COM_ParseToken_Simple(&data, false, false, true))
1258 prog->error_cmd("PRVM_ED_ParseEdict: EOF without closing brace");
1259 if (developer_entityparsing.integer)
1260 Con_Printf("Key: \"%s\"", com_token);
1261 if (com_token[0] == '}')
1264 // anglehack is to allow QuakeEd to write single scalar angles
1265 // and allow them to be turned into vectors. (FIXME...)
1266 if (!strcmp(com_token, "angle"))
1268 strlcpy (com_token, "angles", sizeof(com_token));
1274 // FIXME: change light to _light to get rid of this hack
1275 if (!strcmp(com_token, "light"))
1276 strlcpy (com_token, "light_lev", sizeof(com_token)); // hack for single light def
1278 strlcpy (keyname, com_token, sizeof(keyname));
1280 // another hack to fix keynames with trailing spaces
1281 n = strlen(keyname);
1282 while (n && keyname[n-1] == ' ')
1289 if (!COM_ParseToken_Simple(&data, false, false, true))
1290 prog->error_cmd("PRVM_ED_ParseEdict: EOF without closing brace");
1291 if (developer_entityparsing.integer)
1292 Con_Printf(" \"%s\"\n", com_token);
1294 if (com_token[0] == '}')
1295 prog->error_cmd("PRVM_ED_ParseEdict: closing brace without data");
1299 // ignore attempts to set key "" (this problem occurs in nehahra neh1m8.bsp)
1303 // keynames with a leading underscore are used for utility comments,
1304 // and are immediately discarded by quake
1305 if (keyname[0] == '_')
1308 key = PRVM_ED_FindField (prog, keyname);
1311 Con_DPrintf("%s: '%s' is not a field\n", prog->name, keyname);
1318 strlcpy (temp, com_token, sizeof(temp));
1319 dpsnprintf (com_token, sizeof(com_token), "0 %s 0", temp);
1322 if (!PRVM_ED_ParseEpair(prog, ent, key, com_token, strcmp(keyname, "wad") != 0))
1323 prog->error_cmd("PRVM_ED_ParseEdict: parse error");
1327 ent->priv.required->free = true;
1328 ent->priv.required->freetime = realtime;
1337 PRVM_ED_LoadFromFile
1339 The entities are directly placed in the array, rather than allocated with
1340 PRVM_ED_Alloc, because otherwise an error loading the map would have entity
1341 number references out of order.
1343 Creates a server's entity / program execution context by
1344 parsing textual entity definitions out of an ent file.
1346 Used for both fresh maps and savegame loads. A fresh map would also need
1347 to call PRVM_ED_CallSpawnFunctions () to let the objects initialize themselves.
1350 void PRVM_ED_LoadFromFile (prvm_prog_t *prog, const char *data)
1353 int parsed, inhibited, spawned, died;
1354 const char *funcname;
1363 prvm_reuseedicts_always_allow = realtime;
1368 // parse the opening brace
1369 if (!COM_ParseToken_Simple(&data, false, false, true))
1371 if (com_token[0] != '{')
1372 prog->error_cmd("PRVM_ED_LoadFromFile: %s: found %s when expecting {", prog->name, com_token);
1374 // CHANGED: this is not conform to PR_LoadFromFile
1375 if(prog->loadintoworld)
1377 prog->loadintoworld = false;
1378 ent = PRVM_EDICT_NUM(0);
1381 ent = PRVM_ED_Alloc(prog);
1384 if (ent != prog->edicts) // hack
1385 memset (ent->fields.fp, 0, prog->entityfields * sizeof(prvm_vec_t));
1387 data = PRVM_ED_ParseEdict (prog, data, ent);
1390 // remove the entity ?
1391 if(!prog->load_edict(prog, ent))
1393 PRVM_ED_Free(prog, ent);
1398 if (PRVM_serverfunction(SV_OnEntityPreSpawnFunction))
1401 PRVM_serverglobalfloat(time) = sv.time;
1402 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1403 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityPreSpawnFunction), "QC function SV_OnEntityPreSpawnFunction is missing");
1406 if(ent->priv.required->free)
1413 // immediately call spawn function, but only if there is a self global and a classname
1415 if(!ent->priv.required->free)
1417 if (!PRVM_alledictstring(ent, classname))
1419 Con_Print("No classname for:\n");
1420 PRVM_ED_Print(prog, ent, NULL);
1421 PRVM_ED_Free (prog, ent);
1425 // look for the spawn function
1426 funcname = PRVM_GetString(prog, PRVM_alledictstring(ent, classname));
1427 func = PRVM_ED_FindFunction (prog, va(vabuf, sizeof(vabuf), "spawnfunc_%s", funcname));
1429 if(!PRVM_allglobalfloat(require_spawnfunc_prefix))
1430 func = PRVM_ED_FindFunction (prog, funcname);
1434 // check for OnEntityNoSpawnFunction
1435 if (PRVM_serverfunction(SV_OnEntityNoSpawnFunction))
1438 PRVM_serverglobalfloat(time) = sv.time;
1439 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1440 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityNoSpawnFunction), "QC function SV_OnEntityNoSpawnFunction is missing");
1444 if (developer.integer > 0) // don't confuse non-developers with errors
1446 Con_Print("No spawn function for:\n");
1447 PRVM_ED_Print(prog, ent, NULL);
1449 PRVM_ED_Free (prog, ent);
1450 continue; // not included in "inhibited" count
1456 PRVM_serverglobalfloat(time) = sv.time;
1457 PRVM_allglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1458 prog->ExecuteProgram(prog, func - prog->functions, "");
1462 if(!ent->priv.required->free)
1463 if (PRVM_serverfunction(SV_OnEntityPostSpawnFunction))
1466 PRVM_serverglobalfloat(time) = sv.time;
1467 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1468 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityPostSpawnFunction), "QC function SV_OnEntityPostSpawnFunction is missing");
1472 if (ent->priv.required->free)
1476 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);
1478 prvm_reuseedicts_always_allow = 0;
1481 static void PRVM_FindOffsets(prvm_prog_t *prog)
1483 // field and global searches use -1 for NULL
1484 memset(&prog->fieldoffsets, -1, sizeof(prog->fieldoffsets));
1485 memset(&prog->globaloffsets, -1, sizeof(prog->globaloffsets));
1486 // function searches use 0 for NULL
1487 memset(&prog->funcoffsets, 0, sizeof(prog->funcoffsets));
1488 #define PRVM_DECLARE_serverglobalfloat(x)
1489 #define PRVM_DECLARE_serverglobalvector(x)
1490 #define PRVM_DECLARE_serverglobalstring(x)
1491 #define PRVM_DECLARE_serverglobaledict(x)
1492 #define PRVM_DECLARE_serverglobalfunction(x)
1493 #define PRVM_DECLARE_clientglobalfloat(x)
1494 #define PRVM_DECLARE_clientglobalvector(x)
1495 #define PRVM_DECLARE_clientglobalstring(x)
1496 #define PRVM_DECLARE_clientglobaledict(x)
1497 #define PRVM_DECLARE_clientglobalfunction(x)
1498 #define PRVM_DECLARE_menuglobalfloat(x)
1499 #define PRVM_DECLARE_menuglobalvector(x)
1500 #define PRVM_DECLARE_menuglobalstring(x)
1501 #define PRVM_DECLARE_menuglobaledict(x)
1502 #define PRVM_DECLARE_menuglobalfunction(x)
1503 #define PRVM_DECLARE_serverfieldfloat(x)
1504 #define PRVM_DECLARE_serverfieldvector(x)
1505 #define PRVM_DECLARE_serverfieldstring(x)
1506 #define PRVM_DECLARE_serverfieldedict(x)
1507 #define PRVM_DECLARE_serverfieldfunction(x)
1508 #define PRVM_DECLARE_clientfieldfloat(x)
1509 #define PRVM_DECLARE_clientfieldvector(x)
1510 #define PRVM_DECLARE_clientfieldstring(x)
1511 #define PRVM_DECLARE_clientfieldedict(x)
1512 #define PRVM_DECLARE_clientfieldfunction(x)
1513 #define PRVM_DECLARE_menufieldfloat(x)
1514 #define PRVM_DECLARE_menufieldvector(x)
1515 #define PRVM_DECLARE_menufieldstring(x)
1516 #define PRVM_DECLARE_menufieldedict(x)
1517 #define PRVM_DECLARE_menufieldfunction(x)
1518 #define PRVM_DECLARE_serverfunction(x)
1519 #define PRVM_DECLARE_clientfunction(x)
1520 #define PRVM_DECLARE_menufunction(x)
1521 #define PRVM_DECLARE_field(x) prog->fieldoffsets.x = PRVM_ED_FindFieldOffset(prog, #x);
1522 #define PRVM_DECLARE_global(x) prog->globaloffsets.x = PRVM_ED_FindGlobalOffset(prog, #x);
1523 #define PRVM_DECLARE_function(x) prog->funcoffsets.x = PRVM_ED_FindFunctionOffset(prog, #x);
1524 #include "prvm_offsets.h"
1525 #undef PRVM_DECLARE_serverglobalfloat
1526 #undef PRVM_DECLARE_serverglobalvector
1527 #undef PRVM_DECLARE_serverglobalstring
1528 #undef PRVM_DECLARE_serverglobaledict
1529 #undef PRVM_DECLARE_serverglobalfunction
1530 #undef PRVM_DECLARE_clientglobalfloat
1531 #undef PRVM_DECLARE_clientglobalvector
1532 #undef PRVM_DECLARE_clientglobalstring
1533 #undef PRVM_DECLARE_clientglobaledict
1534 #undef PRVM_DECLARE_clientglobalfunction
1535 #undef PRVM_DECLARE_menuglobalfloat
1536 #undef PRVM_DECLARE_menuglobalvector
1537 #undef PRVM_DECLARE_menuglobalstring
1538 #undef PRVM_DECLARE_menuglobaledict
1539 #undef PRVM_DECLARE_menuglobalfunction
1540 #undef PRVM_DECLARE_serverfieldfloat
1541 #undef PRVM_DECLARE_serverfieldvector
1542 #undef PRVM_DECLARE_serverfieldstring
1543 #undef PRVM_DECLARE_serverfieldedict
1544 #undef PRVM_DECLARE_serverfieldfunction
1545 #undef PRVM_DECLARE_clientfieldfloat
1546 #undef PRVM_DECLARE_clientfieldvector
1547 #undef PRVM_DECLARE_clientfieldstring
1548 #undef PRVM_DECLARE_clientfieldedict
1549 #undef PRVM_DECLARE_clientfieldfunction
1550 #undef PRVM_DECLARE_menufieldfloat
1551 #undef PRVM_DECLARE_menufieldvector
1552 #undef PRVM_DECLARE_menufieldstring
1553 #undef PRVM_DECLARE_menufieldedict
1554 #undef PRVM_DECLARE_menufieldfunction
1555 #undef PRVM_DECLARE_serverfunction
1556 #undef PRVM_DECLARE_clientfunction
1557 #undef PRVM_DECLARE_menufunction
1558 #undef PRVM_DECLARE_field
1559 #undef PRVM_DECLARE_global
1560 #undef PRVM_DECLARE_function
1565 typedef struct dpfield_s
1572 #define DPFIELDS (sizeof(dpfields) / sizeof(dpfield_t))
1574 dpfield_t dpfields[] =
1585 #define PO_HASHSIZE 16384
1586 typedef struct po_string_s
1589 struct po_string_s *nextonhashchain;
1594 po_string_t *hashtable[PO_HASHSIZE];
1597 static void PRVM_PO_UnparseString(char *out, const char *in, size_t outsize)
1606 case '\a': if(outsize >= 2) { *out++ = '\\'; *out++ = 'a'; outsize -= 2; } break;
1607 case '\b': if(outsize >= 2) { *out++ = '\\'; *out++ = 'b'; outsize -= 2; } break;
1608 case '\t': if(outsize >= 2) { *out++ = '\\'; *out++ = 't'; outsize -= 2; } break;
1609 case '\r': if(outsize >= 2) { *out++ = '\\'; *out++ = 'r'; outsize -= 2; } break;
1610 case '\n': if(outsize >= 2) { *out++ = '\\'; *out++ = 'n'; outsize -= 2; } break;
1611 case '\\': if(outsize >= 2) { *out++ = '\\'; *out++ = '\\'; outsize -= 2; } break;
1612 case '"': if(outsize >= 2) { *out++ = '\\'; *out++ = '"'; outsize -= 2; } break;
1614 if(*in >= 0 && *in <= 0x1F)
1619 *out++ = '0' + ((*in & 0700) >> 6);
1620 *out++ = '0' + ((*in & 0070) >> 3);
1621 *out++ = '0' + (*in & 0007) ;
1638 static void PRVM_PO_ParseString(char *out, const char *in, size_t outsize)
1651 case 'a': if(outsize > 0) { *out++ = '\a'; --outsize; } break;
1652 case 'b': if(outsize > 0) { *out++ = '\b'; --outsize; } break;
1653 case 't': if(outsize > 0) { *out++ = '\t'; --outsize; } break;
1654 case 'r': if(outsize > 0) { *out++ = '\r'; --outsize; } break;
1655 case 'n': if(outsize > 0) { *out++ = '\n'; --outsize; } break;
1656 case '\\': if(outsize > 0) { *out++ = '\\'; --outsize; } break;
1657 case '"': if(outsize > 0) { *out++ = '"'; --outsize; } break;
1658 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
1662 if(*in >= '0' && *in <= '7')
1665 *out = (*out << 3) | (*in - '0');
1668 if(*in >= '0' && *in <= '7')
1671 *out = (*out << 3) | (*in - '0');
1682 if(outsize > 0) { *out++ = *in; --outsize; }
1697 static po_t *PRVM_PO_Load(const char *filename, const char *filename2, mempool_t *pool)
1702 char inbuf[MAX_INPUTLINE];
1703 char decodedbuf[MAX_INPUTLINE];
1706 po_string_t thisstr;
1709 for (i = 0; i < 2; ++i)
1711 const char *buf = (const char *)
1712 FS_LoadFile((i > 0 ? filename : filename2), pool, true, NULL);
1713 // first read filename2, then read filename
1714 // so that progs.dat.de.po wins over common.de.po
1715 // and within file, last item wins
1722 po = (po_t *)Mem_Alloc(pool, sizeof(*po));
1723 memset(po, 0, sizeof(*po));
1726 memset(&thisstr, 0, sizeof(thisstr)); // hush compiler warning
1734 p = strchr(p, '\n');
1740 if(*p == '\r' || *p == '\n')
1745 if(!strncmp(p, "msgid \"", 7))
1750 else if(!strncmp(p, "msgstr \"", 8))
1757 p = strchr(p, '\n');
1767 q = strchr(p, '\n');
1774 if((size_t)(q - p) >= (size_t) sizeof(inbuf))
1776 strlcpy(inbuf, p, q - p); // not - 1, because this adds a NUL
1777 PRVM_PO_ParseString(decodedbuf + decodedpos, inbuf, sizeof(decodedbuf) - decodedpos);
1778 decodedpos += strlen(decodedbuf + decodedpos);
1788 Mem_Free(thisstr.key);
1789 thisstr.key = (char *)Mem_Alloc(pool, decodedpos + 1);
1790 memcpy(thisstr.key, decodedbuf, decodedpos + 1);
1792 else if(decodedpos > 0 && thisstr.key) // skip empty translation results
1794 thisstr.value = (char *)Mem_Alloc(pool, decodedpos + 1);
1795 memcpy(thisstr.value, decodedbuf, decodedpos + 1);
1796 hashindex = CRC_Block((const unsigned char *) thisstr.key, strlen(thisstr.key)) % PO_HASHSIZE;
1797 thisstr.nextonhashchain = po->hashtable[hashindex];
1798 po->hashtable[hashindex] = (po_string_t *)Mem_Alloc(pool, sizeof(thisstr));
1799 memcpy(po->hashtable[hashindex], &thisstr, sizeof(thisstr));
1800 memset(&thisstr, 0, sizeof(thisstr));
1804 Mem_Free((char *) buf);
1809 static const char *PRVM_PO_Lookup(po_t *po, const char *str)
1811 int hashindex = CRC_Block((const unsigned char *) str, strlen(str)) % PO_HASHSIZE;
1812 po_string_t *p = po->hashtable[hashindex];
1815 if(!strcmp(str, p->key))
1817 p = p->nextonhashchain;
1821 static void PRVM_PO_Destroy(po_t *po)
1824 for(i = 0; i < PO_HASHSIZE; ++i)
1826 po_string_t *p = po->hashtable[i];
1830 p = p->nextonhashchain;
1839 void PRVM_LeakTest(prvm_prog_t *prog);
1840 void PRVM_Prog_Reset(prvm_prog_t *prog)
1844 PRVM_LeakTest(prog);
1845 prog->reset_cmd(prog);
1846 Mem_FreePool(&prog->progs_mempool);
1848 PRVM_PO_Destroy((po_t *) prog->po);
1850 memset(prog,0,sizeof(prvm_prog_t));
1851 prog->break_statement = -1;
1852 prog->watch_global_type = ev_void;
1853 prog->watch_field_type = ev_void;
1861 static void PRVM_LoadLNO( prvm_prog_t *prog, const char *progname ) {
1862 fs_offset_t filesize;
1864 unsigned int *header;
1867 FS_StripExtension( progname, filename, sizeof( filename ) );
1868 strlcat( filename, ".lno", sizeof( filename ) );
1870 lno = FS_LoadFile( filename, tempmempool, false, &filesize );
1876 <Spike> SafeWrite (h, &lnotype, sizeof(int));
1877 <Spike> SafeWrite (h, &version, sizeof(int));
1878 <Spike> SafeWrite (h, &numglobaldefs, sizeof(int));
1879 <Spike> SafeWrite (h, &numpr_globals, sizeof(int));
1880 <Spike> SafeWrite (h, &numfielddefs, sizeof(int));
1881 <Spike> SafeWrite (h, &numstatements, sizeof(int));
1882 <Spike> SafeWrite (h, statement_linenums, numstatements*sizeof(int));
1884 if ((unsigned int)filesize < (6 + prog->progs_numstatements) * sizeof(int))
1890 header = (unsigned int *) lno;
1891 if( header[ 0 ] == *(unsigned int *) "LNOF" &&
1892 LittleLong( header[ 1 ] ) == 1 &&
1893 (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs_numglobaldefs &&
1894 (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs_numglobals &&
1895 (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs_numfielddefs &&
1896 (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs_numstatements )
1898 prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof( int ) );
1899 memcpy( prog->statement_linenums, header + 6, prog->progs_numstatements * sizeof( int ) );
1901 /* gmqcc suports columnums */
1902 if ((unsigned int)filesize > ((6 + 2 * prog->progs_numstatements) * sizeof( int )))
1904 prog->statement_columnnums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof( int ) );
1905 memcpy( prog->statement_columnnums, header + 6 + prog->progs_numstatements, prog->progs_numstatements * sizeof( int ) );
1916 static void PRVM_UpdateBreakpoints(prvm_prog_t *prog);
1917 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)
1920 dprograms_t *dprograms;
1921 dstatement_t *instatements;
1922 ddef_t *infielddefs;
1923 ddef_t *inglobaldefs;
1925 dfunction_t *infunctions;
1927 fs_offset_t filesize;
1928 int requiredglobalspace;
1945 prog->error_cmd("PRVM_LoadProgs: there is already a %s program loaded!", prog->name );
1947 Host_LockSession(); // all progs can use the session cvar
1948 Crypto_LoadKeys(); // all progs might use the keys at init time
1952 dprograms = (dprograms_t *) data;
1956 dprograms = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
1957 if (dprograms == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
1958 prog->error_cmd("PRVM_LoadProgs: couldn't load %s for %s", filename, prog->name);
1959 // TODO bounds check header fields (e.g. numstatements), they must never go behind end of file
1961 prog->profiletime = Sys_DirtyTime();
1962 prog->starttime = realtime;
1964 Con_DPrintf("%s programs occupy %iK.\n", prog->name, (int)(filesize/1024));
1966 requiredglobalspace = 0;
1967 for (i = 0;i < numrequiredglobals;i++)
1968 requiredglobalspace += required_global[i].type == ev_vector ? 3 : 1;
1970 prog->filecrc = CRC_Block((unsigned char *)dprograms, filesize);
1972 // byte swap the header
1973 prog->progs_version = LittleLong(dprograms->version);
1974 prog->progs_crc = LittleLong(dprograms->crc);
1975 if (prog->progs_version != PROG_VERSION)
1976 prog->error_cmd("%s: %s has wrong version number (%i should be %i)", prog->name, filename, prog->progs_version, PROG_VERSION);
1977 instatements = (dstatement_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_statements));
1978 prog->progs_numstatements = LittleLong(dprograms->numstatements);
1979 inglobaldefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globaldefs));
1980 prog->progs_numglobaldefs = LittleLong(dprograms->numglobaldefs);
1981 infielddefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_fielddefs));
1982 prog->progs_numfielddefs = LittleLong(dprograms->numfielddefs);
1983 infunctions = (dfunction_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_functions));
1984 prog->progs_numfunctions = LittleLong(dprograms->numfunctions);
1985 instrings = (char *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_strings));
1986 prog->progs_numstrings = LittleLong(dprograms->numstrings);
1987 inglobals = (int *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globals));
1988 prog->progs_numglobals = LittleLong(dprograms->numglobals);
1989 prog->progs_entityfields = LittleLong(dprograms->entityfields);
1991 prog->numstatements = prog->progs_numstatements;
1992 prog->numglobaldefs = prog->progs_numglobaldefs;
1993 prog->numfielddefs = prog->progs_numfielddefs;
1994 prog->numfunctions = prog->progs_numfunctions;
1995 prog->numstrings = prog->progs_numstrings;
1996 prog->numglobals = prog->progs_numglobals;
1997 prog->entityfields = prog->progs_entityfields;
1999 if (LittleLong(dprograms->ofs_strings) + prog->progs_numstrings > (int)filesize)
2000 prog->error_cmd("%s: %s strings go past end of file", prog->name, filename);
2001 prog->strings = (char *)Mem_Alloc(prog->progs_mempool, prog->progs_numstrings);
2002 memcpy(prog->strings, instrings, prog->progs_numstrings);
2003 prog->stringssize = prog->progs_numstrings;
2005 prog->numknownstrings = 0;
2006 prog->maxknownstrings = 0;
2007 prog->knownstrings = NULL;
2008 prog->knownstrings_freeable = NULL;
2010 Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
2012 // we need to expand the globaldefs and fielddefs to include engine defs
2013 prog->globaldefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobaldefs + numrequiredglobals) * sizeof(ddef_t));
2014 prog->globals.fp = (prvm_vec_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobals + requiredglobalspace + 2) * sizeof(prvm_vec_t));
2015 // + 2 is because of an otherwise occurring overrun in RETURN instruction
2016 // when trying to return the last or second-last global
2017 // (RETURN always returns a vector, there is no RETURN_F instruction)
2018 prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numfielddefs + numrequiredfields) * sizeof(ddef_t));
2019 // we need to convert the statements to our memory format
2020 prog->statements = (mstatement_t *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(mstatement_t));
2021 // allocate space for profiling statement usage
2022 prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(*prog->statement_profile));
2023 prog->explicit_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(*prog->statement_profile));
2024 // functions need to be converted to the memory format
2025 prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs_numfunctions);
2027 for (i = 0;i < prog->progs_numfunctions;i++)
2029 prog->functions[i].first_statement = LittleLong(infunctions[i].first_statement);
2030 prog->functions[i].parm_start = LittleLong(infunctions[i].parm_start);
2031 prog->functions[i].s_name = LittleLong(infunctions[i].s_name);
2032 prog->functions[i].s_file = LittleLong(infunctions[i].s_file);
2033 prog->functions[i].numparms = LittleLong(infunctions[i].numparms);
2034 prog->functions[i].locals = LittleLong(infunctions[i].locals);
2035 memcpy(prog->functions[i].parm_size, infunctions[i].parm_size, sizeof(infunctions[i].parm_size));
2036 if(prog->functions[i].first_statement >= prog->numstatements)
2037 prog->error_cmd("PRVM_LoadProgs: out of bounds function statement (function %d) in %s", i, prog->name);
2038 // TODO bounds check parm_start, s_name, s_file, numparms, locals, parm_size
2041 // copy the globaldefs to the new globaldefs list
2042 for (i=0 ; i<prog->numglobaldefs ; i++)
2044 prog->globaldefs[i].type = LittleShort(inglobaldefs[i].type);
2045 prog->globaldefs[i].ofs = LittleShort(inglobaldefs[i].ofs);
2046 prog->globaldefs[i].s_name = LittleLong(inglobaldefs[i].s_name);
2047 // TODO bounds check ofs, s_name
2050 // append the required globals
2051 for (i = 0;i < numrequiredglobals;i++)
2053 prog->globaldefs[prog->numglobaldefs].type = required_global[i].type;
2054 prog->globaldefs[prog->numglobaldefs].ofs = prog->numglobals;
2055 prog->globaldefs[prog->numglobaldefs].s_name = PRVM_SetEngineString(prog, required_global[i].name);
2056 if (prog->globaldefs[prog->numglobaldefs].type == ev_vector)
2057 prog->numglobals += 3;
2060 prog->numglobaldefs++;
2063 // copy the progs fields to the new fields list
2064 for (i = 0;i < prog->numfielddefs;i++)
2066 prog->fielddefs[i].type = LittleShort(infielddefs[i].type);
2067 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
2068 prog->error_cmd("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", prog->name);
2069 prog->fielddefs[i].ofs = LittleShort(infielddefs[i].ofs);
2070 prog->fielddefs[i].s_name = LittleLong(infielddefs[i].s_name);
2071 // TODO bounds check ofs, s_name
2074 // append the required fields
2075 for (i = 0;i < numrequiredfields;i++)
2077 prog->fielddefs[prog->numfielddefs].type = required_field[i].type;
2078 prog->fielddefs[prog->numfielddefs].ofs = prog->entityfields;
2079 prog->fielddefs[prog->numfielddefs].s_name = PRVM_SetEngineString(prog, required_field[i].name);
2080 if (prog->fielddefs[prog->numfielddefs].type == ev_vector)
2081 prog->entityfields += 3;
2083 prog->entityfields++;
2084 prog->numfielddefs++;
2087 // LordHavoc: TODO: reorder globals to match engine struct
2088 // LordHavoc: TODO: reorder fields to match engine struct
2089 #define remapglobal(index) (index)
2090 #define remapfield(index) (index)
2093 // FIXME: LordHavoc: this uses a crude way to identify integer constants, rather than checking for matching globaldefs and checking their type
2094 for (i = 0;i < prog->progs_numglobals;i++)
2096 u.i = LittleLong(inglobals[i]);
2097 // most globals are 0, we only need to deal with the ones that are not
2100 d = u.i & 0xFF800000;
2101 if ((d == 0xFF800000) || (d == 0))
2103 // Looks like an integer (expand to int64)
2104 prog->globals.ip[remapglobal(i)] = u.i;
2108 // Looks like a float (expand to double)
2109 prog->globals.fp[remapglobal(i)] = u.f;
2114 // LordHavoc: TODO: support 32bit progs statement formats
2115 // copy, remap globals in statements, bounds check
2116 for (i = 0;i < prog->progs_numstatements;i++)
2118 op = (opcode_t)LittleShort(instatements[i].op);
2119 a = (unsigned short)LittleShort(instatements[i].a);
2120 b = (unsigned short)LittleShort(instatements[i].b);
2121 c = (unsigned short)LittleShort(instatements[i].c);
2127 if (a >= prog->progs_numglobals || b + i < 0 || b + i >= prog->progs_numstatements)
2128 prog->error_cmd("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", i, prog->name);
2129 prog->statements[i].op = op;
2130 prog->statements[i].operand[0] = remapglobal(a);
2131 prog->statements[i].operand[1] = -1;
2132 prog->statements[i].operand[2] = -1;
2133 prog->statements[i].jumpabsolute = i + b;
2137 if (a + i < 0 || a + i >= prog->progs_numstatements)
2138 prog->error_cmd("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, prog->name);
2139 prog->statements[i].op = op;
2140 prog->statements[i].operand[0] = -1;
2141 prog->statements[i].operand[1] = -1;
2142 prog->statements[i].operand[2] = -1;
2143 prog->statements[i].jumpabsolute = i + a;
2146 Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", (int)op, i, prog->name);
2148 // global global global
2183 if (a >= prog->progs_numglobals || b >= prog->progs_numglobals || c >= prog->progs_numglobals)
2184 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
2185 prog->statements[i].op = op;
2186 prog->statements[i].operand[0] = remapglobal(a);
2187 prog->statements[i].operand[1] = remapglobal(b);
2188 prog->statements[i].operand[2] = remapglobal(c);
2189 prog->statements[i].jumpabsolute = -1;
2191 // global none global
2197 if (a >= prog->progs_numglobals || c >= prog->progs_numglobals)
2198 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2199 prog->statements[i].op = op;
2200 prog->statements[i].operand[0] = remapglobal(a);
2201 prog->statements[i].operand[1] = -1;
2202 prog->statements[i].operand[2] = remapglobal(c);
2203 prog->statements[i].jumpabsolute = -1;
2219 if (a >= prog->progs_numglobals || b >= prog->progs_numglobals)
2220 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2221 prog->statements[i].op = op;
2222 prog->statements[i].operand[0] = remapglobal(a);
2223 prog->statements[i].operand[1] = remapglobal(b);
2224 prog->statements[i].operand[2] = -1;
2225 prog->statements[i].jumpabsolute = -1;
2229 if ( a < prog->progs_numglobals)
2230 if ( prog->globals.ip[remapglobal(a)] >= 0 )
2231 if ( prog->globals.ip[remapglobal(a)] < prog->progs_numfunctions )
2232 if ( prog->functions[prog->globals.ip[remapglobal(a)]].first_statement == -642 )
2233 ++prog->numexplicitcoveragestatements;
2244 if ( a >= prog->progs_numglobals)
2245 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2246 prog->statements[i].op = op;
2247 prog->statements[i].operand[0] = remapglobal(a);
2248 prog->statements[i].operand[1] = -1;
2249 prog->statements[i].operand[2] = -1;
2250 prog->statements[i].jumpabsolute = -1;
2254 if(prog->numstatements < 1)
2256 prog->error_cmd("PRVM_LoadProgs: empty program in %s", prog->name);
2258 else switch(prog->statements[prog->numstatements - 1].op)
2265 prog->error_cmd("PRVM_LoadProgs: program may fall off the edge (does not end with RETURN, GOTO or DONE) in %s", prog->name);
2269 // we're done with the file now
2271 Mem_Free(dprograms);
2274 // check required functions
2275 for(i=0 ; i < numrequiredfunc ; i++)
2276 if(PRVM_ED_FindFunction(prog, required_func[i]) == 0)
2277 prog->error_cmd("%s: %s not found in %s",prog->name, required_func[i], filename);
2279 PRVM_LoadLNO(prog, filename);
2281 PRVM_Init_Exec(prog);
2283 if(*prvm_language.string)
2284 // in CSQC we really shouldn't be able to change how stuff works... sorry for now
2285 // later idea: include a list of authorized .po file checksums with the csprogs
2287 qboolean deftrans = prog == CLVM_prog;
2288 const char *realfilename = (prog != CLVM_prog ? filename : csqc_progname.string);
2289 if(deftrans) // once we have dotranslate_ strings, ALWAYS use the opt-in method!
2291 for (i=0 ; i<prog->numglobaldefs ; i++)
2294 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2295 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2296 if(name && !strncmp(name, "dotranslate_", 12))
2303 if(!strcmp(prvm_language.string, "dump"))
2305 qfile_t *f = FS_OpenRealFile(va(vabuf, sizeof(vabuf), "%s.pot", realfilename), "w", false);
2306 Con_Printf("Dumping to %s.pot\n", realfilename);
2309 for (i=0 ; i<prog->numglobaldefs ; i++)
2312 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2313 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2314 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2316 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2317 const char *value = PRVM_GetString(prog, val->string);
2320 char buf[MAX_INPUTLINE];
2321 PRVM_PO_UnparseString(buf, value, sizeof(buf));
2322 FS_Printf(f, "msgid \"%s\"\nmsgstr \"\"\n\n", buf);
2331 po_t *po = PRVM_PO_Load(
2332 va(vabuf, sizeof(vabuf), "%s.%s.po", realfilename, prvm_language.string),
2333 va(vabuf2, sizeof(vabuf2), "common.%s.po", prvm_language.string),
2334 prog->progs_mempool);
2337 for (i=0 ; i<prog->numglobaldefs ; i++)
2340 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2341 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2342 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2344 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2345 const char *value = PRVM_GetString(prog, val->string);
2348 value = PRVM_PO_Lookup(po, value);
2350 val->string = PRVM_SetEngineString(prog, value);
2358 for (cvar = cvar_vars; cvar; cvar = cvar->next)
2359 cvar->globaldefindex[prog - prvm_prog_list] = -1;
2361 for (i=0 ; i<prog->numglobaldefs ; i++)
2364 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2365 //Con_Printf("found var %s\n", name);
2367 && !strncmp(name, "autocvar_", 9)
2368 && !(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
2371 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2372 cvar = Cvar_FindVar(name + 9);
2373 //Con_Printf("PRVM_LoadProgs: autocvar global %s in %s, processing...\n", name, prog->name);
2378 Con_DPrintf("PRVM_LoadProgs: no cvar for autocvar global %s in %s, creating...\n", name, prog->name);
2379 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2382 if((float)((int)(val->_float)) == val->_float)
2383 dpsnprintf(buf, sizeof(buf), "%i", (int)(val->_float));
2385 dpsnprintf(buf, sizeof(buf), "%.9g", val->_float);
2389 dpsnprintf(buf, sizeof(buf), "%.9g %.9g %.9g", val->vector[0], val->vector[1], val->vector[2]); value = buf;
2392 value = PRVM_GetString(prog, val->string);
2395 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2398 cvar = Cvar_Get(name + 9, value, 0, NULL);
2399 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2401 val->string = PRVM_SetEngineString(prog, cvar->string);
2402 cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2405 prog->error_cmd("PRVM_LoadProgs: could not create cvar for autocvar global %s in %s", name, prog->name);
2406 cvar->globaldefindex[prog - prvm_prog_list] = i;
2408 else if((cvar->flags & CVAR_PRIVATE) == 0)
2410 // MUST BE SYNCED WITH cvar.c Cvar_Set
2413 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2416 val->_float = cvar->value;
2420 VectorClear(val->vector);
2421 for (j = 0;j < 3;j++)
2423 while (*s && ISWHITESPACE(*s))
2427 val->vector[j] = atof(s);
2428 while (!ISWHITESPACE(*s))
2435 val->string = PRVM_SetEngineString(prog, cvar->string);
2436 cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2439 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2442 cvar->globaldefindex[prog - prvm_prog_list] = i;
2445 Con_Printf("PRVM_LoadProgs: private cvar for autocvar global %s in %s\n", name, prog->name);
2451 prog->loaded = TRUE;
2453 PRVM_UpdateBreakpoints(prog);
2455 // set flags & ddef_ts in prog
2459 PRVM_FindOffsets(prog);
2461 prog->init_cmd(prog);
2464 PRVM_MEM_Alloc(prog);
2466 // Inittime is at least the time when this function finished. However,
2467 // later events may bump it.
2468 prog->inittime = realtime;
2472 static void PRVM_Fields_f (void)
2475 int i, j, ednum, used, usedamount;
2477 char tempstring[MAX_INPUTLINE], tempstring2[260];
2487 Con_Print("no progs loaded\n");
2494 Con_Print("prvm_fields <program name>\n");
2498 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2501 counts = (int *)Mem_Alloc(tempmempool, prog->numfielddefs * sizeof(int));
2502 for (ednum = 0;ednum < prog->max_edicts;ednum++)
2504 ed = PRVM_EDICT_NUM(ednum);
2505 if (ed->priv.required->free)
2507 for (i = 1;i < prog->numfielddefs;i++)
2509 d = &prog->fielddefs[i];
2510 name = PRVM_GetString(prog, d->s_name);
2511 if (name[strlen(name)-2] == '_')
2512 continue; // skip _x, _y, _z vars
2513 val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
2514 // if the value is still all 0, skip the field
2515 for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
2517 if (val->ivector[j])
2528 for (i = 0;i < prog->numfielddefs;i++)
2530 d = &prog->fielddefs[i];
2531 name = PRVM_GetString(prog, d->s_name);
2532 if (name[strlen(name)-2] == '_')
2533 continue; // skip _x, _y, _z vars
2534 switch(d->type & ~DEF_SAVEGLOBAL)
2537 strlcat(tempstring, "string ", sizeof(tempstring));
2540 strlcat(tempstring, "entity ", sizeof(tempstring));
2543 strlcat(tempstring, "function ", sizeof(tempstring));
2546 strlcat(tempstring, "field ", sizeof(tempstring));
2549 strlcat(tempstring, "void ", sizeof(tempstring));
2552 strlcat(tempstring, "float ", sizeof(tempstring));
2555 strlcat(tempstring, "vector ", sizeof(tempstring));
2558 strlcat(tempstring, "pointer ", sizeof(tempstring));
2561 dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
2562 strlcat(tempstring, tempstring2, sizeof(tempstring));
2565 if (strlen(name) > sizeof(tempstring2)-4)
2567 memcpy (tempstring2, name, sizeof(tempstring2)-4);
2568 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
2569 tempstring2[sizeof(tempstring2)-1] = 0;
2572 strlcat(tempstring, name, sizeof(tempstring));
2573 for (j = (int)strlen(name);j < 25;j++)
2574 strlcat(tempstring, " ", sizeof(tempstring));
2575 dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
2576 strlcat(tempstring, tempstring2, sizeof(tempstring));
2577 strlcat(tempstring, "\n", sizeof(tempstring));
2578 if (strlen(tempstring) >= sizeof(tempstring)/2)
2580 Con_Print(tempstring);
2586 usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
2590 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);
2593 static void PRVM_Globals_f (void)
2597 const char *wildcard;
2603 Con_Print("no progs loaded\n");
2606 if(Cmd_Argc () < 2 || Cmd_Argc() > 3)
2608 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
2612 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2615 if( Cmd_Argc() == 3)
2616 wildcard = Cmd_Argv(2);
2620 Con_Printf("%s :", prog->name);
2622 for (i = 0;i < prog->numglobaldefs;i++)
2625 if( !matchpattern( PRVM_GetString(prog, prog->globaldefs[i].s_name), wildcard, 1) )
2630 Con_Printf("%s\n", PRVM_GetString(prog, prog->globaldefs[i].s_name));
2632 Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->numglobals, numculled, prog->numglobals * 4);
2640 static void PRVM_Global_f(void)
2644 char valuebuf[MAX_INPUTLINE];
2645 if( Cmd_Argc() != 3 ) {
2646 Con_Printf( "prvm_global <program name> <global name>\n" );
2650 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2653 global = PRVM_ED_FindGlobal( prog, Cmd_Argv(2) );
2655 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2657 Con_Printf( "%s: %s\n", Cmd_Argv(2), PRVM_ValueString( prog, (etype_t)global->type, PRVM_GLOBALFIELDVALUE(global->ofs), valuebuf, sizeof(valuebuf) ) );
2665 static void PRVM_GlobalSet_f(void)
2669 if( Cmd_Argc() != 4 ) {
2670 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2674 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2677 global = PRVM_ED_FindGlobal( prog, Cmd_Argv(2) );
2679 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2681 PRVM_ED_ParseEpair( prog, NULL, global, Cmd_Argv(3), true );
2685 ======================
2686 Break- and Watchpoints
2687 ======================
2691 char break_statement[256];
2692 char watch_global[256];
2694 char watch_field[256];
2697 static debug_data_t debug_data[PRVM_PROG_MAX];
2699 void PRVM_Breakpoint(prvm_prog_t *prog, int stack_index, const char *text)
2702 Con_Printf("PRVM_Breakpoint: %s\n", text);
2703 PRVM_PrintState(prog, stack_index);
2704 if (prvm_breakpointdump.integer)
2705 Host_Savegame_to(prog, va(vabuf, sizeof(vabuf), "breakpoint-%s.dmp", prog->name));
2708 void PRVM_Watchpoint(prvm_prog_t *prog, int stack_index, const char *text, etype_t type, prvm_eval_t *o, prvm_eval_t *n)
2710 size_t sz = sizeof(prvm_vec_t) * ((type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2711 if (memcmp(o, n, sz))
2714 char valuebuf_o[128];
2715 char valuebuf_n[128];
2716 PRVM_UglyValueString(prog, type, o, valuebuf_o, sizeof(valuebuf_o));
2717 PRVM_UglyValueString(prog, type, n, valuebuf_n, sizeof(valuebuf_n));
2718 dpsnprintf(buf, sizeof(buf), "%s: %s -> %s", text, valuebuf_o, valuebuf_n);
2719 PRVM_Breakpoint(prog, stack_index, buf);
2724 static void PRVM_UpdateBreakpoints(prvm_prog_t *prog)
2726 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2729 if (debug->break_statement[0])
2731 if (debug->break_statement[0] >= '0' && debug->break_statement[0] <= '9')
2733 prog->break_statement = atoi(debug->break_statement);
2734 prog->break_stack_index = 0;
2739 func = PRVM_ED_FindFunction (prog, debug->break_statement);
2742 Con_Printf("%s progs: no function or statement named %s to break on!\n", prog->name, debug->break_statement);
2743 prog->break_statement = -1;
2747 prog->break_statement = func->first_statement;
2748 prog->break_stack_index = 1;
2751 if (prog->break_statement >= -1)
2752 Con_Printf("%s progs: breakpoint is at statement %d\n", prog->name, prog->break_statement);
2755 prog->break_statement = -1;
2757 if (debug->watch_global[0])
2759 ddef_t *global = PRVM_ED_FindGlobal( prog, debug->watch_global );
2762 Con_Printf( "%s progs: no global named '%s' to watch!\n", prog->name, debug->watch_global );
2763 prog->watch_global_type = ev_void;
2767 size_t sz = sizeof(prvm_vec_t) * ((global->type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2768 prog->watch_global = global->ofs;
2769 prog->watch_global_type = (etype_t)global->type;
2770 memcpy(&prog->watch_global_value, PRVM_GLOBALFIELDVALUE(prog->watch_global), sz);
2772 if (prog->watch_global_type != ev_void)
2773 Con_Printf("%s progs: global watchpoint is at global index %d\n", prog->name, prog->watch_global);
2776 prog->watch_global_type = ev_void;
2778 if (debug->watch_field[0])
2780 ddef_t *field = PRVM_ED_FindField( prog, debug->watch_field );
2783 Con_Printf( "%s progs: no field named '%s' to watch!\n", prog->name, debug->watch_field );
2784 prog->watch_field_type = ev_void;
2788 size_t sz = sizeof(prvm_vec_t) * ((field->type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2789 prog->watch_edict = debug->watch_edict;
2790 prog->watch_field = field->ofs;
2791 prog->watch_field_type = (etype_t)field->type;
2792 if (prog->watch_edict < prog->num_edicts)
2793 memcpy(&prog->watch_edictfield_value, PRVM_EDICTFIELDVALUE(PRVM_EDICT_NUM(prog->watch_edict), prog->watch_field), sz);
2795 memset(&prog->watch_edictfield_value, 0, sz);
2797 if (prog->watch_edict != ev_void)
2798 Con_Printf("%s progs: edict field watchpoint is at edict %d field index %d\n", prog->name, prog->watch_edict, prog->watch_field);
2801 prog->watch_field_type = ev_void;
2804 static void PRVM_Breakpoint_f(void)
2808 if( Cmd_Argc() == 2 ) {
2809 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2812 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2813 debug->break_statement[0] = 0;
2815 PRVM_UpdateBreakpoints(prog);
2818 if( Cmd_Argc() != 3 ) {
2819 Con_Printf( "prvm_breakpoint <program name> <function name | statement>\n" );
2823 if (!(prog = PRVM_ProgFromString(Cmd_Argv(1))))
2827 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2828 strlcpy(debug->break_statement, Cmd_Argv(2), sizeof(debug->break_statement));
2830 PRVM_UpdateBreakpoints(prog);
2833 static void PRVM_GlobalWatchpoint_f(void)
2837 if( Cmd_Argc() == 2 ) {
2838 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2841 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2842 debug->watch_global[0] = 0;
2844 PRVM_UpdateBreakpoints(prog);
2847 if( Cmd_Argc() != 3 ) {
2848 Con_Printf( "prvm_globalwatchpoint <program name> <global name>\n" );
2852 if (!(prog = PRVM_ProgFromString(Cmd_Argv(1))))
2856 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2857 strlcpy(debug->watch_global, Cmd_Argv(2), sizeof(debug->watch_global));
2859 PRVM_UpdateBreakpoints(prog);
2862 static void PRVM_EdictWatchpoint_f(void)
2866 if( Cmd_Argc() == 2 ) {
2867 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2870 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2871 debug->watch_field[0] = 0;
2873 PRVM_UpdateBreakpoints(prog);
2876 if( Cmd_Argc() != 4 ) {
2877 Con_Printf( "prvm_edictwatchpoint <program name> <edict number> <field name>\n" );
2881 if (!(prog = PRVM_ProgFromString(Cmd_Argv(1))))
2885 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2886 debug->watch_edict = atoi(Cmd_Argv(2));
2887 strlcpy(debug->watch_field, Cmd_Argv(3), sizeof(debug->watch_field));
2889 PRVM_UpdateBreakpoints(prog);
2897 void PRVM_Init (void)
2899 Cmd_AddCommand ("prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2900 Cmd_AddCommand ("prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2901 Cmd_AddCommand ("prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2902 Cmd_AddCommand ("prvm_profile", PRVM_Profile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu)");
2903 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");
2904 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)");
2905 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)");
2906 Cmd_AddCommand ("prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2907 Cmd_AddCommand ("prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2908 Cmd_AddCommand ("prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2909 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)");
2910 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");
2911 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");
2912 Cmd_AddCommand ("prvm_printfunction", PRVM_PrintFunction_f, "prints a disassembly (QuakeC instructions) of the specified function in the selected VM (server, client, menu)");
2913 Cmd_AddCommand ("cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2914 Cmd_AddCommand ("menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2915 Cmd_AddCommand ("sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2917 Cmd_AddCommand ("prvm_breakpoint", PRVM_Breakpoint_f, "marks a statement or function as breakpoint (when this is executed, a stack trace is printed); to actually halt and investigate state, combine this with a gdb breakpoint on PRVM_Breakpoint, or with prvm_breakpointdump; run with just progs name to clear breakpoint");
2918 Cmd_AddCommand ("prvm_globalwatchpoint", PRVM_GlobalWatchpoint_f, "marks a global as watchpoint (when this is executed, a stack trace is printed); to actually halt and investigate state, combine this with a gdb breakpoint on PRVM_Breakpoint, or with prvm_breakpointdump; run with just progs name to clear watchpoint");
2919 Cmd_AddCommand ("prvm_edictwatchpoint", PRVM_EdictWatchpoint_f, "marks an entity field as watchpoint (when this is executed, a stack trace is printed); to actually halt and investigate state, combine this with a gdb breakpoint on PRVM_Breakpoint, or with prvm_breakpointdump; run with just progs name to clear watchpoint");
2921 Cvar_RegisterVariable (&prvm_language);
2922 Cvar_RegisterVariable (&prvm_traceqc);
2923 Cvar_RegisterVariable (&prvm_statementprofiling);
2924 Cvar_RegisterVariable (&prvm_timeprofiling);
2925 Cvar_RegisterVariable (&prvm_coverage);
2926 Cvar_RegisterVariable (&prvm_backtraceforwarnings);
2927 Cvar_RegisterVariable (&prvm_leaktest);
2928 Cvar_RegisterVariable (&prvm_leaktest_follow_targetname);
2929 Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames);
2930 Cvar_RegisterVariable (&prvm_errordump);
2931 Cvar_RegisterVariable (&prvm_breakpointdump);
2932 Cvar_RegisterVariable (&prvm_reuseedicts_startuptime);
2933 Cvar_RegisterVariable (&prvm_reuseedicts_neverinsameframe);
2935 // COMMANDLINEOPTION: PRVM: -norunaway disables the runaway loop check (it might be impossible to exit DarkPlaces if used!)
2936 prvm_runawaycheck = !COM_CheckParm("-norunaway");
2946 void PRVM_Prog_Init(prvm_prog_t *prog)
2948 PRVM_Prog_Reset(prog);
2949 prog->leaktest_active = prvm_leaktest.integer != 0;
2952 // LordHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
2953 unsigned int PRVM_EDICT_NUM_ERROR(prvm_prog_t *prog, unsigned int n, const char *filename, int fileline)
2955 prog->error_cmd("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", prog->name, n, filename, fileline);
2959 #define PRVM_KNOWNSTRINGBASE 0x40000000
2961 const char *PRVM_GetString(prvm_prog_t *prog, int num)
2966 VM_Warning(prog, "PRVM_GetString: Invalid string offset (%i < 0)\n", num);
2969 else if (num < prog->stringssize)
2971 // constant string from progs.dat
2972 return prog->strings + num;
2974 else if (num <= prog->stringssize + prog->tempstringsbuf.maxsize)
2976 // tempstring returned by engine to QC (becomes invalid after returning to engine)
2977 num -= prog->stringssize;
2978 if (num < prog->tempstringsbuf.cursize)
2979 return (char *)prog->tempstringsbuf.data + num;
2982 VM_Warning(prog, "PRVM_GetString: Invalid temp-string offset (%i >= %i prog->tempstringsbuf.cursize)\n", num, prog->tempstringsbuf.cursize);
2986 else if (num & PRVM_KNOWNSTRINGBASE)
2989 num = num - PRVM_KNOWNSTRINGBASE;
2990 if (num >= 0 && num < prog->numknownstrings)
2992 if (!prog->knownstrings[num])
2994 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
2997 return prog->knownstrings[num];
3001 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
3007 // invalid string offset
3008 VM_Warning(prog, "PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
3013 const char *PRVM_ChangeEngineString(prvm_prog_t *prog, int i, const char *s)
3016 i = i - PRVM_KNOWNSTRINGBASE;
3017 if(i < 0 || i >= prog->numknownstrings)
3018 prog->error_cmd("PRVM_ChangeEngineString: s is not an engine string");
3019 old = prog->knownstrings[i];
3020 prog->knownstrings[i] = s;
3024 int PRVM_SetEngineString(prvm_prog_t *prog, const char *s)
3029 if (s >= prog->strings && s <= prog->strings + prog->stringssize)
3030 prog->error_cmd("PRVM_SetEngineString: s in prog->strings area");
3031 // if it's in the tempstrings area, use a reserved range
3032 // (otherwise we'd get millions of useless string offsets cluttering the database)
3033 if (s >= (char *)prog->tempstringsbuf.data && s < (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.maxsize)
3034 return prog->stringssize + (s - (char *)prog->tempstringsbuf.data);
3035 // see if it's a known string address
3036 for (i = 0;i < prog->numknownstrings;i++)
3037 if (prog->knownstrings[i] == s)
3038 return PRVM_KNOWNSTRINGBASE + i;
3039 // new unknown engine string
3040 if (developer_insane.integer)
3041 Con_DPrintf("new engine string %p = \"%s\"\n", s, s);
3042 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3043 if (!prog->knownstrings[i])
3045 if (i >= prog->numknownstrings)
3047 if (i >= prog->maxknownstrings)
3049 const char **oldstrings = prog->knownstrings;
3050 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
3051 const char **oldstrings_origin = prog->knownstrings_origin;
3052 prog->maxknownstrings += 128;
3053 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3054 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
3055 if(prog->leaktest_active)
3056 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3057 if (prog->numknownstrings)
3059 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
3060 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
3061 if(prog->leaktest_active)
3062 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
3065 prog->numknownstrings++;
3067 prog->firstfreeknownstring = i + 1;
3068 prog->knownstrings[i] = s;
3069 prog->knownstrings_freeable[i] = false;
3070 if(prog->leaktest_active)
3071 prog->knownstrings_origin[i] = NULL;
3072 return PRVM_KNOWNSTRINGBASE + i;
3075 // temp string handling
3077 // all tempstrings go into this buffer consecutively, and it is reset
3078 // whenever PRVM_ExecuteProgram returns to the engine
3079 // (technically each PRVM_ExecuteProgram call saves the cursize value and
3080 // restores it on return, so multiple recursive calls can share the same
3082 // the buffer size is automatically grown as needed
3084 int PRVM_SetTempString(prvm_prog_t *prog, const char *s)
3090 size = (int)strlen(s) + 1;
3091 if (developer_insane.integer)
3092 Con_DPrintf("PRVM_SetTempString: cursize %i, size %i\n", prog->tempstringsbuf.cursize, size);
3093 if (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
3095 sizebuf_t old = prog->tempstringsbuf;
3096 if (prog->tempstringsbuf.cursize + size >= 1<<28)
3097 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);
3098 prog->tempstringsbuf.maxsize = max(prog->tempstringsbuf.maxsize, 65536);
3099 while (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
3100 prog->tempstringsbuf.maxsize *= 2;
3101 if (prog->tempstringsbuf.maxsize != old.maxsize || prog->tempstringsbuf.data == NULL)
3103 Con_DPrintf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, prog->tempstringsbuf.maxsize/1024);
3104 prog->tempstringsbuf.data = (unsigned char *) Mem_Alloc(prog->progs_mempool, prog->tempstringsbuf.maxsize);
3108 memcpy(prog->tempstringsbuf.data, old.data, old.cursize);
3113 t = (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.cursize;
3115 prog->tempstringsbuf.cursize += size;
3116 return PRVM_SetEngineString(prog, t);
3119 int PRVM_AllocString(prvm_prog_t *prog, size_t bufferlength, char **pointer)
3128 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3129 if (!prog->knownstrings[i])
3131 if (i >= prog->numknownstrings)
3133 if (i >= prog->maxknownstrings)
3135 const char **oldstrings = prog->knownstrings;
3136 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
3137 const char **oldstrings_origin = prog->knownstrings_origin;
3138 prog->maxknownstrings += 128;
3139 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3140 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
3141 if(prog->leaktest_active)
3142 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3143 if (prog->numknownstrings)
3145 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
3146 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
3147 if(prog->leaktest_active)
3148 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
3151 Mem_Free((char **)oldstrings);
3152 if (oldstrings_freeable)
3153 Mem_Free((unsigned char *)oldstrings_freeable);
3154 if (oldstrings_origin)
3155 Mem_Free((char **)oldstrings_origin);
3157 prog->numknownstrings++;
3159 prog->firstfreeknownstring = i + 1;
3160 prog->knownstrings[i] = (char *)PRVM_Alloc(bufferlength);
3161 prog->knownstrings_freeable[i] = true;
3162 if(prog->leaktest_active)
3163 prog->knownstrings_origin[i] = PRVM_AllocationOrigin(prog);
3165 *pointer = (char *)(prog->knownstrings[i]);
3166 return PRVM_KNOWNSTRINGBASE + i;
3169 void PRVM_FreeString(prvm_prog_t *prog, int num)
3172 prog->error_cmd("PRVM_FreeString: attempt to free a NULL string");
3173 else if (num >= 0 && num < prog->stringssize)
3174 prog->error_cmd("PRVM_FreeString: attempt to free a constant string");
3175 else if (num >= PRVM_KNOWNSTRINGBASE && num < PRVM_KNOWNSTRINGBASE + prog->numknownstrings)
3177 num = num - PRVM_KNOWNSTRINGBASE;
3178 if (!prog->knownstrings[num])
3179 prog->error_cmd("PRVM_FreeString: attempt to free a non-existent or already freed string");
3180 if (!prog->knownstrings_freeable[num])
3181 prog->error_cmd("PRVM_FreeString: attempt to free a string owned by the engine");
3182 PRVM_Free((char *)prog->knownstrings[num]);
3183 if(prog->leaktest_active)
3184 if(prog->knownstrings_origin[num])
3185 PRVM_Free((char *)prog->knownstrings_origin[num]);
3186 prog->knownstrings[num] = NULL;
3187 prog->knownstrings_freeable[num] = false;
3188 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
3191 prog->error_cmd("PRVM_FreeString: invalid string offset %i", num);
3194 static qboolean PRVM_IsStringReferenced(prvm_prog_t *prog, string_t string)
3198 for (i = 0;i < prog->numglobaldefs;i++)
3200 ddef_t *d = &prog->globaldefs[i];
3201 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3203 if(string == PRVM_GLOBALFIELDSTRING(d->ofs))
3207 for(j = 0; j < prog->num_edicts; ++j)
3209 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3210 if (ed->priv.required->free)
3212 for (i=0; i<prog->numfielddefs; ++i)
3214 ddef_t *d = &prog->fielddefs[i];
3215 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3217 if(string == PRVM_EDICTFIELDSTRING(ed, d->ofs))
3225 static qboolean PRVM_IsEdictRelevant(prvm_prog_t *prog, prvm_edict_t *edict)
3229 if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
3230 return true; // world or clients
3231 if (edict->priv.required->freetime <= prog->inittime)
3232 return true; // created during startup
3233 if (prog == SVVM_prog)
3235 if(PRVM_serveredictfloat(edict, solid)) // can block other stuff, or is a trigger?
3237 if(PRVM_serveredictfloat(edict, modelindex)) // visible ent?
3239 if(PRVM_serveredictfloat(edict, effects)) // particle effect?
3241 if(PRVM_serveredictfunction(edict, think)) // has a think function?
3242 if(PRVM_serveredictfloat(edict, nextthink) > 0) // that actually will eventually run?
3244 if(PRVM_serveredictfloat(edict, takedamage))
3246 if(*prvm_leaktest_ignore_classnames.string)
3248 if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_serveredictstring(edict, classname)))))
3252 else if (prog == CLVM_prog)
3254 // TODO someone add more stuff here
3255 if(PRVM_clientedictfloat(edict, entnum)) // csqc networked
3257 if(PRVM_clientedictfloat(edict, modelindex)) // visible ent?
3259 if(PRVM_clientedictfloat(edict, effects)) // particle effect?
3261 if(PRVM_clientedictfunction(edict, think)) // has a think function?
3262 if(PRVM_clientedictfloat(edict, nextthink) > 0) // that actually will eventually run?
3264 if(*prvm_leaktest_ignore_classnames.string)
3266 if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_clientedictstring(edict, classname)))))
3272 // menu prog does not have classnames
3277 static qboolean PRVM_IsEdictReferenced(prvm_prog_t *prog, prvm_edict_t *edict, int mark)
3280 int edictnum = PRVM_NUM_FOR_EDICT(edict);
3281 const char *targetname = NULL;
3283 if (prog == SVVM_prog && prvm_leaktest_follow_targetname.integer)
3284 targetname = PRVM_GetString(prog, PRVM_serveredictstring(edict, targetname));
3287 if(!*targetname) // ""
3290 for(j = 0; j < prog->num_edicts; ++j)
3292 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3293 if (ed->priv.required->mark < mark)
3299 const char *target = PRVM_GetString(prog, PRVM_serveredictstring(ed, target));
3301 if(!strcmp(target, targetname))
3304 for (i=0; i<prog->numfielddefs; ++i)
3306 ddef_t *d = &prog->fielddefs[i];
3307 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3309 if(edictnum == PRVM_EDICTFIELDEDICT(ed, d->ofs))
3317 static void PRVM_MarkReferencedEdicts(prvm_prog_t *prog)
3323 // Stage 1: world, all entities that are relevant, and all entities that are referenced by globals.
3325 for(j = 0; j < prog->num_edicts; ++j)
3327 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3328 if(ed->priv.required->free)
3330 ed->priv.required->mark = PRVM_IsEdictRelevant(prog, ed) ? stage : 0;
3332 for (i = 0;i < prog->numglobaldefs;i++)
3334 ddef_t *d = &prog->globaldefs[i];
3336 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3338 j = PRVM_GLOBALFIELDEDICT(d->ofs);
3339 if (i < 0 || j >= prog->max_edicts) {
3340 Con_Printf("Invalid entity reference from global %s.\n", PRVM_GetString(prog, d->s_name));
3343 ed = PRVM_EDICT_NUM(j);;
3344 ed->priv.required->mark = stage;
3347 // Future stages: all entities that are referenced by an entity of the previous stage.
3351 for(j = 0; j < prog->num_edicts; ++j)
3353 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3354 if(ed->priv.required->free)
3356 if(ed->priv.required->mark)
3358 if(PRVM_IsEdictReferenced(prog, ed, stage))
3360 ed->priv.required->mark = stage + 1;
3367 Con_DPrintf("leak check used %d stages to find all references\n", stage);
3370 void PRVM_LeakTest(prvm_prog_t *prog)
3373 qboolean leaked = false;
3375 if(!prog->leaktest_active)
3379 for (i = 0; i < prog->numknownstrings; ++i)
3381 if(prog->knownstrings[i])
3382 if(prog->knownstrings_freeable[i])
3383 if(prog->knownstrings_origin[i])
3384 if(!PRVM_IsStringReferenced(prog, PRVM_KNOWNSTRINGBASE + i))
3386 Con_Printf("Unreferenced string found!\n Value: %s\n Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
3392 PRVM_MarkReferencedEdicts(prog);
3393 for(j = 0; j < prog->num_edicts; ++j)
3395 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3396 if(ed->priv.required->free)
3398 if(!ed->priv.required->mark)
3399 if(ed->priv.required->allocation_origin)
3401 Con_Printf("Unreferenced edict found!\n Allocated at: %s\n", ed->priv.required->allocation_origin);
3402 PRVM_ED_Print(prog, ed, NULL);
3407 ed->priv.required->mark = 0; // clear marks again when done
3410 for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
3412 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
3414 if(stringbuffer->origin)
3416 Con_Printf("Open string buffer handle found!\n Allocated at: %s\n", stringbuffer->origin);
3421 for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
3423 if(prog->openfiles[i])
3424 if(prog->openfiles_origin[i])
3426 Con_Printf("Open file handle found!\n Allocated at: %s\n", prog->openfiles_origin[i]);
3431 for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
3433 if(prog->opensearches[i])
3434 if(prog->opensearches_origin[i])
3436 Con_Printf("Open search handle found!\n Allocated at: %s\n", prog->opensearches_origin[i]);
3442 Con_Printf("Congratulations. No leaks found.\n");