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;)
524 dpsnprintf (line, linelength, "%i", val->edict);
527 f = prog->functions + val->function;
528 strlcpy (line, PRVM_GetString (prog, f->s_name), linelength);
531 def = PRVM_ED_FieldAtOfs ( prog, val->_int );
532 dpsnprintf (line, linelength, ".%s", PRVM_GetString(prog, def->s_name));
535 dpsnprintf (line, linelength, "void");
538 dpsnprintf (line, linelength, FLOAT_LOSSLESS_FORMAT, val->_float);
541 dpsnprintf (line, linelength, VECTOR_LOSSLESS_FORMAT, val->vector[0], val->vector[1], val->vector[2]);
544 dpsnprintf (line, linelength, "bad type %i", type);
555 Returns a string with a description and the contents of a global,
556 padded to 20 field width
559 char *PRVM_GlobalString (prvm_prog_t *prog, int ofs, char *line, size_t linelength)
565 char valuebuf[MAX_INPUTLINE];
567 val = (prvm_eval_t *)&prog->globals.fp[ofs];
568 def = PRVM_ED_GlobalAtOfs(prog, ofs);
570 dpsnprintf (line, linelength, "GLOBAL%i", ofs);
573 s = PRVM_ValueString (prog, (etype_t)def->type, val, valuebuf, sizeof(valuebuf));
574 dpsnprintf (line, linelength, "%s (=%s)", PRVM_GetString(prog, def->s_name), s);
578 //for ( ; i<20 ; i++)
579 // strcat (line," ");
585 char *PRVM_GlobalStringNoContents (prvm_prog_t *prog, int ofs, char *line, size_t linelength)
590 def = PRVM_ED_GlobalAtOfs(prog, ofs);
592 dpsnprintf (line, linelength, "GLOBAL%i", ofs);
594 dpsnprintf (line, linelength, "%s", PRVM_GetString(prog, def->s_name));
597 //for ( ; i<20 ; i++)
598 // strcat (line," ");
612 // LordHavoc: optimized this to print out much more quickly (tempstring)
613 // LordHavoc: changed to print out every 4096 characters (incase there are a lot of fields to print)
614 void PRVM_ED_Print(prvm_prog_t *prog, prvm_edict_t *ed, const char *wildcard_fieldname)
622 char tempstring[MAX_INPUTLINE], tempstring2[260]; // temporary string buffers
623 char valuebuf[MAX_INPUTLINE];
625 if (ed->priv.required->free)
627 Con_Printf("%s: FREE\n",prog->name);
632 dpsnprintf(tempstring, sizeof(tempstring), "\n%s EDICT %i:\n", prog->name, PRVM_NUM_FOR_EDICT(ed));
633 for (i = 1;i < prog->numfielddefs;i++)
635 d = &prog->fielddefs[i];
636 name = PRVM_GetString(prog, d->s_name);
637 if(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
638 continue; // skip _x, _y, _z vars
640 // Check Field Name Wildcard
641 if(wildcard_fieldname)
642 if( !matchpattern(name, wildcard_fieldname, 1) )
643 // Didn't match; skip
646 val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
648 // if the value is still all 0, skip the field
649 type = d->type & ~DEF_SAVEGLOBAL;
651 for (j=0 ; j<prvm_type_size[type] ; j++)
654 if (j == prvm_type_size[type])
657 if (strlen(name) > sizeof(tempstring2)-4)
659 memcpy (tempstring2, name, sizeof(tempstring2)-4);
660 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
661 tempstring2[sizeof(tempstring2)-1] = 0;
664 strlcat(tempstring, name, sizeof(tempstring));
665 for (l = strlen(name);l < 14;l++)
666 strlcat(tempstring, " ", sizeof(tempstring));
667 strlcat(tempstring, " ", sizeof(tempstring));
669 name = PRVM_ValueString(prog, (etype_t)d->type, val, valuebuf, sizeof(valuebuf));
670 if (strlen(name) > sizeof(tempstring2)-4)
672 memcpy (tempstring2, name, sizeof(tempstring2)-4);
673 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
674 tempstring2[sizeof(tempstring2)-1] = 0;
677 strlcat(tempstring, name, sizeof(tempstring));
678 strlcat(tempstring, "\n", sizeof(tempstring));
679 if (strlen(tempstring) >= sizeof(tempstring)/2)
681 Con_Print(tempstring);
686 Con_Print(tempstring);
696 extern cvar_t developer_entityparsing;
697 void PRVM_ED_Write (prvm_prog_t *prog, qfile_t *f, prvm_edict_t *ed)
705 char valuebuf[MAX_INPUTLINE];
709 if (ed->priv.required->free)
715 for (i = 1;i < prog->numfielddefs;i++)
717 d = &prog->fielddefs[i];
718 name = PRVM_GetString(prog, d->s_name);
720 if(developer_entityparsing.integer)
721 Con_Printf("PRVM_ED_Write: at entity %d field %s\n", PRVM_NUM_FOR_EDICT(ed), name);
723 //if(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
724 if(strlen(name) > 1 && name[strlen(name)-2] == '_')
725 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?)
727 val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
729 // if the value is still all 0, skip the field
730 type = d->type & ~DEF_SAVEGLOBAL;
731 for (j=0 ; j<prvm_type_size[type] ; j++)
734 if (j == prvm_type_size[type])
737 FS_Printf(f,"\"%s\" ",name);
738 prog->statestring = va(vabuf, sizeof(vabuf), "PRVM_ED_Write, ent=%d, name=%s", i, name);
739 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString(prog, (etype_t)d->type, val, valuebuf, sizeof(valuebuf)));
740 prog->statestring = NULL;
746 void PRVM_ED_PrintNum (prvm_prog_t *prog, int ent, const char *wildcard_fieldname)
748 PRVM_ED_Print(prog, PRVM_EDICT_NUM(ent), wildcard_fieldname);
753 PRVM_ED_PrintEdicts_f
755 For debugging, prints all the entities in the current server
758 void PRVM_ED_PrintEdicts_f (void)
762 const char *wildcard_fieldname;
764 if(Cmd_Argc() < 2 || Cmd_Argc() > 3)
766 Con_Print("prvm_edicts <program name> <optional field name wildcard>\n");
770 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
774 wildcard_fieldname = Cmd_Argv(2);
776 wildcard_fieldname = NULL;
778 Con_Printf("%s: %i entities\n", prog->name, prog->num_edicts);
779 for (i=0 ; i<prog->num_edicts ; i++)
780 PRVM_ED_PrintNum (prog, i, wildcard_fieldname);
787 For debugging, prints a single edict
790 static void PRVM_ED_PrintEdict_f (void)
794 const char *wildcard_fieldname;
796 if(Cmd_Argc() < 3 || Cmd_Argc() > 4)
798 Con_Print("prvm_edict <program name> <edict number> <optional field name wildcard>\n");
802 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
805 i = atoi (Cmd_Argv(2));
806 if (i >= prog->num_edicts)
808 Con_Print("Bad edict number\n");
812 // Optional Wildcard Provided
813 wildcard_fieldname = Cmd_Argv(3);
816 wildcard_fieldname = NULL;
817 PRVM_ED_PrintNum (prog, i, wildcard_fieldname);
827 // 2 possibilities : 1. just displaying the active edict count
828 // 2. making a function pointer [x]
829 static void PRVM_ED_Count_f (void)
835 Con_Print("prvm_count <program name>\n");
839 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
842 prog->count_edicts(prog);
846 ==============================================================================
850 FIXME: need to tag constants, doesn't really work
851 ==============================================================================
859 void PRVM_ED_WriteGlobals (prvm_prog_t *prog, qfile_t *f)
866 char valuebuf[MAX_INPUTLINE];
869 for (i = 0;i < prog->numglobaldefs;i++)
871 def = &prog->globaldefs[i];
873 if ( !(def->type & DEF_SAVEGLOBAL) )
875 type &= ~DEF_SAVEGLOBAL;
877 if (type != ev_string && type != ev_float && type != ev_entity)
880 name = PRVM_GetString(prog, def->s_name);
882 if(developer_entityparsing.integer)
883 Con_Printf("PRVM_ED_WriteGlobals: at global %s\n", name);
885 prog->statestring = va(vabuf, sizeof(vabuf), "PRVM_ED_WriteGlobals, name=%s", name);
886 FS_Printf(f,"\"%s\" ", name);
887 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString(prog, (etype_t)type, (prvm_eval_t *)&prog->globals.fp[def->ofs], valuebuf, sizeof(valuebuf)));
888 prog->statestring = NULL;
898 void PRVM_ED_ParseGlobals (prvm_prog_t *prog, const char *data)
900 char keyname[MAX_INPUTLINE];
906 if (!COM_ParseToken_Simple(&data, false, false, true))
907 prog->error_cmd("PRVM_ED_ParseGlobals: EOF without closing brace");
908 if (com_token[0] == '}')
911 if (developer_entityparsing.integer)
912 Con_Printf("Key: \"%s\"", com_token);
914 strlcpy (keyname, com_token, sizeof(keyname));
917 if (!COM_ParseToken_Simple(&data, false, true, true))
918 prog->error_cmd("PRVM_ED_ParseGlobals: EOF without closing brace");
920 if (developer_entityparsing.integer)
921 Con_Printf(" \"%s\"\n", com_token);
923 if (com_token[0] == '}')
924 prog->error_cmd("PRVM_ED_ParseGlobals: closing brace without data");
926 key = PRVM_ED_FindGlobal (prog, keyname);
929 Con_DPrintf("'%s' is not a global on %s\n", keyname, prog->name);
933 if (!PRVM_ED_ParseEpair(prog, NULL, key, com_token, true))
934 prog->error_cmd("PRVM_ED_ParseGlobals: parse error");
938 //============================================================================
945 Can parse either fields or globals
946 returns false if error
949 qboolean PRVM_ED_ParseEpair(prvm_prog_t *prog, prvm_edict_t *ent, ddef_t *key, const char *s, qboolean parsebackslash)
958 val = (prvm_eval_t *)(ent->fields.fp + key->ofs);
960 val = (prvm_eval_t *)(prog->globals.fp + key->ofs);
961 switch (key->type & ~DEF_SAVEGLOBAL)
964 l = (int)strlen(s) + 1;
965 val->string = PRVM_AllocString(prog, l, &new_p);
966 for (i = 0;i < l;i++)
968 if (s[i] == '\\' && s[i+1] && parsebackslash)
973 else if (s[i] == 'r')
984 while (*s && ISWHITESPACE(*s))
986 val->_float = atof(s);
990 for (i = 0;i < 3;i++)
992 while (*s && ISWHITESPACE(*s))
996 val->vector[i] = atof(s);
997 while (!ISWHITESPACE(*s))
1005 while (*s && ISWHITESPACE(*s))
1008 if (i >= prog->limit_edicts)
1009 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);
1010 while (i >= prog->max_edicts)
1011 PRVM_MEM_IncreaseEdicts(prog);
1012 // if IncreaseEdicts was called the base pointer needs to be updated
1014 val = (prvm_eval_t *)(ent->fields.fp + key->ofs);
1015 val->edict = PRVM_EDICT_TO_PROG(PRVM_EDICT_NUM((int)i));
1021 Con_DPrintf("PRVM_ED_ParseEpair: Bogus field name %s in %s\n", s, prog->name);
1024 def = PRVM_ED_FindField(prog, s + 1);
1027 Con_DPrintf("PRVM_ED_ParseEpair: Can't find field %s in %s\n", s, prog->name);
1030 val->_int = def->ofs;
1034 func = PRVM_ED_FindFunction(prog, s);
1037 Con_Printf("PRVM_ED_ParseEpair: Can't find function %s in %s\n", s, prog->name);
1040 val->function = func - prog->functions;
1044 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);
1054 Console command to send a string to QC function GameCommand of the
1058 sv_cmd adminmsg 3 "do not teamkill"
1059 cl_cmd someclientcommand
1060 menu_cmd somemenucommand
1062 All progs can support this extension; sg calls it in server QC, cg in client
1066 static void PRVM_GameCommand(const char *whichprogs, const char *whichcmd)
1071 Con_Printf("%s text...\n", whichcmd);
1075 if (!(prog = PRVM_FriendlyProgFromString(whichprogs)))
1078 if(!PRVM_allfunction(GameCommand))
1080 Con_Printf("%s program do not support GameCommand!\n", whichprogs);
1084 int restorevm_tempstringsbuf_cursize;
1089 restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
1090 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(prog, s ? s : "");
1091 prog->ExecuteProgram(prog, PRVM_allfunction(GameCommand), "QC function GameCommand is missing");
1092 prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1095 static void PRVM_GameCommand_Server_f(void)
1097 PRVM_GameCommand("server", "sv_cmd");
1099 static void PRVM_GameCommand_Client_f(void)
1101 PRVM_GameCommand("client", "cl_cmd");
1103 static void PRVM_GameCommand_Menu_f(void)
1105 PRVM_GameCommand("menu", "menu_cmd");
1112 Console command to load a field of a specified edict
1115 static void PRVM_ED_EdictGet_f(void)
1122 char valuebuf[MAX_INPUTLINE];
1124 if(Cmd_Argc() != 4 && Cmd_Argc() != 5)
1126 Con_Print("prvm_edictget <program name> <edict number> <field> [<cvar>]\n");
1130 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
1133 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1135 if((key = PRVM_ED_FindField(prog, Cmd_Argv(3))) == 0)
1137 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1141 v = (prvm_eval_t *)(ed->fields.fp + key->ofs);
1142 s = PRVM_UglyValueString(prog, (etype_t)key->type, v, valuebuf, sizeof(valuebuf));
1145 cvar_t *cvar = Cvar_FindVar(Cmd_Argv(4));
1146 if (cvar && cvar->flags & CVAR_READONLY)
1148 Con_Printf("prvm_edictget: %s is read-only\n", cvar->name);
1151 Cvar_Get(Cmd_Argv(4), s, 0, NULL);
1154 Con_Printf("%s\n", s);
1160 static void PRVM_ED_GlobalGet_f(void)
1166 char valuebuf[MAX_INPUTLINE];
1168 if(Cmd_Argc() != 3 && Cmd_Argc() != 4)
1170 Con_Print("prvm_globalget <program name> <global> [<cvar>]\n");
1174 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
1177 key = PRVM_ED_FindGlobal(prog, Cmd_Argv(2));
1180 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
1184 v = (prvm_eval_t *) &prog->globals.fp[key->ofs];
1185 s = PRVM_UglyValueString(prog, (etype_t)key->type, v, valuebuf, sizeof(valuebuf));
1188 cvar_t *cvar = Cvar_FindVar(Cmd_Argv(3));
1189 if (cvar && cvar->flags & CVAR_READONLY)
1191 Con_Printf("prvm_globalget: %s is read-only\n", cvar->name);
1194 Cvar_Get(Cmd_Argv(3), s, 0, NULL);
1197 Con_Printf("%s\n", s);
1207 Console command to set a field of a specified edict
1210 static void PRVM_ED_EdictSet_f(void)
1218 Con_Print("prvm_edictset <program name> <edict number> <field> <value>\n");
1222 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
1225 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1227 if((key = PRVM_ED_FindField(prog, Cmd_Argv(3))) == 0)
1228 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1230 PRVM_ED_ParseEpair(prog, ed, key, Cmd_Argv(4), true);
1234 ====================
1237 Parses an edict out of the given string, returning the new position
1238 ed should be a properly initialized empty edict.
1239 Used for initial level load and for savegames.
1240 ====================
1242 const char *PRVM_ED_ParseEdict (prvm_prog_t *prog, const char *data, prvm_edict_t *ent)
1252 // go through all the dictionary pairs
1256 if (!COM_ParseToken_Simple(&data, false, false, true))
1257 prog->error_cmd("PRVM_ED_ParseEdict: EOF without closing brace");
1258 if (developer_entityparsing.integer)
1259 Con_Printf("Key: \"%s\"", com_token);
1260 if (com_token[0] == '}')
1263 // anglehack is to allow QuakeEd to write single scalar angles
1264 // and allow them to be turned into vectors. (FIXME...)
1265 if (!strcmp(com_token, "angle"))
1267 strlcpy (com_token, "angles", sizeof(com_token));
1273 // FIXME: change light to _light to get rid of this hack
1274 if (!strcmp(com_token, "light"))
1275 strlcpy (com_token, "light_lev", sizeof(com_token)); // hack for single light def
1277 strlcpy (keyname, com_token, sizeof(keyname));
1279 // another hack to fix keynames with trailing spaces
1280 n = strlen(keyname);
1281 while (n && keyname[n-1] == ' ')
1288 if (!COM_ParseToken_Simple(&data, false, false, true))
1289 prog->error_cmd("PRVM_ED_ParseEdict: EOF without closing brace");
1290 if (developer_entityparsing.integer)
1291 Con_Printf(" \"%s\"\n", com_token);
1293 if (com_token[0] == '}')
1294 prog->error_cmd("PRVM_ED_ParseEdict: closing brace without data");
1298 // ignore attempts to set key "" (this problem occurs in nehahra neh1m8.bsp)
1302 // keynames with a leading underscore are used for utility comments,
1303 // and are immediately discarded by quake
1304 if (keyname[0] == '_')
1307 key = PRVM_ED_FindField (prog, keyname);
1310 Con_DPrintf("%s: '%s' is not a field\n", prog->name, keyname);
1317 strlcpy (temp, com_token, sizeof(temp));
1318 dpsnprintf (com_token, sizeof(com_token), "0 %s 0", temp);
1321 if (!PRVM_ED_ParseEpair(prog, ent, key, com_token, strcmp(keyname, "wad") != 0))
1322 prog->error_cmd("PRVM_ED_ParseEdict: parse error");
1326 ent->priv.required->free = true;
1327 ent->priv.required->freetime = realtime;
1336 PRVM_ED_LoadFromFile
1338 The entities are directly placed in the array, rather than allocated with
1339 PRVM_ED_Alloc, because otherwise an error loading the map would have entity
1340 number references out of order.
1342 Creates a server's entity / program execution context by
1343 parsing textual entity definitions out of an ent file.
1345 Used for both fresh maps and savegame loads. A fresh map would also need
1346 to call PRVM_ED_CallSpawnFunctions () to let the objects initialize themselves.
1349 void PRVM_ED_LoadFromFile (prvm_prog_t *prog, const char *data)
1352 int parsed, inhibited, spawned, died;
1353 const char *funcname;
1362 prvm_reuseedicts_always_allow = realtime;
1367 // parse the opening brace
1368 if (!COM_ParseToken_Simple(&data, false, false, true))
1370 if (com_token[0] != '{')
1371 prog->error_cmd("PRVM_ED_LoadFromFile: %s: found %s when expecting {", prog->name, com_token);
1373 // CHANGED: this is not conform to PR_LoadFromFile
1374 if(prog->loadintoworld)
1376 prog->loadintoworld = false;
1377 ent = PRVM_EDICT_NUM(0);
1380 ent = PRVM_ED_Alloc(prog);
1383 if (ent != prog->edicts) // hack
1384 memset (ent->fields.fp, 0, prog->entityfields * sizeof(prvm_vec_t));
1386 data = PRVM_ED_ParseEdict (prog, data, ent);
1389 // remove the entity ?
1390 if(!prog->load_edict(prog, ent))
1392 PRVM_ED_Free(prog, ent);
1397 if (PRVM_serverfunction(SV_OnEntityPreSpawnFunction))
1400 PRVM_serverglobalfloat(time) = sv.time;
1401 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1402 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityPreSpawnFunction), "QC function SV_OnEntityPreSpawnFunction is missing");
1405 if(ent->priv.required->free)
1412 // immediately call spawn function, but only if there is a self global and a classname
1414 if(!ent->priv.required->free)
1416 if (!PRVM_alledictstring(ent, classname))
1418 Con_Print("No classname for:\n");
1419 PRVM_ED_Print(prog, ent, NULL);
1420 PRVM_ED_Free (prog, ent);
1424 // look for the spawn function
1425 funcname = PRVM_GetString(prog, PRVM_alledictstring(ent, classname));
1426 func = PRVM_ED_FindFunction (prog, va(vabuf, sizeof(vabuf), "spawnfunc_%s", funcname));
1428 if(!PRVM_allglobalfloat(require_spawnfunc_prefix))
1429 func = PRVM_ED_FindFunction (prog, funcname);
1433 // check for OnEntityNoSpawnFunction
1434 if (PRVM_serverfunction(SV_OnEntityNoSpawnFunction))
1437 PRVM_serverglobalfloat(time) = sv.time;
1438 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1439 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityNoSpawnFunction), "QC function SV_OnEntityNoSpawnFunction is missing");
1443 if (developer.integer > 0) // don't confuse non-developers with errors
1445 Con_Print("No spawn function for:\n");
1446 PRVM_ED_Print(prog, ent, NULL);
1448 PRVM_ED_Free (prog, ent);
1449 continue; // not included in "inhibited" count
1455 PRVM_serverglobalfloat(time) = sv.time;
1456 PRVM_allglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1457 prog->ExecuteProgram(prog, func - prog->functions, "");
1461 if(!ent->priv.required->free)
1462 if (PRVM_serverfunction(SV_OnEntityPostSpawnFunction))
1465 PRVM_serverglobalfloat(time) = sv.time;
1466 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1467 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityPostSpawnFunction), "QC function SV_OnEntityPostSpawnFunction is missing");
1471 if (ent->priv.required->free)
1475 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);
1477 prvm_reuseedicts_always_allow = 0;
1480 static void PRVM_FindOffsets(prvm_prog_t *prog)
1482 // field and global searches use -1 for NULL
1483 memset(&prog->fieldoffsets, -1, sizeof(prog->fieldoffsets));
1484 memset(&prog->globaloffsets, -1, sizeof(prog->globaloffsets));
1485 // function searches use 0 for NULL
1486 memset(&prog->funcoffsets, 0, sizeof(prog->funcoffsets));
1487 #define PRVM_DECLARE_serverglobalfloat(x)
1488 #define PRVM_DECLARE_serverglobalvector(x)
1489 #define PRVM_DECLARE_serverglobalstring(x)
1490 #define PRVM_DECLARE_serverglobaledict(x)
1491 #define PRVM_DECLARE_serverglobalfunction(x)
1492 #define PRVM_DECLARE_clientglobalfloat(x)
1493 #define PRVM_DECLARE_clientglobalvector(x)
1494 #define PRVM_DECLARE_clientglobalstring(x)
1495 #define PRVM_DECLARE_clientglobaledict(x)
1496 #define PRVM_DECLARE_clientglobalfunction(x)
1497 #define PRVM_DECLARE_menuglobalfloat(x)
1498 #define PRVM_DECLARE_menuglobalvector(x)
1499 #define PRVM_DECLARE_menuglobalstring(x)
1500 #define PRVM_DECLARE_menuglobaledict(x)
1501 #define PRVM_DECLARE_menuglobalfunction(x)
1502 #define PRVM_DECLARE_serverfieldfloat(x)
1503 #define PRVM_DECLARE_serverfieldvector(x)
1504 #define PRVM_DECLARE_serverfieldstring(x)
1505 #define PRVM_DECLARE_serverfieldedict(x)
1506 #define PRVM_DECLARE_serverfieldfunction(x)
1507 #define PRVM_DECLARE_clientfieldfloat(x)
1508 #define PRVM_DECLARE_clientfieldvector(x)
1509 #define PRVM_DECLARE_clientfieldstring(x)
1510 #define PRVM_DECLARE_clientfieldedict(x)
1511 #define PRVM_DECLARE_clientfieldfunction(x)
1512 #define PRVM_DECLARE_menufieldfloat(x)
1513 #define PRVM_DECLARE_menufieldvector(x)
1514 #define PRVM_DECLARE_menufieldstring(x)
1515 #define PRVM_DECLARE_menufieldedict(x)
1516 #define PRVM_DECLARE_menufieldfunction(x)
1517 #define PRVM_DECLARE_serverfunction(x)
1518 #define PRVM_DECLARE_clientfunction(x)
1519 #define PRVM_DECLARE_menufunction(x)
1520 #define PRVM_DECLARE_field(x) prog->fieldoffsets.x = PRVM_ED_FindFieldOffset(prog, #x);
1521 #define PRVM_DECLARE_global(x) prog->globaloffsets.x = PRVM_ED_FindGlobalOffset(prog, #x);
1522 #define PRVM_DECLARE_function(x) prog->funcoffsets.x = PRVM_ED_FindFunctionOffset(prog, #x);
1523 #include "prvm_offsets.h"
1524 #undef PRVM_DECLARE_serverglobalfloat
1525 #undef PRVM_DECLARE_serverglobalvector
1526 #undef PRVM_DECLARE_serverglobalstring
1527 #undef PRVM_DECLARE_serverglobaledict
1528 #undef PRVM_DECLARE_serverglobalfunction
1529 #undef PRVM_DECLARE_clientglobalfloat
1530 #undef PRVM_DECLARE_clientglobalvector
1531 #undef PRVM_DECLARE_clientglobalstring
1532 #undef PRVM_DECLARE_clientglobaledict
1533 #undef PRVM_DECLARE_clientglobalfunction
1534 #undef PRVM_DECLARE_menuglobalfloat
1535 #undef PRVM_DECLARE_menuglobalvector
1536 #undef PRVM_DECLARE_menuglobalstring
1537 #undef PRVM_DECLARE_menuglobaledict
1538 #undef PRVM_DECLARE_menuglobalfunction
1539 #undef PRVM_DECLARE_serverfieldfloat
1540 #undef PRVM_DECLARE_serverfieldvector
1541 #undef PRVM_DECLARE_serverfieldstring
1542 #undef PRVM_DECLARE_serverfieldedict
1543 #undef PRVM_DECLARE_serverfieldfunction
1544 #undef PRVM_DECLARE_clientfieldfloat
1545 #undef PRVM_DECLARE_clientfieldvector
1546 #undef PRVM_DECLARE_clientfieldstring
1547 #undef PRVM_DECLARE_clientfieldedict
1548 #undef PRVM_DECLARE_clientfieldfunction
1549 #undef PRVM_DECLARE_menufieldfloat
1550 #undef PRVM_DECLARE_menufieldvector
1551 #undef PRVM_DECLARE_menufieldstring
1552 #undef PRVM_DECLARE_menufieldedict
1553 #undef PRVM_DECLARE_menufieldfunction
1554 #undef PRVM_DECLARE_serverfunction
1555 #undef PRVM_DECLARE_clientfunction
1556 #undef PRVM_DECLARE_menufunction
1557 #undef PRVM_DECLARE_field
1558 #undef PRVM_DECLARE_global
1559 #undef PRVM_DECLARE_function
1564 typedef struct dpfield_s
1571 #define DPFIELDS (sizeof(dpfields) / sizeof(dpfield_t))
1573 dpfield_t dpfields[] =
1584 #define PO_HASHSIZE 16384
1585 typedef struct po_string_s
1588 struct po_string_s *nextonhashchain;
1593 po_string_t *hashtable[PO_HASHSIZE];
1596 static void PRVM_PO_UnparseString(char *out, const char *in, size_t outsize)
1605 case '\a': if(outsize >= 2) { *out++ = '\\'; *out++ = 'a'; outsize -= 2; } break;
1606 case '\b': if(outsize >= 2) { *out++ = '\\'; *out++ = 'b'; outsize -= 2; } break;
1607 case '\t': if(outsize >= 2) { *out++ = '\\'; *out++ = 't'; outsize -= 2; } break;
1608 case '\r': if(outsize >= 2) { *out++ = '\\'; *out++ = 'r'; outsize -= 2; } break;
1609 case '\n': if(outsize >= 2) { *out++ = '\\'; *out++ = 'n'; outsize -= 2; } break;
1610 case '\\': if(outsize >= 2) { *out++ = '\\'; *out++ = '\\'; outsize -= 2; } break;
1611 case '"': if(outsize >= 2) { *out++ = '\\'; *out++ = '"'; outsize -= 2; } break;
1613 if(*in >= 0 && *in <= 0x1F)
1618 *out++ = '0' + ((*in & 0700) >> 6);
1619 *out++ = '0' + ((*in & 0070) >> 3);
1620 *out++ = '0' + (*in & 0007) ;
1637 static void PRVM_PO_ParseString(char *out, const char *in, size_t outsize)
1650 case 'a': if(outsize > 0) { *out++ = '\a'; --outsize; } break;
1651 case 'b': if(outsize > 0) { *out++ = '\b'; --outsize; } break;
1652 case 't': if(outsize > 0) { *out++ = '\t'; --outsize; } break;
1653 case 'r': if(outsize > 0) { *out++ = '\r'; --outsize; } break;
1654 case 'n': if(outsize > 0) { *out++ = '\n'; --outsize; } break;
1655 case '\\': if(outsize > 0) { *out++ = '\\'; --outsize; } break;
1656 case '"': if(outsize > 0) { *out++ = '"'; --outsize; } break;
1657 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
1661 if(*in >= '0' && *in <= '7')
1664 *out = (*out << 3) | (*in - '0');
1667 if(*in >= '0' && *in <= '7')
1670 *out = (*out << 3) | (*in - '0');
1681 if(outsize > 0) { *out++ = *in; --outsize; }
1696 static po_t *PRVM_PO_Load(const char *filename, const char *filename2, mempool_t *pool)
1701 char inbuf[MAX_INPUTLINE];
1702 char decodedbuf[MAX_INPUTLINE];
1705 po_string_t thisstr;
1708 for (i = 0; i < 2; ++i)
1710 const char *buf = (const char *)
1711 FS_LoadFile((i > 0 ? filename : filename2), pool, true, NULL);
1712 // first read filename2, then read filename
1713 // so that progs.dat.de.po wins over common.de.po
1714 // and within file, last item wins
1721 po = (po_t *)Mem_Alloc(pool, sizeof(*po));
1722 memset(po, 0, sizeof(*po));
1725 memset(&thisstr, 0, sizeof(thisstr)); // hush compiler warning
1733 p = strchr(p, '\n');
1739 if(*p == '\r' || *p == '\n')
1744 if(!strncmp(p, "msgid \"", 7))
1749 else if(!strncmp(p, "msgstr \"", 8))
1756 p = strchr(p, '\n');
1766 q = strchr(p, '\n');
1773 if((size_t)(q - p) >= (size_t) sizeof(inbuf))
1775 strlcpy(inbuf, p, q - p); // not - 1, because this adds a NUL
1776 PRVM_PO_ParseString(decodedbuf + decodedpos, inbuf, sizeof(decodedbuf) - decodedpos);
1777 decodedpos += strlen(decodedbuf + decodedpos);
1787 Mem_Free(thisstr.key);
1788 thisstr.key = (char *)Mem_Alloc(pool, decodedpos + 1);
1789 memcpy(thisstr.key, decodedbuf, decodedpos + 1);
1791 else if(decodedpos > 0 && thisstr.key) // skip empty translation results
1793 thisstr.value = (char *)Mem_Alloc(pool, decodedpos + 1);
1794 memcpy(thisstr.value, decodedbuf, decodedpos + 1);
1795 hashindex = CRC_Block((const unsigned char *) thisstr.key, strlen(thisstr.key)) % PO_HASHSIZE;
1796 thisstr.nextonhashchain = po->hashtable[hashindex];
1797 po->hashtable[hashindex] = (po_string_t *)Mem_Alloc(pool, sizeof(thisstr));
1798 memcpy(po->hashtable[hashindex], &thisstr, sizeof(thisstr));
1799 memset(&thisstr, 0, sizeof(thisstr));
1803 Mem_Free((char *) buf);
1808 static const char *PRVM_PO_Lookup(po_t *po, const char *str)
1810 int hashindex = CRC_Block((const unsigned char *) str, strlen(str)) % PO_HASHSIZE;
1811 po_string_t *p = po->hashtable[hashindex];
1814 if(!strcmp(str, p->key))
1816 p = p->nextonhashchain;
1820 static void PRVM_PO_Destroy(po_t *po)
1823 for(i = 0; i < PO_HASHSIZE; ++i)
1825 po_string_t *p = po->hashtable[i];
1829 p = p->nextonhashchain;
1838 void PRVM_LeakTest(prvm_prog_t *prog);
1839 void PRVM_Prog_Reset(prvm_prog_t *prog)
1843 PRVM_LeakTest(prog);
1844 prog->reset_cmd(prog);
1845 Mem_FreePool(&prog->progs_mempool);
1847 PRVM_PO_Destroy((po_t *) prog->po);
1849 memset(prog,0,sizeof(prvm_prog_t));
1850 prog->break_statement = -1;
1851 prog->watch_global_type = ev_void;
1852 prog->watch_field_type = ev_void;
1860 static void PRVM_LoadLNO( prvm_prog_t *prog, const char *progname ) {
1861 fs_offset_t filesize;
1863 unsigned int *header;
1866 FS_StripExtension( progname, filename, sizeof( filename ) );
1867 strlcat( filename, ".lno", sizeof( filename ) );
1869 lno = FS_LoadFile( filename, tempmempool, false, &filesize );
1875 <Spike> SafeWrite (h, &lnotype, sizeof(int));
1876 <Spike> SafeWrite (h, &version, sizeof(int));
1877 <Spike> SafeWrite (h, &numglobaldefs, sizeof(int));
1878 <Spike> SafeWrite (h, &numpr_globals, sizeof(int));
1879 <Spike> SafeWrite (h, &numfielddefs, sizeof(int));
1880 <Spike> SafeWrite (h, &numstatements, sizeof(int));
1881 <Spike> SafeWrite (h, statement_linenums, numstatements*sizeof(int));
1883 if ((unsigned int)filesize < (6 + prog->progs_numstatements) * sizeof(int))
1889 header = (unsigned int *) lno;
1890 if( header[ 0 ] == *(unsigned int *) "LNOF" &&
1891 LittleLong( header[ 1 ] ) == 1 &&
1892 (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs_numglobaldefs &&
1893 (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs_numglobals &&
1894 (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs_numfielddefs &&
1895 (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs_numstatements )
1897 prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof( int ) );
1898 memcpy( prog->statement_linenums, header + 6, prog->progs_numstatements * sizeof( int ) );
1900 /* gmqcc suports columnums */
1901 if ((unsigned int)filesize > ((6 + 2 * prog->progs_numstatements) * sizeof( int )))
1903 prog->statement_columnnums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof( int ) );
1904 memcpy( prog->statement_columnnums, header + 6 + prog->progs_numstatements, prog->progs_numstatements * sizeof( int ) );
1915 static void PRVM_UpdateBreakpoints(prvm_prog_t *prog);
1916 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)
1919 dprograms_t *dprograms;
1920 dstatement_t *instatements;
1921 ddef_t *infielddefs;
1922 ddef_t *inglobaldefs;
1924 dfunction_t *infunctions;
1926 fs_offset_t filesize;
1927 int requiredglobalspace;
1943 prog->error_cmd("PRVM_LoadProgs: there is already a %s program loaded!", prog->name );
1945 Host_LockSession(); // all progs can use the session cvar
1946 Crypto_LoadKeys(); // all progs might use the keys at init time
1950 dprograms = (dprograms_t *) data;
1954 dprograms = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
1955 if (dprograms == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
1956 prog->error_cmd("PRVM_LoadProgs: couldn't load %s for %s", filename, prog->name);
1957 // TODO bounds check header fields (e.g. numstatements), they must never go behind end of file
1959 prog->profiletime = Sys_DirtyTime();
1960 prog->starttime = realtime;
1962 Con_DPrintf("%s programs occupy %iK.\n", prog->name, (int)(filesize/1024));
1964 requiredglobalspace = 0;
1965 for (i = 0;i < numrequiredglobals;i++)
1966 requiredglobalspace += required_global[i].type == ev_vector ? 3 : 1;
1968 prog->filecrc = CRC_Block((unsigned char *)dprograms, filesize);
1970 // byte swap the header
1971 prog->progs_version = LittleLong(dprograms->version);
1972 prog->progs_crc = LittleLong(dprograms->crc);
1973 if (prog->progs_version != PROG_VERSION)
1974 prog->error_cmd("%s: %s has wrong version number (%i should be %i)", prog->name, filename, prog->progs_version, PROG_VERSION);
1975 instatements = (dstatement_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_statements));
1976 prog->progs_numstatements = LittleLong(dprograms->numstatements);
1977 inglobaldefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globaldefs));
1978 prog->progs_numglobaldefs = LittleLong(dprograms->numglobaldefs);
1979 infielddefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_fielddefs));
1980 prog->progs_numfielddefs = LittleLong(dprograms->numfielddefs);
1981 infunctions = (dfunction_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_functions));
1982 prog->progs_numfunctions = LittleLong(dprograms->numfunctions);
1983 instrings = (char *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_strings));
1984 prog->progs_numstrings = LittleLong(dprograms->numstrings);
1985 inglobals = (int *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globals));
1986 prog->progs_numglobals = LittleLong(dprograms->numglobals);
1987 prog->progs_entityfields = LittleLong(dprograms->entityfields);
1989 prog->numstatements = prog->progs_numstatements;
1990 prog->numglobaldefs = prog->progs_numglobaldefs;
1991 prog->numfielddefs = prog->progs_numfielddefs;
1992 prog->numfunctions = prog->progs_numfunctions;
1993 prog->numstrings = prog->progs_numstrings;
1994 prog->numglobals = prog->progs_numglobals;
1995 prog->entityfields = prog->progs_entityfields;
1997 if (LittleLong(dprograms->ofs_strings) + prog->progs_numstrings > (int)filesize)
1998 prog->error_cmd("%s: %s strings go past end of file", prog->name, filename);
1999 prog->strings = (char *)Mem_Alloc(prog->progs_mempool, prog->progs_numstrings);
2000 memcpy(prog->strings, instrings, prog->progs_numstrings);
2001 prog->stringssize = prog->progs_numstrings;
2003 prog->numknownstrings = 0;
2004 prog->maxknownstrings = 0;
2005 prog->knownstrings = NULL;
2006 prog->knownstrings_freeable = NULL;
2008 Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
2010 // we need to expand the globaldefs and fielddefs to include engine defs
2011 prog->globaldefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobaldefs + numrequiredglobals) * sizeof(ddef_t));
2012 prog->globals.fp = (prvm_vec_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobals + requiredglobalspace + 2) * sizeof(prvm_vec_t));
2013 // + 2 is because of an otherwise occurring overrun in RETURN instruction
2014 // when trying to return the last or second-last global
2015 // (RETURN always returns a vector, there is no RETURN_F instruction)
2016 prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numfielddefs + numrequiredfields) * sizeof(ddef_t));
2017 // we need to convert the statements to our memory format
2018 prog->statements = (mstatement_t *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(mstatement_t));
2019 // allocate space for profiling statement usage
2020 prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(*prog->statement_profile));
2021 prog->explicit_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(*prog->statement_profile));
2022 // functions need to be converted to the memory format
2023 prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs_numfunctions);
2025 for (i = 0;i < prog->progs_numfunctions;i++)
2027 prog->functions[i].first_statement = LittleLong(infunctions[i].first_statement);
2028 prog->functions[i].parm_start = LittleLong(infunctions[i].parm_start);
2029 prog->functions[i].s_name = LittleLong(infunctions[i].s_name);
2030 prog->functions[i].s_file = LittleLong(infunctions[i].s_file);
2031 prog->functions[i].numparms = LittleLong(infunctions[i].numparms);
2032 prog->functions[i].locals = LittleLong(infunctions[i].locals);
2033 memcpy(prog->functions[i].parm_size, infunctions[i].parm_size, sizeof(infunctions[i].parm_size));
2034 if(prog->functions[i].first_statement >= prog->numstatements)
2035 prog->error_cmd("PRVM_LoadProgs: out of bounds function statement (function %d) in %s", i, prog->name);
2036 // TODO bounds check parm_start, s_name, s_file, numparms, locals, parm_size
2039 // copy the globaldefs to the new globaldefs list
2040 for (i=0 ; i<prog->numglobaldefs ; i++)
2042 prog->globaldefs[i].type = LittleShort(inglobaldefs[i].type);
2043 prog->globaldefs[i].ofs = LittleShort(inglobaldefs[i].ofs);
2044 prog->globaldefs[i].s_name = LittleLong(inglobaldefs[i].s_name);
2045 // TODO bounds check ofs, s_name
2048 // append the required globals
2049 for (i = 0;i < numrequiredglobals;i++)
2051 prog->globaldefs[prog->numglobaldefs].type = required_global[i].type;
2052 prog->globaldefs[prog->numglobaldefs].ofs = prog->numglobals;
2053 prog->globaldefs[prog->numglobaldefs].s_name = PRVM_SetEngineString(prog, required_global[i].name);
2054 if (prog->globaldefs[prog->numglobaldefs].type == ev_vector)
2055 prog->numglobals += 3;
2058 prog->numglobaldefs++;
2061 // copy the progs fields to the new fields list
2062 for (i = 0;i < prog->numfielddefs;i++)
2064 prog->fielddefs[i].type = LittleShort(infielddefs[i].type);
2065 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
2066 prog->error_cmd("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", prog->name);
2067 prog->fielddefs[i].ofs = LittleShort(infielddefs[i].ofs);
2068 prog->fielddefs[i].s_name = LittleLong(infielddefs[i].s_name);
2069 // TODO bounds check ofs, s_name
2072 // append the required fields
2073 for (i = 0;i < numrequiredfields;i++)
2075 prog->fielddefs[prog->numfielddefs].type = required_field[i].type;
2076 prog->fielddefs[prog->numfielddefs].ofs = prog->entityfields;
2077 prog->fielddefs[prog->numfielddefs].s_name = PRVM_SetEngineString(prog, required_field[i].name);
2078 if (prog->fielddefs[prog->numfielddefs].type == ev_vector)
2079 prog->entityfields += 3;
2081 prog->entityfields++;
2082 prog->numfielddefs++;
2085 // LordHavoc: TODO: reorder globals to match engine struct
2086 // LordHavoc: TODO: reorder fields to match engine struct
2087 #define remapglobal(index) (index)
2088 #define remapfield(index) (index)
2091 // FIXME: LordHavoc: this uses a crude way to identify integer constants, rather than checking for matching globaldefs and checking their type
2092 for (i = 0;i < prog->progs_numglobals;i++)
2094 u.i = LittleLong(inglobals[i]);
2095 // most globals are 0, we only need to deal with the ones that are not
2098 d = u.i & 0xFF800000;
2099 if ((d == 0xFF800000) || (d == 0))
2101 // Looks like an integer (expand to int64)
2102 prog->globals.ip[remapglobal(i)] = u.i;
2106 // Looks like a float (expand to double)
2107 prog->globals.fp[remapglobal(i)] = u.f;
2112 // LordHavoc: TODO: support 32bit progs statement formats
2113 // copy, remap globals in statements, bounds check
2114 for (i = 0;i < prog->progs_numstatements;i++)
2116 op = (opcode_t)LittleShort(instatements[i].op);
2117 a = (unsigned short)LittleShort(instatements[i].a);
2118 b = (unsigned short)LittleShort(instatements[i].b);
2119 c = (unsigned short)LittleShort(instatements[i].c);
2125 if (a >= prog->progs_numglobals || b + i < 0 || b + i >= prog->progs_numstatements)
2126 prog->error_cmd("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", i, prog->name);
2127 prog->statements[i].op = op;
2128 prog->statements[i].operand[0] = remapglobal(a);
2129 prog->statements[i].operand[1] = -1;
2130 prog->statements[i].operand[2] = -1;
2131 prog->statements[i].jumpabsolute = i + b;
2135 if (a + i < 0 || a + i >= prog->progs_numstatements)
2136 prog->error_cmd("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, prog->name);
2137 prog->statements[i].op = op;
2138 prog->statements[i].operand[0] = -1;
2139 prog->statements[i].operand[1] = -1;
2140 prog->statements[i].operand[2] = -1;
2141 prog->statements[i].jumpabsolute = i + a;
2144 Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", (int)op, i, prog->name);
2146 // global global global
2181 if (a >= prog->progs_numglobals || b >= prog->progs_numglobals || c >= prog->progs_numglobals)
2182 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
2183 prog->statements[i].op = op;
2184 prog->statements[i].operand[0] = remapglobal(a);
2185 prog->statements[i].operand[1] = remapglobal(b);
2186 prog->statements[i].operand[2] = remapglobal(c);
2187 prog->statements[i].jumpabsolute = -1;
2189 // global none global
2195 if (a >= prog->progs_numglobals || c >= prog->progs_numglobals)
2196 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2197 prog->statements[i].op = op;
2198 prog->statements[i].operand[0] = remapglobal(a);
2199 prog->statements[i].operand[1] = -1;
2200 prog->statements[i].operand[2] = remapglobal(c);
2201 prog->statements[i].jumpabsolute = -1;
2217 if (a >= prog->progs_numglobals || b >= prog->progs_numglobals)
2218 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2219 prog->statements[i].op = op;
2220 prog->statements[i].operand[0] = remapglobal(a);
2221 prog->statements[i].operand[1] = remapglobal(b);
2222 prog->statements[i].operand[2] = -1;
2223 prog->statements[i].jumpabsolute = -1;
2227 if ( a < prog->progs_numglobals)
2228 if ( prog->globals.ip[remapglobal(a)] >= 0 )
2229 if ( prog->globals.ip[remapglobal(a)] < prog->progs_numfunctions )
2230 if ( prog->functions[prog->globals.ip[remapglobal(a)]].first_statement == -642 )
2231 ++prog->numexplicitcoveragestatements;
2242 if ( a >= prog->progs_numglobals)
2243 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2244 prog->statements[i].op = op;
2245 prog->statements[i].operand[0] = remapglobal(a);
2246 prog->statements[i].operand[1] = -1;
2247 prog->statements[i].operand[2] = -1;
2248 prog->statements[i].jumpabsolute = -1;
2252 if(prog->numstatements < 1)
2254 prog->error_cmd("PRVM_LoadProgs: empty program in %s", prog->name);
2256 else switch(prog->statements[prog->numstatements - 1].op)
2263 prog->error_cmd("PRVM_LoadProgs: program may fall off the edge (does not end with RETURN, GOTO or DONE) in %s", prog->name);
2267 // we're done with the file now
2269 Mem_Free(dprograms);
2272 // check required functions
2273 for(i=0 ; i < numrequiredfunc ; i++)
2274 if(PRVM_ED_FindFunction(prog, required_func[i]) == 0)
2275 prog->error_cmd("%s: %s not found in %s",prog->name, required_func[i], filename);
2277 PRVM_LoadLNO(prog, filename);
2279 PRVM_Init_Exec(prog);
2281 if(*prvm_language.string)
2282 // in CSQC we really shouldn't be able to change how stuff works... sorry for now
2283 // later idea: include a list of authorized .po file checksums with the csprogs
2285 qboolean deftrans = prog == CLVM_prog;
2286 const char *realfilename = (prog != CLVM_prog ? filename : csqc_progname.string);
2287 if(deftrans) // once we have dotranslate_ strings, ALWAYS use the opt-in method!
2289 for (i=0 ; i<prog->numglobaldefs ; i++)
2292 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2293 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2294 if(name && !strncmp(name, "dotranslate_", 12))
2301 if(!strcmp(prvm_language.string, "dump"))
2303 qfile_t *f = FS_OpenRealFile(va(vabuf, sizeof(vabuf), "%s.pot", realfilename), "w", false);
2304 Con_Printf("Dumping to %s.pot\n", realfilename);
2307 for (i=0 ; i<prog->numglobaldefs ; i++)
2310 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2311 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2312 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2314 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2315 const char *value = PRVM_GetString(prog, val->string);
2318 char buf[MAX_INPUTLINE];
2319 PRVM_PO_UnparseString(buf, value, sizeof(buf));
2320 FS_Printf(f, "msgid \"%s\"\nmsgstr \"\"\n\n", buf);
2329 po_t *po = PRVM_PO_Load(
2330 va(vabuf, sizeof(vabuf), "%s.%s.po", realfilename, prvm_language.string),
2331 va(vabuf2, sizeof(vabuf2), "common.%s.po", prvm_language.string),
2332 prog->progs_mempool);
2335 for (i=0 ; i<prog->numglobaldefs ; i++)
2338 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2339 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2340 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2342 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2343 const char *value = PRVM_GetString(prog, val->string);
2346 value = PRVM_PO_Lookup(po, value);
2348 val->string = PRVM_SetEngineString(prog, value);
2356 for (i=0 ; i<prog->numglobaldefs ; i++)
2359 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2360 //Con_Printf("found var %s\n", name);
2362 && !strncmp(name, "autocvar_", 9)
2363 && !(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
2366 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2367 cvar_t *cvar = Cvar_FindVar(name + 9);
2368 //Con_Printf("PRVM_LoadProgs: autocvar global %s in %s, processing...\n", name, prog->name);
2373 Con_DPrintf("PRVM_LoadProgs: no cvar for autocvar global %s in %s, creating...\n", name, prog->name);
2374 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2377 if((float)((int)(val->_float)) == val->_float)
2378 dpsnprintf(buf, sizeof(buf), "%i", (int)(val->_float));
2380 dpsnprintf(buf, sizeof(buf), "%.9g", val->_float);
2384 dpsnprintf(buf, sizeof(buf), "%.9g %.9g %.9g", val->vector[0], val->vector[1], val->vector[2]); value = buf;
2387 value = PRVM_GetString(prog, val->string);
2390 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2393 cvar = Cvar_Get(name + 9, value, 0, NULL);
2394 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2396 val->string = PRVM_SetEngineString(prog, cvar->string);
2397 cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2400 prog->error_cmd("PRVM_LoadProgs: could not create cvar for autocvar global %s in %s", name, prog->name);
2401 cvar->globaldefindex_progid[prog - prvm_prog_list] = prog->id;
2402 cvar->globaldefindex[prog - prvm_prog_list] = i;
2404 else if((cvar->flags & CVAR_PRIVATE) == 0)
2406 // MUST BE SYNCED WITH cvar.c Cvar_Set
2409 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2412 val->_float = cvar->value;
2416 VectorClear(val->vector);
2417 for (j = 0;j < 3;j++)
2419 while (*s && ISWHITESPACE(*s))
2423 val->vector[j] = atof(s);
2424 while (!ISWHITESPACE(*s))
2431 val->string = PRVM_SetEngineString(prog, cvar->string);
2432 cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2435 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2438 cvar->globaldefindex_progid[prog - prvm_prog_list] = prog->id;
2439 cvar->globaldefindex[prog - prvm_prog_list] = i;
2442 Con_Printf("PRVM_LoadProgs: private cvar for autocvar global %s in %s\n", name, prog->name);
2448 prog->loaded = TRUE;
2450 PRVM_UpdateBreakpoints(prog);
2452 // set flags & ddef_ts in prog
2456 PRVM_FindOffsets(prog);
2458 prog->init_cmd(prog);
2461 PRVM_MEM_Alloc(prog);
2463 // Inittime is at least the time when this function finished. However,
2464 // later events may bump it.
2465 prog->inittime = realtime;
2469 static void PRVM_Fields_f (void)
2472 int i, j, ednum, used, usedamount;
2474 char tempstring[MAX_INPUTLINE], tempstring2[260];
2484 Con_Print("no progs loaded\n");
2491 Con_Print("prvm_fields <program name>\n");
2495 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2498 counts = (int *)Mem_Alloc(tempmempool, prog->numfielddefs * sizeof(int));
2499 for (ednum = 0;ednum < prog->max_edicts;ednum++)
2501 ed = PRVM_EDICT_NUM(ednum);
2502 if (ed->priv.required->free)
2504 for (i = 1;i < prog->numfielddefs;i++)
2506 d = &prog->fielddefs[i];
2507 name = PRVM_GetString(prog, d->s_name);
2508 if (name[strlen(name)-2] == '_')
2509 continue; // skip _x, _y, _z vars
2510 val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
2511 // if the value is still all 0, skip the field
2512 for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
2514 if (val->ivector[j])
2525 for (i = 0;i < prog->numfielddefs;i++)
2527 d = &prog->fielddefs[i];
2528 name = PRVM_GetString(prog, d->s_name);
2529 if (name[strlen(name)-2] == '_')
2530 continue; // skip _x, _y, _z vars
2531 switch(d->type & ~DEF_SAVEGLOBAL)
2534 strlcat(tempstring, "string ", sizeof(tempstring));
2537 strlcat(tempstring, "entity ", sizeof(tempstring));
2540 strlcat(tempstring, "function ", sizeof(tempstring));
2543 strlcat(tempstring, "field ", sizeof(tempstring));
2546 strlcat(tempstring, "void ", sizeof(tempstring));
2549 strlcat(tempstring, "float ", sizeof(tempstring));
2552 strlcat(tempstring, "vector ", sizeof(tempstring));
2555 strlcat(tempstring, "pointer ", sizeof(tempstring));
2558 dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
2559 strlcat(tempstring, tempstring2, sizeof(tempstring));
2562 if (strlen(name) > sizeof(tempstring2)-4)
2564 memcpy (tempstring2, name, sizeof(tempstring2)-4);
2565 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
2566 tempstring2[sizeof(tempstring2)-1] = 0;
2569 strlcat(tempstring, name, sizeof(tempstring));
2570 for (j = (int)strlen(name);j < 25;j++)
2571 strlcat(tempstring, " ", sizeof(tempstring));
2572 dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
2573 strlcat(tempstring, tempstring2, sizeof(tempstring));
2574 strlcat(tempstring, "\n", sizeof(tempstring));
2575 if (strlen(tempstring) >= sizeof(tempstring)/2)
2577 Con_Print(tempstring);
2583 usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
2587 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);
2590 static void PRVM_Globals_f (void)
2594 const char *wildcard;
2600 Con_Print("no progs loaded\n");
2603 if(Cmd_Argc () < 2 || Cmd_Argc() > 3)
2605 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
2609 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2612 if( Cmd_Argc() == 3)
2613 wildcard = Cmd_Argv(2);
2617 Con_Printf("%s :", prog->name);
2619 for (i = 0;i < prog->numglobaldefs;i++)
2622 if( !matchpattern( PRVM_GetString(prog, prog->globaldefs[i].s_name), wildcard, 1) )
2627 Con_Printf("%s\n", PRVM_GetString(prog, prog->globaldefs[i].s_name));
2629 Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->numglobals, numculled, prog->numglobals * 4);
2637 static void PRVM_Global_f(void)
2641 char valuebuf[MAX_INPUTLINE];
2642 if( Cmd_Argc() != 3 ) {
2643 Con_Printf( "prvm_global <program name> <global name>\n" );
2647 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2650 global = PRVM_ED_FindGlobal( prog, Cmd_Argv(2) );
2652 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2654 Con_Printf( "%s: %s\n", Cmd_Argv(2), PRVM_ValueString( prog, (etype_t)global->type, PRVM_GLOBALFIELDVALUE(global->ofs), valuebuf, sizeof(valuebuf) ) );
2662 static void PRVM_GlobalSet_f(void)
2666 if( Cmd_Argc() != 4 ) {
2667 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2671 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2674 global = PRVM_ED_FindGlobal( prog, Cmd_Argv(2) );
2676 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2678 PRVM_ED_ParseEpair( prog, NULL, global, Cmd_Argv(3), true );
2682 ======================
2683 Break- and Watchpoints
2684 ======================
2688 char break_statement[256];
2689 char watch_global[256];
2691 char watch_field[256];
2694 static debug_data_t debug_data[PRVM_PROG_MAX];
2696 void PRVM_Breakpoint(prvm_prog_t *prog, int stack_index, const char *text)
2699 Con_Printf("PRVM_Breakpoint: %s\n", text);
2700 PRVM_PrintState(prog, stack_index);
2701 if (prvm_breakpointdump.integer)
2702 Host_Savegame_to(prog, va(vabuf, sizeof(vabuf), "breakpoint-%s.dmp", prog->name));
2705 void PRVM_Watchpoint(prvm_prog_t *prog, int stack_index, const char *text, etype_t type, prvm_eval_t *o, prvm_eval_t *n)
2707 size_t sz = sizeof(prvm_vec_t) * ((type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2708 if (memcmp(o, n, sz))
2711 char valuebuf_o[128];
2712 char valuebuf_n[128];
2713 PRVM_UglyValueString(prog, type, o, valuebuf_o, sizeof(valuebuf_o));
2714 PRVM_UglyValueString(prog, type, n, valuebuf_n, sizeof(valuebuf_n));
2715 dpsnprintf(buf, sizeof(buf), "%s: %s -> %s", text, valuebuf_o, valuebuf_n);
2716 PRVM_Breakpoint(prog, stack_index, buf);
2721 static void PRVM_UpdateBreakpoints(prvm_prog_t *prog)
2723 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2726 if (debug->break_statement[0])
2728 if (debug->break_statement[0] >= '0' && debug->break_statement[0] <= '9')
2730 prog->break_statement = atoi(debug->break_statement);
2731 prog->break_stack_index = 0;
2736 func = PRVM_ED_FindFunction (prog, debug->break_statement);
2739 Con_Printf("%s progs: no function or statement named %s to break on!\n", prog->name, debug->break_statement);
2740 prog->break_statement = -1;
2744 prog->break_statement = func->first_statement;
2745 prog->break_stack_index = 1;
2748 if (prog->break_statement >= -1)
2749 Con_Printf("%s progs: breakpoint is at statement %d\n", prog->name, prog->break_statement);
2752 prog->break_statement = -1;
2754 if (debug->watch_global[0])
2756 ddef_t *global = PRVM_ED_FindGlobal( prog, debug->watch_global );
2759 Con_Printf( "%s progs: no global named '%s' to watch!\n", prog->name, debug->watch_global );
2760 prog->watch_global_type = ev_void;
2764 size_t sz = sizeof(prvm_vec_t) * ((global->type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2765 prog->watch_global = global->ofs;
2766 prog->watch_global_type = (etype_t)global->type;
2767 memcpy(&prog->watch_global_value, PRVM_GLOBALFIELDVALUE(prog->watch_global), sz);
2769 if (prog->watch_global_type != ev_void)
2770 Con_Printf("%s progs: global watchpoint is at global index %d\n", prog->name, prog->watch_global);
2773 prog->watch_global_type = ev_void;
2775 if (debug->watch_field[0])
2777 ddef_t *field = PRVM_ED_FindField( prog, debug->watch_field );
2780 Con_Printf( "%s progs: no field named '%s' to watch!\n", prog->name, debug->watch_field );
2781 prog->watch_field_type = ev_void;
2785 size_t sz = sizeof(prvm_vec_t) * ((field->type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2786 prog->watch_edict = debug->watch_edict;
2787 prog->watch_field = field->ofs;
2788 prog->watch_field_type = (etype_t)field->type;
2789 if (prog->watch_edict < prog->num_edicts)
2790 memcpy(&prog->watch_edictfield_value, PRVM_EDICTFIELDVALUE(PRVM_EDICT_NUM(prog->watch_edict), prog->watch_field), sz);
2792 memset(&prog->watch_edictfield_value, 0, sz);
2794 if (prog->watch_edict != ev_void)
2795 Con_Printf("%s progs: edict field watchpoint is at edict %d field index %d\n", prog->name, prog->watch_edict, prog->watch_field);
2798 prog->watch_field_type = ev_void;
2801 static void PRVM_Breakpoint_f(void)
2805 if( Cmd_Argc() == 2 ) {
2806 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2809 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2810 debug->break_statement[0] = 0;
2812 PRVM_UpdateBreakpoints(prog);
2815 if( Cmd_Argc() != 3 ) {
2816 Con_Printf( "prvm_breakpoint <program name> <function name | statement>\n" );
2820 if (!(prog = PRVM_ProgFromString(Cmd_Argv(1))))
2824 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2825 strlcpy(debug->break_statement, Cmd_Argv(2), sizeof(debug->break_statement));
2827 PRVM_UpdateBreakpoints(prog);
2830 static void PRVM_GlobalWatchpoint_f(void)
2834 if( Cmd_Argc() == 2 ) {
2835 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2838 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2839 debug->watch_global[0] = 0;
2841 PRVM_UpdateBreakpoints(prog);
2844 if( Cmd_Argc() != 3 ) {
2845 Con_Printf( "prvm_globalwatchpoint <program name> <global name>\n" );
2849 if (!(prog = PRVM_ProgFromString(Cmd_Argv(1))))
2853 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2854 strlcpy(debug->watch_global, Cmd_Argv(2), sizeof(debug->watch_global));
2856 PRVM_UpdateBreakpoints(prog);
2859 static void PRVM_EdictWatchpoint_f(void)
2863 if( Cmd_Argc() == 2 ) {
2864 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2867 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2868 debug->watch_field[0] = 0;
2870 PRVM_UpdateBreakpoints(prog);
2873 if( Cmd_Argc() != 4 ) {
2874 Con_Printf( "prvm_edictwatchpoint <program name> <edict number> <field name>\n" );
2878 if (!(prog = PRVM_ProgFromString(Cmd_Argv(1))))
2882 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2883 debug->watch_edict = atoi(Cmd_Argv(2));
2884 strlcpy(debug->watch_field, Cmd_Argv(3), sizeof(debug->watch_field));
2886 PRVM_UpdateBreakpoints(prog);
2894 void PRVM_Init (void)
2896 Cmd_AddCommand ("prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2897 Cmd_AddCommand ("prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2898 Cmd_AddCommand ("prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2899 Cmd_AddCommand ("prvm_profile", PRVM_Profile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu)");
2900 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");
2901 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)");
2902 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)");
2903 Cmd_AddCommand ("prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2904 Cmd_AddCommand ("prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2905 Cmd_AddCommand ("prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2906 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)");
2907 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");
2908 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");
2909 Cmd_AddCommand ("prvm_printfunction", PRVM_PrintFunction_f, "prints a disassembly (QuakeC instructions) of the specified function in the selected VM (server, client, menu)");
2910 Cmd_AddCommand ("cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2911 Cmd_AddCommand ("menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2912 Cmd_AddCommand ("sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2914 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");
2915 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");
2916 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");
2918 Cvar_RegisterVariable (&prvm_language);
2919 Cvar_RegisterVariable (&prvm_traceqc);
2920 Cvar_RegisterVariable (&prvm_statementprofiling);
2921 Cvar_RegisterVariable (&prvm_timeprofiling);
2922 Cvar_RegisterVariable (&prvm_coverage);
2923 Cvar_RegisterVariable (&prvm_backtraceforwarnings);
2924 Cvar_RegisterVariable (&prvm_leaktest);
2925 Cvar_RegisterVariable (&prvm_leaktest_follow_targetname);
2926 Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames);
2927 Cvar_RegisterVariable (&prvm_errordump);
2928 Cvar_RegisterVariable (&prvm_breakpointdump);
2929 Cvar_RegisterVariable (&prvm_reuseedicts_startuptime);
2930 Cvar_RegisterVariable (&prvm_reuseedicts_neverinsameframe);
2932 // COMMANDLINEOPTION: PRVM: -norunaway disables the runaway loop check (it might be impossible to exit DarkPlaces if used!)
2933 prvm_runawaycheck = !COM_CheckParm("-norunaway");
2943 void PRVM_Prog_Init(prvm_prog_t *prog)
2945 PRVM_Prog_Reset(prog);
2946 prog->leaktest_active = prvm_leaktest.integer != 0;
2949 // LordHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
2950 unsigned int PRVM_EDICT_NUM_ERROR(prvm_prog_t *prog, unsigned int n, const char *filename, int fileline)
2952 prog->error_cmd("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", prog->name, n, filename, fileline);
2956 #define PRVM_KNOWNSTRINGBASE 0x40000000
2958 const char *PRVM_GetString(prvm_prog_t *prog, int num)
2963 VM_Warning(prog, "PRVM_GetString: Invalid string offset (%i < 0)\n", num);
2966 else if (num < prog->stringssize)
2968 // constant string from progs.dat
2969 return prog->strings + num;
2971 else if (num <= prog->stringssize + prog->tempstringsbuf.maxsize)
2973 // tempstring returned by engine to QC (becomes invalid after returning to engine)
2974 num -= prog->stringssize;
2975 if (num < prog->tempstringsbuf.cursize)
2976 return (char *)prog->tempstringsbuf.data + num;
2979 VM_Warning(prog, "PRVM_GetString: Invalid temp-string offset (%i >= %i prog->tempstringsbuf.cursize)\n", num, prog->tempstringsbuf.cursize);
2983 else if (num & PRVM_KNOWNSTRINGBASE)
2986 num = num - PRVM_KNOWNSTRINGBASE;
2987 if (num >= 0 && num < prog->numknownstrings)
2989 if (!prog->knownstrings[num])
2991 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
2994 return prog->knownstrings[num];
2998 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
3004 // invalid string offset
3005 VM_Warning(prog, "PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
3010 const char *PRVM_ChangeEngineString(prvm_prog_t *prog, int i, const char *s)
3013 i = i - PRVM_KNOWNSTRINGBASE;
3014 if(i < 0 || i >= prog->numknownstrings)
3015 prog->error_cmd("PRVM_ChangeEngineString: s is not an engine string");
3016 old = prog->knownstrings[i];
3017 prog->knownstrings[i] = s;
3021 int PRVM_SetEngineString(prvm_prog_t *prog, const char *s)
3026 if (s >= prog->strings && s <= prog->strings + prog->stringssize)
3027 prog->error_cmd("PRVM_SetEngineString: s in prog->strings area");
3028 // if it's in the tempstrings area, use a reserved range
3029 // (otherwise we'd get millions of useless string offsets cluttering the database)
3030 if (s >= (char *)prog->tempstringsbuf.data && s < (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.maxsize)
3031 return prog->stringssize + (s - (char *)prog->tempstringsbuf.data);
3032 // see if it's a known string address
3033 for (i = 0;i < prog->numknownstrings;i++)
3034 if (prog->knownstrings[i] == s)
3035 return PRVM_KNOWNSTRINGBASE + i;
3036 // new unknown engine string
3037 if (developer_insane.integer)
3038 Con_DPrintf("new engine string %p = \"%s\"\n", s, s);
3039 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3040 if (!prog->knownstrings[i])
3042 if (i >= prog->numknownstrings)
3044 if (i >= prog->maxknownstrings)
3046 const char **oldstrings = prog->knownstrings;
3047 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
3048 const char **oldstrings_origin = prog->knownstrings_origin;
3049 prog->maxknownstrings += 128;
3050 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3051 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
3052 if(prog->leaktest_active)
3053 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3054 if (prog->numknownstrings)
3056 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
3057 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
3058 if(prog->leaktest_active)
3059 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
3062 prog->numknownstrings++;
3064 prog->firstfreeknownstring = i + 1;
3065 prog->knownstrings[i] = s;
3066 prog->knownstrings_freeable[i] = false;
3067 if(prog->leaktest_active)
3068 prog->knownstrings_origin[i] = NULL;
3069 return PRVM_KNOWNSTRINGBASE + i;
3072 // temp string handling
3074 // all tempstrings go into this buffer consecutively, and it is reset
3075 // whenever PRVM_ExecuteProgram returns to the engine
3076 // (technically each PRVM_ExecuteProgram call saves the cursize value and
3077 // restores it on return, so multiple recursive calls can share the same
3079 // the buffer size is automatically grown as needed
3081 int PRVM_SetTempString(prvm_prog_t *prog, const char *s)
3087 size = (int)strlen(s) + 1;
3088 if (developer_insane.integer)
3089 Con_DPrintf("PRVM_SetTempString: cursize %i, size %i\n", prog->tempstringsbuf.cursize, size);
3090 if (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
3092 sizebuf_t old = prog->tempstringsbuf;
3093 if (prog->tempstringsbuf.cursize + size >= 1<<28)
3094 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);
3095 prog->tempstringsbuf.maxsize = max(prog->tempstringsbuf.maxsize, 65536);
3096 while (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
3097 prog->tempstringsbuf.maxsize *= 2;
3098 if (prog->tempstringsbuf.maxsize != old.maxsize || prog->tempstringsbuf.data == NULL)
3100 Con_DPrintf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, prog->tempstringsbuf.maxsize/1024);
3101 prog->tempstringsbuf.data = (unsigned char *) Mem_Alloc(prog->progs_mempool, prog->tempstringsbuf.maxsize);
3105 memcpy(prog->tempstringsbuf.data, old.data, old.cursize);
3110 t = (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.cursize;
3112 prog->tempstringsbuf.cursize += size;
3113 return PRVM_SetEngineString(prog, t);
3116 int PRVM_AllocString(prvm_prog_t *prog, size_t bufferlength, char **pointer)
3125 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3126 if (!prog->knownstrings[i])
3128 if (i >= prog->numknownstrings)
3130 if (i >= prog->maxknownstrings)
3132 const char **oldstrings = prog->knownstrings;
3133 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
3134 const char **oldstrings_origin = prog->knownstrings_origin;
3135 prog->maxknownstrings += 128;
3136 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3137 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
3138 if(prog->leaktest_active)
3139 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3140 if (prog->numknownstrings)
3142 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
3143 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
3144 if(prog->leaktest_active)
3145 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
3148 Mem_Free((char **)oldstrings);
3149 if (oldstrings_freeable)
3150 Mem_Free((unsigned char *)oldstrings_freeable);
3151 if (oldstrings_origin)
3152 Mem_Free((char **)oldstrings_origin);
3154 prog->numknownstrings++;
3156 prog->firstfreeknownstring = i + 1;
3157 prog->knownstrings[i] = (char *)PRVM_Alloc(bufferlength);
3158 prog->knownstrings_freeable[i] = true;
3159 if(prog->leaktest_active)
3160 prog->knownstrings_origin[i] = PRVM_AllocationOrigin(prog);
3162 *pointer = (char *)(prog->knownstrings[i]);
3163 return PRVM_KNOWNSTRINGBASE + i;
3166 void PRVM_FreeString(prvm_prog_t *prog, int num)
3169 prog->error_cmd("PRVM_FreeString: attempt to free a NULL string");
3170 else if (num >= 0 && num < prog->stringssize)
3171 prog->error_cmd("PRVM_FreeString: attempt to free a constant string");
3172 else if (num >= PRVM_KNOWNSTRINGBASE && num < PRVM_KNOWNSTRINGBASE + prog->numknownstrings)
3174 num = num - PRVM_KNOWNSTRINGBASE;
3175 if (!prog->knownstrings[num])
3176 prog->error_cmd("PRVM_FreeString: attempt to free a non-existent or already freed string");
3177 if (!prog->knownstrings_freeable[num])
3178 prog->error_cmd("PRVM_FreeString: attempt to free a string owned by the engine");
3179 PRVM_Free((char *)prog->knownstrings[num]);
3180 if(prog->leaktest_active)
3181 if(prog->knownstrings_origin[num])
3182 PRVM_Free((char *)prog->knownstrings_origin[num]);
3183 prog->knownstrings[num] = NULL;
3184 prog->knownstrings_freeable[num] = false;
3185 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
3188 prog->error_cmd("PRVM_FreeString: invalid string offset %i", num);
3191 static qboolean PRVM_IsStringReferenced(prvm_prog_t *prog, string_t string)
3195 for (i = 0;i < prog->numglobaldefs;i++)
3197 ddef_t *d = &prog->globaldefs[i];
3198 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3200 if(string == PRVM_GLOBALFIELDSTRING(d->ofs))
3204 for(j = 0; j < prog->num_edicts; ++j)
3206 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3207 if (ed->priv.required->free)
3209 for (i=0; i<prog->numfielddefs; ++i)
3211 ddef_t *d = &prog->fielddefs[i];
3212 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3214 if(string == PRVM_EDICTFIELDSTRING(ed, d->ofs))
3222 static qboolean PRVM_IsEdictRelevant(prvm_prog_t *prog, prvm_edict_t *edict)
3226 if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
3227 return true; // world or clients
3228 if (edict->priv.required->freetime <= prog->inittime)
3229 return true; // created during startup
3230 if (prog == SVVM_prog)
3232 if(PRVM_serveredictfloat(edict, solid)) // can block other stuff, or is a trigger?
3234 if(PRVM_serveredictfloat(edict, modelindex)) // visible ent?
3236 if(PRVM_serveredictfloat(edict, effects)) // particle effect?
3238 if(PRVM_serveredictfunction(edict, think)) // has a think function?
3239 if(PRVM_serveredictfloat(edict, nextthink) > 0) // that actually will eventually run?
3241 if(PRVM_serveredictfloat(edict, takedamage))
3243 if(*prvm_leaktest_ignore_classnames.string)
3245 if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_serveredictstring(edict, classname)))))
3249 else if (prog == CLVM_prog)
3251 // TODO someone add more stuff here
3252 if(PRVM_clientedictfloat(edict, entnum)) // csqc networked
3254 if(PRVM_clientedictfloat(edict, modelindex)) // visible ent?
3256 if(PRVM_clientedictfloat(edict, effects)) // particle effect?
3258 if(PRVM_clientedictfunction(edict, think)) // has a think function?
3259 if(PRVM_clientedictfloat(edict, nextthink) > 0) // that actually will eventually run?
3261 if(*prvm_leaktest_ignore_classnames.string)
3263 if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_clientedictstring(edict, classname)))))
3269 // menu prog does not have classnames
3274 static qboolean PRVM_IsEdictReferenced(prvm_prog_t *prog, prvm_edict_t *edict, int mark)
3277 int edictnum = PRVM_NUM_FOR_EDICT(edict);
3278 const char *targetname = NULL;
3280 if (prog == SVVM_prog && prvm_leaktest_follow_targetname.integer)
3281 targetname = PRVM_GetString(prog, PRVM_serveredictstring(edict, targetname));
3284 if(!*targetname) // ""
3287 for(j = 0; j < prog->num_edicts; ++j)
3289 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3290 if (ed->priv.required->mark < mark)
3296 const char *target = PRVM_GetString(prog, PRVM_serveredictstring(ed, target));
3298 if(!strcmp(target, targetname))
3301 for (i=0; i<prog->numfielddefs; ++i)
3303 ddef_t *d = &prog->fielddefs[i];
3304 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3306 if(edictnum == PRVM_EDICTFIELDEDICT(ed, d->ofs))
3314 static void PRVM_MarkReferencedEdicts(prvm_prog_t *prog)
3320 // Stage 1: world, all entities that are relevant, and all entities that are referenced by globals.
3322 for(j = 0; j < prog->num_edicts; ++j)
3324 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3325 if(ed->priv.required->free)
3327 ed->priv.required->mark = PRVM_IsEdictRelevant(prog, ed) ? stage : 0;
3329 for (i = 0;i < prog->numglobaldefs;i++)
3331 ddef_t *d = &prog->globaldefs[i];
3333 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3335 j = PRVM_GLOBALFIELDEDICT(d->ofs);
3336 if (i < 0 || j >= prog->max_edicts) {
3337 Con_Printf("Invalid entity reference from global %s.\n", PRVM_GetString(prog, d->s_name));
3340 ed = PRVM_EDICT_NUM(j);;
3341 ed->priv.required->mark = stage;
3344 // Future stages: all entities that are referenced by an entity of the previous stage.
3348 for(j = 0; j < prog->num_edicts; ++j)
3350 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3351 if(ed->priv.required->free)
3353 if(ed->priv.required->mark)
3355 if(PRVM_IsEdictReferenced(prog, ed, stage))
3357 ed->priv.required->mark = stage + 1;
3364 Con_DPrintf("leak check used %d stages to find all references\n", stage);
3367 void PRVM_LeakTest(prvm_prog_t *prog)
3370 qboolean leaked = false;
3372 if(!prog->leaktest_active)
3376 for (i = 0; i < prog->numknownstrings; ++i)
3378 if(prog->knownstrings[i])
3379 if(prog->knownstrings_freeable[i])
3380 if(prog->knownstrings_origin[i])
3381 if(!PRVM_IsStringReferenced(prog, PRVM_KNOWNSTRINGBASE + i))
3383 Con_Printf("Unreferenced string found!\n Value: %s\n Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
3389 PRVM_MarkReferencedEdicts(prog);
3390 for(j = 0; j < prog->num_edicts; ++j)
3392 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3393 if(ed->priv.required->free)
3395 if(!ed->priv.required->mark)
3396 if(ed->priv.required->allocation_origin)
3398 Con_Printf("Unreferenced edict found!\n Allocated at: %s\n", ed->priv.required->allocation_origin);
3399 PRVM_ED_Print(prog, ed, NULL);
3404 ed->priv.required->mark = 0; // clear marks again when done
3407 for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
3409 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
3411 if(stringbuffer->origin)
3413 Con_Printf("Open string buffer handle found!\n Allocated at: %s\n", stringbuffer->origin);
3418 for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
3420 if(prog->openfiles[i])
3421 if(prog->openfiles_origin[i])
3423 Con_Printf("Open file handle found!\n Allocated at: %s\n", prog->openfiles_origin[i]);
3428 for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
3430 if(prog->opensearches[i])
3431 if(prog->opensearches_origin[i])
3433 Con_Printf("Open search handle found!\n Allocated at: %s\n", prog->opensearches_origin[i]);
3439 Con_Printf("Congratulations. No leaks found.\n");