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;
1944 prog->error_cmd("PRVM_LoadProgs: there is already a %s program loaded!", prog->name );
1946 Host_LockSession(); // all progs can use the session cvar
1947 Crypto_LoadKeys(); // all progs might use the keys at init time
1951 dprograms = (dprograms_t *) data;
1955 dprograms = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
1956 if (dprograms == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
1957 prog->error_cmd("PRVM_LoadProgs: couldn't load %s for %s", filename, prog->name);
1958 // TODO bounds check header fields (e.g. numstatements), they must never go behind end of file
1960 prog->profiletime = Sys_DirtyTime();
1961 prog->starttime = realtime;
1963 Con_DPrintf("%s programs occupy %iK.\n", prog->name, (int)(filesize/1024));
1965 requiredglobalspace = 0;
1966 for (i = 0;i < numrequiredglobals;i++)
1967 requiredglobalspace += required_global[i].type == ev_vector ? 3 : 1;
1969 prog->filecrc = CRC_Block((unsigned char *)dprograms, filesize);
1971 // byte swap the header
1972 prog->progs_version = LittleLong(dprograms->version);
1973 prog->progs_crc = LittleLong(dprograms->crc);
1974 if (prog->progs_version != PROG_VERSION)
1975 prog->error_cmd("%s: %s has wrong version number (%i should be %i)", prog->name, filename, prog->progs_version, PROG_VERSION);
1976 instatements = (dstatement_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_statements));
1977 prog->progs_numstatements = LittleLong(dprograms->numstatements);
1978 inglobaldefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globaldefs));
1979 prog->progs_numglobaldefs = LittleLong(dprograms->numglobaldefs);
1980 infielddefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_fielddefs));
1981 prog->progs_numfielddefs = LittleLong(dprograms->numfielddefs);
1982 infunctions = (dfunction_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_functions));
1983 prog->progs_numfunctions = LittleLong(dprograms->numfunctions);
1984 instrings = (char *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_strings));
1985 prog->progs_numstrings = LittleLong(dprograms->numstrings);
1986 inglobals = (int *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globals));
1987 prog->progs_numglobals = LittleLong(dprograms->numglobals);
1988 prog->progs_entityfields = LittleLong(dprograms->entityfields);
1990 prog->numstatements = prog->progs_numstatements;
1991 prog->numglobaldefs = prog->progs_numglobaldefs;
1992 prog->numfielddefs = prog->progs_numfielddefs;
1993 prog->numfunctions = prog->progs_numfunctions;
1994 prog->numstrings = prog->progs_numstrings;
1995 prog->numglobals = prog->progs_numglobals;
1996 prog->entityfields = prog->progs_entityfields;
1998 if (LittleLong(dprograms->ofs_strings) + prog->progs_numstrings > (int)filesize)
1999 prog->error_cmd("%s: %s strings go past end of file", prog->name, filename);
2000 prog->strings = (char *)Mem_Alloc(prog->progs_mempool, prog->progs_numstrings);
2001 memcpy(prog->strings, instrings, prog->progs_numstrings);
2002 prog->stringssize = prog->progs_numstrings;
2004 prog->numknownstrings = 0;
2005 prog->maxknownstrings = 0;
2006 prog->knownstrings = NULL;
2007 prog->knownstrings_freeable = NULL;
2009 Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
2011 // we need to expand the globaldefs and fielddefs to include engine defs
2012 prog->globaldefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobaldefs + numrequiredglobals) * sizeof(ddef_t));
2013 prog->globals.fp = (prvm_vec_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobals + requiredglobalspace + 2) * sizeof(prvm_vec_t));
2014 // + 2 is because of an otherwise occurring overrun in RETURN instruction
2015 // when trying to return the last or second-last global
2016 // (RETURN always returns a vector, there is no RETURN_F instruction)
2017 prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numfielddefs + numrequiredfields) * sizeof(ddef_t));
2018 // we need to convert the statements to our memory format
2019 prog->statements = (mstatement_t *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(mstatement_t));
2020 // allocate space for profiling statement usage
2021 prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(*prog->statement_profile));
2022 prog->explicit_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(*prog->statement_profile));
2023 // functions need to be converted to the memory format
2024 prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs_numfunctions);
2026 for (i = 0;i < prog->progs_numfunctions;i++)
2028 prog->functions[i].first_statement = LittleLong(infunctions[i].first_statement);
2029 prog->functions[i].parm_start = LittleLong(infunctions[i].parm_start);
2030 prog->functions[i].s_name = LittleLong(infunctions[i].s_name);
2031 prog->functions[i].s_file = LittleLong(infunctions[i].s_file);
2032 prog->functions[i].numparms = LittleLong(infunctions[i].numparms);
2033 prog->functions[i].locals = LittleLong(infunctions[i].locals);
2034 memcpy(prog->functions[i].parm_size, infunctions[i].parm_size, sizeof(infunctions[i].parm_size));
2035 if(prog->functions[i].first_statement >= prog->numstatements)
2036 prog->error_cmd("PRVM_LoadProgs: out of bounds function statement (function %d) in %s", i, prog->name);
2037 // TODO bounds check parm_start, s_name, s_file, numparms, locals, parm_size
2040 // copy the globaldefs to the new globaldefs list
2041 for (i=0 ; i<prog->numglobaldefs ; i++)
2043 prog->globaldefs[i].type = LittleShort(inglobaldefs[i].type);
2044 prog->globaldefs[i].ofs = LittleShort(inglobaldefs[i].ofs);
2045 prog->globaldefs[i].s_name = LittleLong(inglobaldefs[i].s_name);
2046 // TODO bounds check ofs, s_name
2049 // append the required globals
2050 for (i = 0;i < numrequiredglobals;i++)
2052 prog->globaldefs[prog->numglobaldefs].type = required_global[i].type;
2053 prog->globaldefs[prog->numglobaldefs].ofs = prog->numglobals;
2054 prog->globaldefs[prog->numglobaldefs].s_name = PRVM_SetEngineString(prog, required_global[i].name);
2055 if (prog->globaldefs[prog->numglobaldefs].type == ev_vector)
2056 prog->numglobals += 3;
2059 prog->numglobaldefs++;
2062 // copy the progs fields to the new fields list
2063 for (i = 0;i < prog->numfielddefs;i++)
2065 prog->fielddefs[i].type = LittleShort(infielddefs[i].type);
2066 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
2067 prog->error_cmd("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", prog->name);
2068 prog->fielddefs[i].ofs = LittleShort(infielddefs[i].ofs);
2069 prog->fielddefs[i].s_name = LittleLong(infielddefs[i].s_name);
2070 // TODO bounds check ofs, s_name
2073 // append the required fields
2074 for (i = 0;i < numrequiredfields;i++)
2076 prog->fielddefs[prog->numfielddefs].type = required_field[i].type;
2077 prog->fielddefs[prog->numfielddefs].ofs = prog->entityfields;
2078 prog->fielddefs[prog->numfielddefs].s_name = PRVM_SetEngineString(prog, required_field[i].name);
2079 if (prog->fielddefs[prog->numfielddefs].type == ev_vector)
2080 prog->entityfields += 3;
2082 prog->entityfields++;
2083 prog->numfielddefs++;
2086 // LordHavoc: TODO: reorder globals to match engine struct
2087 // LordHavoc: TODO: reorder fields to match engine struct
2088 #define remapglobal(index) (index)
2089 #define remapfield(index) (index)
2092 // FIXME: LordHavoc: this uses a crude way to identify integer constants, rather than checking for matching globaldefs and checking their type
2093 for (i = 0;i < prog->progs_numglobals;i++)
2095 u.i = LittleLong(inglobals[i]);
2096 // most globals are 0, we only need to deal with the ones that are not
2099 d = u.i & 0xFF800000;
2100 if ((d == 0xFF800000) || (d == 0))
2102 // Looks like an integer (expand to int64)
2103 prog->globals.ip[remapglobal(i)] = u.i;
2107 // Looks like a float (expand to double)
2108 prog->globals.fp[remapglobal(i)] = u.f;
2113 // LordHavoc: TODO: support 32bit progs statement formats
2114 // copy, remap globals in statements, bounds check
2115 for (i = 0;i < prog->progs_numstatements;i++)
2117 op = (opcode_t)LittleShort(instatements[i].op);
2118 a = (unsigned short)LittleShort(instatements[i].a);
2119 b = (unsigned short)LittleShort(instatements[i].b);
2120 c = (unsigned short)LittleShort(instatements[i].c);
2126 if (a >= prog->progs_numglobals || b + i < 0 || b + i >= prog->progs_numstatements)
2127 prog->error_cmd("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", i, prog->name);
2128 prog->statements[i].op = op;
2129 prog->statements[i].operand[0] = remapglobal(a);
2130 prog->statements[i].operand[1] = -1;
2131 prog->statements[i].operand[2] = -1;
2132 prog->statements[i].jumpabsolute = i + b;
2136 if (a + i < 0 || a + i >= prog->progs_numstatements)
2137 prog->error_cmd("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, prog->name);
2138 prog->statements[i].op = op;
2139 prog->statements[i].operand[0] = -1;
2140 prog->statements[i].operand[1] = -1;
2141 prog->statements[i].operand[2] = -1;
2142 prog->statements[i].jumpabsolute = i + a;
2145 Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", (int)op, i, prog->name);
2147 // global global global
2182 if (a >= prog->progs_numglobals || b >= prog->progs_numglobals || c >= prog->progs_numglobals)
2183 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
2184 prog->statements[i].op = op;
2185 prog->statements[i].operand[0] = remapglobal(a);
2186 prog->statements[i].operand[1] = remapglobal(b);
2187 prog->statements[i].operand[2] = remapglobal(c);
2188 prog->statements[i].jumpabsolute = -1;
2190 // global none global
2196 if (a >= prog->progs_numglobals || c >= prog->progs_numglobals)
2197 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2198 prog->statements[i].op = op;
2199 prog->statements[i].operand[0] = remapglobal(a);
2200 prog->statements[i].operand[1] = -1;
2201 prog->statements[i].operand[2] = remapglobal(c);
2202 prog->statements[i].jumpabsolute = -1;
2218 if (a >= prog->progs_numglobals || b >= prog->progs_numglobals)
2219 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2220 prog->statements[i].op = op;
2221 prog->statements[i].operand[0] = remapglobal(a);
2222 prog->statements[i].operand[1] = remapglobal(b);
2223 prog->statements[i].operand[2] = -1;
2224 prog->statements[i].jumpabsolute = -1;
2228 if ( a < prog->progs_numglobals)
2229 if ( prog->globals.ip[remapglobal(a)] >= 0 )
2230 if ( prog->globals.ip[remapglobal(a)] < prog->progs_numfunctions )
2231 if ( prog->functions[prog->globals.ip[remapglobal(a)]].first_statement == -642 )
2232 ++prog->numexplicitcoveragestatements;
2243 if ( a >= prog->progs_numglobals)
2244 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2245 prog->statements[i].op = op;
2246 prog->statements[i].operand[0] = remapglobal(a);
2247 prog->statements[i].operand[1] = -1;
2248 prog->statements[i].operand[2] = -1;
2249 prog->statements[i].jumpabsolute = -1;
2253 if(prog->numstatements < 1)
2255 prog->error_cmd("PRVM_LoadProgs: empty program in %s", prog->name);
2257 else switch(prog->statements[prog->numstatements - 1].op)
2264 prog->error_cmd("PRVM_LoadProgs: program may fall off the edge (does not end with RETURN, GOTO or DONE) in %s", prog->name);
2268 // we're done with the file now
2270 Mem_Free(dprograms);
2273 // check required functions
2274 for(i=0 ; i < numrequiredfunc ; i++)
2275 if(PRVM_ED_FindFunction(prog, required_func[i]) == 0)
2276 prog->error_cmd("%s: %s not found in %s",prog->name, required_func[i], filename);
2278 PRVM_LoadLNO(prog, filename);
2280 PRVM_Init_Exec(prog);
2282 if(*prvm_language.string)
2283 // in CSQC we really shouldn't be able to change how stuff works... sorry for now
2284 // later idea: include a list of authorized .po file checksums with the csprogs
2286 qboolean deftrans = prog == CLVM_prog;
2287 const char *realfilename = (prog != CLVM_prog ? filename : csqc_progname.string);
2288 if(deftrans) // once we have dotranslate_ strings, ALWAYS use the opt-in method!
2290 for (i=0 ; i<prog->numglobaldefs ; i++)
2293 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2294 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2295 if(name && !strncmp(name, "dotranslate_", 12))
2302 if(!strcmp(prvm_language.string, "dump"))
2304 qfile_t *f = FS_OpenRealFile(va(vabuf, sizeof(vabuf), "%s.pot", realfilename), "w", false);
2305 Con_Printf("Dumping to %s.pot\n", realfilename);
2308 for (i=0 ; i<prog->numglobaldefs ; i++)
2311 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2312 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2313 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2315 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2316 const char *value = PRVM_GetString(prog, val->string);
2319 char buf[MAX_INPUTLINE];
2320 PRVM_PO_UnparseString(buf, value, sizeof(buf));
2321 FS_Printf(f, "msgid \"%s\"\nmsgstr \"\"\n\n", buf);
2330 po_t *po = PRVM_PO_Load(
2331 va(vabuf, sizeof(vabuf), "%s.%s.po", realfilename, prvm_language.string),
2332 va(vabuf2, sizeof(vabuf2), "common.%s.po", prvm_language.string),
2333 prog->progs_mempool);
2336 for (i=0 ; i<prog->numglobaldefs ; i++)
2339 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2340 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2341 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2343 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2344 const char *value = PRVM_GetString(prog, val->string);
2347 value = PRVM_PO_Lookup(po, value);
2349 val->string = PRVM_SetEngineString(prog, value);
2357 for (cvar = cvar_vars; cvar; cvar = cvar->next)
2358 cvar->globaldefindex[prog - prvm_prog_list] = -1;
2360 for (i=0 ; i<prog->numglobaldefs ; i++)
2363 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2364 //Con_Printf("found var %s\n", name);
2366 && !strncmp(name, "autocvar_", 9)
2367 && !(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
2370 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2371 cvar = Cvar_FindVar(name + 9);
2372 //Con_Printf("PRVM_LoadProgs: autocvar global %s in %s, processing...\n", name, prog->name);
2377 Con_DPrintf("PRVM_LoadProgs: no cvar for autocvar global %s in %s, creating...\n", name, prog->name);
2378 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2381 if((float)((int)(val->_float)) == val->_float)
2382 dpsnprintf(buf, sizeof(buf), "%i", (int)(val->_float));
2384 dpsnprintf(buf, sizeof(buf), "%.9g", val->_float);
2388 dpsnprintf(buf, sizeof(buf), "%.9g %.9g %.9g", val->vector[0], val->vector[1], val->vector[2]); value = buf;
2391 value = PRVM_GetString(prog, val->string);
2394 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2397 cvar = Cvar_Get(name + 9, value, 0, NULL);
2398 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2400 val->string = PRVM_SetEngineString(prog, cvar->string);
2401 cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2404 prog->error_cmd("PRVM_LoadProgs: could not create cvar for autocvar global %s in %s", name, prog->name);
2405 cvar->globaldefindex_progid[prog - prvm_prog_list] = prog->id;
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_progid[prog - prvm_prog_list] = prog->id;
2443 cvar->globaldefindex[prog - prvm_prog_list] = i;
2446 Con_Printf("PRVM_LoadProgs: private cvar for autocvar global %s in %s\n", name, prog->name);
2452 prog->loaded = TRUE;
2454 PRVM_UpdateBreakpoints(prog);
2456 // set flags & ddef_ts in prog
2460 PRVM_FindOffsets(prog);
2462 prog->init_cmd(prog);
2465 PRVM_MEM_Alloc(prog);
2467 // Inittime is at least the time when this function finished. However,
2468 // later events may bump it.
2469 prog->inittime = realtime;
2473 static void PRVM_Fields_f (void)
2476 int i, j, ednum, used, usedamount;
2478 char tempstring[MAX_INPUTLINE], tempstring2[260];
2488 Con_Print("no progs loaded\n");
2495 Con_Print("prvm_fields <program name>\n");
2499 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2502 counts = (int *)Mem_Alloc(tempmempool, prog->numfielddefs * sizeof(int));
2503 for (ednum = 0;ednum < prog->max_edicts;ednum++)
2505 ed = PRVM_EDICT_NUM(ednum);
2506 if (ed->priv.required->free)
2508 for (i = 1;i < prog->numfielddefs;i++)
2510 d = &prog->fielddefs[i];
2511 name = PRVM_GetString(prog, d->s_name);
2512 if (name[strlen(name)-2] == '_')
2513 continue; // skip _x, _y, _z vars
2514 val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
2515 // if the value is still all 0, skip the field
2516 for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
2518 if (val->ivector[j])
2529 for (i = 0;i < prog->numfielddefs;i++)
2531 d = &prog->fielddefs[i];
2532 name = PRVM_GetString(prog, d->s_name);
2533 if (name[strlen(name)-2] == '_')
2534 continue; // skip _x, _y, _z vars
2535 switch(d->type & ~DEF_SAVEGLOBAL)
2538 strlcat(tempstring, "string ", sizeof(tempstring));
2541 strlcat(tempstring, "entity ", sizeof(tempstring));
2544 strlcat(tempstring, "function ", sizeof(tempstring));
2547 strlcat(tempstring, "field ", sizeof(tempstring));
2550 strlcat(tempstring, "void ", sizeof(tempstring));
2553 strlcat(tempstring, "float ", sizeof(tempstring));
2556 strlcat(tempstring, "vector ", sizeof(tempstring));
2559 strlcat(tempstring, "pointer ", sizeof(tempstring));
2562 dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
2563 strlcat(tempstring, tempstring2, sizeof(tempstring));
2566 if (strlen(name) > sizeof(tempstring2)-4)
2568 memcpy (tempstring2, name, sizeof(tempstring2)-4);
2569 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
2570 tempstring2[sizeof(tempstring2)-1] = 0;
2573 strlcat(tempstring, name, sizeof(tempstring));
2574 for (j = (int)strlen(name);j < 25;j++)
2575 strlcat(tempstring, " ", sizeof(tempstring));
2576 dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
2577 strlcat(tempstring, tempstring2, sizeof(tempstring));
2578 strlcat(tempstring, "\n", sizeof(tempstring));
2579 if (strlen(tempstring) >= sizeof(tempstring)/2)
2581 Con_Print(tempstring);
2587 usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
2591 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);
2594 static void PRVM_Globals_f (void)
2598 const char *wildcard;
2604 Con_Print("no progs loaded\n");
2607 if(Cmd_Argc () < 2 || Cmd_Argc() > 3)
2609 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
2613 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2616 if( Cmd_Argc() == 3)
2617 wildcard = Cmd_Argv(2);
2621 Con_Printf("%s :", prog->name);
2623 for (i = 0;i < prog->numglobaldefs;i++)
2626 if( !matchpattern( PRVM_GetString(prog, prog->globaldefs[i].s_name), wildcard, 1) )
2631 Con_Printf("%s\n", PRVM_GetString(prog, prog->globaldefs[i].s_name));
2633 Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->numglobals, numculled, prog->numglobals * 4);
2641 static void PRVM_Global_f(void)
2645 char valuebuf[MAX_INPUTLINE];
2646 if( Cmd_Argc() != 3 ) {
2647 Con_Printf( "prvm_global <program name> <global name>\n" );
2651 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2654 global = PRVM_ED_FindGlobal( prog, Cmd_Argv(2) );
2656 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2658 Con_Printf( "%s: %s\n", Cmd_Argv(2), PRVM_ValueString( prog, (etype_t)global->type, PRVM_GLOBALFIELDVALUE(global->ofs), valuebuf, sizeof(valuebuf) ) );
2666 static void PRVM_GlobalSet_f(void)
2670 if( Cmd_Argc() != 4 ) {
2671 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2675 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2678 global = PRVM_ED_FindGlobal( prog, Cmd_Argv(2) );
2680 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2682 PRVM_ED_ParseEpair( prog, NULL, global, Cmd_Argv(3), true );
2686 ======================
2687 Break- and Watchpoints
2688 ======================
2692 char break_statement[256];
2693 char watch_global[256];
2695 char watch_field[256];
2698 static debug_data_t debug_data[PRVM_PROG_MAX];
2700 void PRVM_Breakpoint(prvm_prog_t *prog, int stack_index, const char *text)
2703 Con_Printf("PRVM_Breakpoint: %s\n", text);
2704 PRVM_PrintState(prog, stack_index);
2705 if (prvm_breakpointdump.integer)
2706 Host_Savegame_to(prog, va(vabuf, sizeof(vabuf), "breakpoint-%s.dmp", prog->name));
2709 void PRVM_Watchpoint(prvm_prog_t *prog, int stack_index, const char *text, etype_t type, prvm_eval_t *o, prvm_eval_t *n)
2711 size_t sz = sizeof(prvm_vec_t) * ((type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2712 if (memcmp(o, n, sz))
2715 char valuebuf_o[128];
2716 char valuebuf_n[128];
2717 PRVM_UglyValueString(prog, type, o, valuebuf_o, sizeof(valuebuf_o));
2718 PRVM_UglyValueString(prog, type, n, valuebuf_n, sizeof(valuebuf_n));
2719 dpsnprintf(buf, sizeof(buf), "%s: %s -> %s", text, valuebuf_o, valuebuf_n);
2720 PRVM_Breakpoint(prog, stack_index, buf);
2725 static void PRVM_UpdateBreakpoints(prvm_prog_t *prog)
2727 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2730 if (debug->break_statement[0])
2732 if (debug->break_statement[0] >= '0' && debug->break_statement[0] <= '9')
2734 prog->break_statement = atoi(debug->break_statement);
2735 prog->break_stack_index = 0;
2740 func = PRVM_ED_FindFunction (prog, debug->break_statement);
2743 Con_Printf("%s progs: no function or statement named %s to break on!\n", prog->name, debug->break_statement);
2744 prog->break_statement = -1;
2748 prog->break_statement = func->first_statement;
2749 prog->break_stack_index = 1;
2752 if (prog->break_statement >= -1)
2753 Con_Printf("%s progs: breakpoint is at statement %d\n", prog->name, prog->break_statement);
2756 prog->break_statement = -1;
2758 if (debug->watch_global[0])
2760 ddef_t *global = PRVM_ED_FindGlobal( prog, debug->watch_global );
2763 Con_Printf( "%s progs: no global named '%s' to watch!\n", prog->name, debug->watch_global );
2764 prog->watch_global_type = ev_void;
2768 size_t sz = sizeof(prvm_vec_t) * ((global->type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2769 prog->watch_global = global->ofs;
2770 prog->watch_global_type = (etype_t)global->type;
2771 memcpy(&prog->watch_global_value, PRVM_GLOBALFIELDVALUE(prog->watch_global), sz);
2773 if (prog->watch_global_type != ev_void)
2774 Con_Printf("%s progs: global watchpoint is at global index %d\n", prog->name, prog->watch_global);
2777 prog->watch_global_type = ev_void;
2779 if (debug->watch_field[0])
2781 ddef_t *field = PRVM_ED_FindField( prog, debug->watch_field );
2784 Con_Printf( "%s progs: no field named '%s' to watch!\n", prog->name, debug->watch_field );
2785 prog->watch_field_type = ev_void;
2789 size_t sz = sizeof(prvm_vec_t) * ((field->type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2790 prog->watch_edict = debug->watch_edict;
2791 prog->watch_field = field->ofs;
2792 prog->watch_field_type = (etype_t)field->type;
2793 if (prog->watch_edict < prog->num_edicts)
2794 memcpy(&prog->watch_edictfield_value, PRVM_EDICTFIELDVALUE(PRVM_EDICT_NUM(prog->watch_edict), prog->watch_field), sz);
2796 memset(&prog->watch_edictfield_value, 0, sz);
2798 if (prog->watch_edict != ev_void)
2799 Con_Printf("%s progs: edict field watchpoint is at edict %d field index %d\n", prog->name, prog->watch_edict, prog->watch_field);
2802 prog->watch_field_type = ev_void;
2805 static void PRVM_Breakpoint_f(void)
2809 if( Cmd_Argc() == 2 ) {
2810 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2813 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2814 debug->break_statement[0] = 0;
2816 PRVM_UpdateBreakpoints(prog);
2819 if( Cmd_Argc() != 3 ) {
2820 Con_Printf( "prvm_breakpoint <program name> <function name | statement>\n" );
2824 if (!(prog = PRVM_ProgFromString(Cmd_Argv(1))))
2828 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2829 strlcpy(debug->break_statement, Cmd_Argv(2), sizeof(debug->break_statement));
2831 PRVM_UpdateBreakpoints(prog);
2834 static void PRVM_GlobalWatchpoint_f(void)
2838 if( Cmd_Argc() == 2 ) {
2839 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2842 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2843 debug->watch_global[0] = 0;
2845 PRVM_UpdateBreakpoints(prog);
2848 if( Cmd_Argc() != 3 ) {
2849 Con_Printf( "prvm_globalwatchpoint <program name> <global name>\n" );
2853 if (!(prog = PRVM_ProgFromString(Cmd_Argv(1))))
2857 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2858 strlcpy(debug->watch_global, Cmd_Argv(2), sizeof(debug->watch_global));
2860 PRVM_UpdateBreakpoints(prog);
2863 static void PRVM_EdictWatchpoint_f(void)
2867 if( Cmd_Argc() == 2 ) {
2868 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2871 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2872 debug->watch_field[0] = 0;
2874 PRVM_UpdateBreakpoints(prog);
2877 if( Cmd_Argc() != 4 ) {
2878 Con_Printf( "prvm_edictwatchpoint <program name> <edict number> <field name>\n" );
2882 if (!(prog = PRVM_ProgFromString(Cmd_Argv(1))))
2886 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2887 debug->watch_edict = atoi(Cmd_Argv(2));
2888 strlcpy(debug->watch_field, Cmd_Argv(3), sizeof(debug->watch_field));
2890 PRVM_UpdateBreakpoints(prog);
2898 void PRVM_Init (void)
2900 Cmd_AddCommand ("prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2901 Cmd_AddCommand ("prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2902 Cmd_AddCommand ("prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2903 Cmd_AddCommand ("prvm_profile", PRVM_Profile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu)");
2904 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");
2905 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)");
2906 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)");
2907 Cmd_AddCommand ("prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2908 Cmd_AddCommand ("prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2909 Cmd_AddCommand ("prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2910 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)");
2911 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");
2912 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");
2913 Cmd_AddCommand ("prvm_printfunction", PRVM_PrintFunction_f, "prints a disassembly (QuakeC instructions) of the specified function in the selected VM (server, client, menu)");
2914 Cmd_AddCommand ("cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2915 Cmd_AddCommand ("menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2916 Cmd_AddCommand ("sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2918 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");
2919 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");
2920 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");
2922 Cvar_RegisterVariable (&prvm_language);
2923 Cvar_RegisterVariable (&prvm_traceqc);
2924 Cvar_RegisterVariable (&prvm_statementprofiling);
2925 Cvar_RegisterVariable (&prvm_timeprofiling);
2926 Cvar_RegisterVariable (&prvm_coverage);
2927 Cvar_RegisterVariable (&prvm_backtraceforwarnings);
2928 Cvar_RegisterVariable (&prvm_leaktest);
2929 Cvar_RegisterVariable (&prvm_leaktest_follow_targetname);
2930 Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames);
2931 Cvar_RegisterVariable (&prvm_errordump);
2932 Cvar_RegisterVariable (&prvm_breakpointdump);
2933 Cvar_RegisterVariable (&prvm_reuseedicts_startuptime);
2934 Cvar_RegisterVariable (&prvm_reuseedicts_neverinsameframe);
2936 // COMMANDLINEOPTION: PRVM: -norunaway disables the runaway loop check (it might be impossible to exit DarkPlaces if used!)
2937 prvm_runawaycheck = !COM_CheckParm("-norunaway");
2947 void PRVM_Prog_Init(prvm_prog_t *prog)
2949 PRVM_Prog_Reset(prog);
2950 prog->leaktest_active = prvm_leaktest.integer != 0;
2953 // LordHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
2954 unsigned int PRVM_EDICT_NUM_ERROR(prvm_prog_t *prog, unsigned int n, const char *filename, int fileline)
2956 prog->error_cmd("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", prog->name, n, filename, fileline);
2960 #define PRVM_KNOWNSTRINGBASE 0x40000000
2962 const char *PRVM_GetString(prvm_prog_t *prog, int num)
2967 VM_Warning(prog, "PRVM_GetString: Invalid string offset (%i < 0)\n", num);
2970 else if (num < prog->stringssize)
2972 // constant string from progs.dat
2973 return prog->strings + num;
2975 else if (num <= prog->stringssize + prog->tempstringsbuf.maxsize)
2977 // tempstring returned by engine to QC (becomes invalid after returning to engine)
2978 num -= prog->stringssize;
2979 if (num < prog->tempstringsbuf.cursize)
2980 return (char *)prog->tempstringsbuf.data + num;
2983 VM_Warning(prog, "PRVM_GetString: Invalid temp-string offset (%i >= %i prog->tempstringsbuf.cursize)\n", num, prog->tempstringsbuf.cursize);
2987 else if (num & PRVM_KNOWNSTRINGBASE)
2990 num = num - PRVM_KNOWNSTRINGBASE;
2991 if (num >= 0 && num < prog->numknownstrings)
2993 if (!prog->knownstrings[num])
2995 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
2998 return prog->knownstrings[num];
3002 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
3008 // invalid string offset
3009 VM_Warning(prog, "PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
3014 const char *PRVM_ChangeEngineString(prvm_prog_t *prog, int i, const char *s)
3017 i = i - PRVM_KNOWNSTRINGBASE;
3018 if(i < 0 || i >= prog->numknownstrings)
3019 prog->error_cmd("PRVM_ChangeEngineString: s is not an engine string");
3020 old = prog->knownstrings[i];
3021 prog->knownstrings[i] = s;
3025 int PRVM_SetEngineString(prvm_prog_t *prog, const char *s)
3030 if (s >= prog->strings && s <= prog->strings + prog->stringssize)
3031 prog->error_cmd("PRVM_SetEngineString: s in prog->strings area");
3032 // if it's in the tempstrings area, use a reserved range
3033 // (otherwise we'd get millions of useless string offsets cluttering the database)
3034 if (s >= (char *)prog->tempstringsbuf.data && s < (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.maxsize)
3035 return prog->stringssize + (s - (char *)prog->tempstringsbuf.data);
3036 // see if it's a known string address
3037 for (i = 0;i < prog->numknownstrings;i++)
3038 if (prog->knownstrings[i] == s)
3039 return PRVM_KNOWNSTRINGBASE + i;
3040 // new unknown engine string
3041 if (developer_insane.integer)
3042 Con_DPrintf("new engine string %p = \"%s\"\n", s, s);
3043 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3044 if (!prog->knownstrings[i])
3046 if (i >= prog->numknownstrings)
3048 if (i >= prog->maxknownstrings)
3050 const char **oldstrings = prog->knownstrings;
3051 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
3052 const char **oldstrings_origin = prog->knownstrings_origin;
3053 prog->maxknownstrings += 128;
3054 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3055 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
3056 if(prog->leaktest_active)
3057 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3058 if (prog->numknownstrings)
3060 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
3061 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
3062 if(prog->leaktest_active)
3063 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
3066 prog->numknownstrings++;
3068 prog->firstfreeknownstring = i + 1;
3069 prog->knownstrings[i] = s;
3070 prog->knownstrings_freeable[i] = false;
3071 if(prog->leaktest_active)
3072 prog->knownstrings_origin[i] = NULL;
3073 return PRVM_KNOWNSTRINGBASE + i;
3076 // temp string handling
3078 // all tempstrings go into this buffer consecutively, and it is reset
3079 // whenever PRVM_ExecuteProgram returns to the engine
3080 // (technically each PRVM_ExecuteProgram call saves the cursize value and
3081 // restores it on return, so multiple recursive calls can share the same
3083 // the buffer size is automatically grown as needed
3085 int PRVM_SetTempString(prvm_prog_t *prog, const char *s)
3091 size = (int)strlen(s) + 1;
3092 if (developer_insane.integer)
3093 Con_DPrintf("PRVM_SetTempString: cursize %i, size %i\n", prog->tempstringsbuf.cursize, size);
3094 if (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
3096 sizebuf_t old = prog->tempstringsbuf;
3097 if (prog->tempstringsbuf.cursize + size >= 1<<28)
3098 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);
3099 prog->tempstringsbuf.maxsize = max(prog->tempstringsbuf.maxsize, 65536);
3100 while (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
3101 prog->tempstringsbuf.maxsize *= 2;
3102 if (prog->tempstringsbuf.maxsize != old.maxsize || prog->tempstringsbuf.data == NULL)
3104 Con_DPrintf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, prog->tempstringsbuf.maxsize/1024);
3105 prog->tempstringsbuf.data = (unsigned char *) Mem_Alloc(prog->progs_mempool, prog->tempstringsbuf.maxsize);
3109 memcpy(prog->tempstringsbuf.data, old.data, old.cursize);
3114 t = (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.cursize;
3116 prog->tempstringsbuf.cursize += size;
3117 return PRVM_SetEngineString(prog, t);
3120 int PRVM_AllocString(prvm_prog_t *prog, size_t bufferlength, char **pointer)
3129 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3130 if (!prog->knownstrings[i])
3132 if (i >= prog->numknownstrings)
3134 if (i >= prog->maxknownstrings)
3136 const char **oldstrings = prog->knownstrings;
3137 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
3138 const char **oldstrings_origin = prog->knownstrings_origin;
3139 prog->maxknownstrings += 128;
3140 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3141 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
3142 if(prog->leaktest_active)
3143 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3144 if (prog->numknownstrings)
3146 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
3147 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
3148 if(prog->leaktest_active)
3149 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
3152 Mem_Free((char **)oldstrings);
3153 if (oldstrings_freeable)
3154 Mem_Free((unsigned char *)oldstrings_freeable);
3155 if (oldstrings_origin)
3156 Mem_Free((char **)oldstrings_origin);
3158 prog->numknownstrings++;
3160 prog->firstfreeknownstring = i + 1;
3161 prog->knownstrings[i] = (char *)PRVM_Alloc(bufferlength);
3162 prog->knownstrings_freeable[i] = true;
3163 if(prog->leaktest_active)
3164 prog->knownstrings_origin[i] = PRVM_AllocationOrigin(prog);
3166 *pointer = (char *)(prog->knownstrings[i]);
3167 return PRVM_KNOWNSTRINGBASE + i;
3170 void PRVM_FreeString(prvm_prog_t *prog, int num)
3173 prog->error_cmd("PRVM_FreeString: attempt to free a NULL string");
3174 else if (num >= 0 && num < prog->stringssize)
3175 prog->error_cmd("PRVM_FreeString: attempt to free a constant string");
3176 else if (num >= PRVM_KNOWNSTRINGBASE && num < PRVM_KNOWNSTRINGBASE + prog->numknownstrings)
3178 num = num - PRVM_KNOWNSTRINGBASE;
3179 if (!prog->knownstrings[num])
3180 prog->error_cmd("PRVM_FreeString: attempt to free a non-existent or already freed string");
3181 if (!prog->knownstrings_freeable[num])
3182 prog->error_cmd("PRVM_FreeString: attempt to free a string owned by the engine");
3183 PRVM_Free((char *)prog->knownstrings[num]);
3184 if(prog->leaktest_active)
3185 if(prog->knownstrings_origin[num])
3186 PRVM_Free((char *)prog->knownstrings_origin[num]);
3187 prog->knownstrings[num] = NULL;
3188 prog->knownstrings_freeable[num] = false;
3189 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
3192 prog->error_cmd("PRVM_FreeString: invalid string offset %i", num);
3195 static qboolean PRVM_IsStringReferenced(prvm_prog_t *prog, string_t string)
3199 for (i = 0;i < prog->numglobaldefs;i++)
3201 ddef_t *d = &prog->globaldefs[i];
3202 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3204 if(string == PRVM_GLOBALFIELDSTRING(d->ofs))
3208 for(j = 0; j < prog->num_edicts; ++j)
3210 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3211 if (ed->priv.required->free)
3213 for (i=0; i<prog->numfielddefs; ++i)
3215 ddef_t *d = &prog->fielddefs[i];
3216 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3218 if(string == PRVM_EDICTFIELDSTRING(ed, d->ofs))
3226 static qboolean PRVM_IsEdictRelevant(prvm_prog_t *prog, prvm_edict_t *edict)
3230 if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
3231 return true; // world or clients
3232 if (edict->priv.required->freetime <= prog->inittime)
3233 return true; // created during startup
3234 if (prog == SVVM_prog)
3236 if(PRVM_serveredictfloat(edict, solid)) // can block other stuff, or is a trigger?
3238 if(PRVM_serveredictfloat(edict, modelindex)) // visible ent?
3240 if(PRVM_serveredictfloat(edict, effects)) // particle effect?
3242 if(PRVM_serveredictfunction(edict, think)) // has a think function?
3243 if(PRVM_serveredictfloat(edict, nextthink) > 0) // that actually will eventually run?
3245 if(PRVM_serveredictfloat(edict, takedamage))
3247 if(*prvm_leaktest_ignore_classnames.string)
3249 if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_serveredictstring(edict, classname)))))
3253 else if (prog == CLVM_prog)
3255 // TODO someone add more stuff here
3256 if(PRVM_clientedictfloat(edict, entnum)) // csqc networked
3258 if(PRVM_clientedictfloat(edict, modelindex)) // visible ent?
3260 if(PRVM_clientedictfloat(edict, effects)) // particle effect?
3262 if(PRVM_clientedictfunction(edict, think)) // has a think function?
3263 if(PRVM_clientedictfloat(edict, nextthink) > 0) // that actually will eventually run?
3265 if(*prvm_leaktest_ignore_classnames.string)
3267 if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_clientedictstring(edict, classname)))))
3273 // menu prog does not have classnames
3278 static qboolean PRVM_IsEdictReferenced(prvm_prog_t *prog, prvm_edict_t *edict, int mark)
3281 int edictnum = PRVM_NUM_FOR_EDICT(edict);
3282 const char *targetname = NULL;
3284 if (prog == SVVM_prog && prvm_leaktest_follow_targetname.integer)
3285 targetname = PRVM_GetString(prog, PRVM_serveredictstring(edict, targetname));
3288 if(!*targetname) // ""
3291 for(j = 0; j < prog->num_edicts; ++j)
3293 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3294 if (ed->priv.required->mark < mark)
3300 const char *target = PRVM_GetString(prog, PRVM_serveredictstring(ed, target));
3302 if(!strcmp(target, targetname))
3305 for (i=0; i<prog->numfielddefs; ++i)
3307 ddef_t *d = &prog->fielddefs[i];
3308 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3310 if(edictnum == PRVM_EDICTFIELDEDICT(ed, d->ofs))
3318 static void PRVM_MarkReferencedEdicts(prvm_prog_t *prog)
3324 // Stage 1: world, all entities that are relevant, and all entities that are referenced by globals.
3326 for(j = 0; j < prog->num_edicts; ++j)
3328 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3329 if(ed->priv.required->free)
3331 ed->priv.required->mark = PRVM_IsEdictRelevant(prog, ed) ? stage : 0;
3333 for (i = 0;i < prog->numglobaldefs;i++)
3335 ddef_t *d = &prog->globaldefs[i];
3337 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3339 j = PRVM_GLOBALFIELDEDICT(d->ofs);
3340 if (i < 0 || j >= prog->max_edicts) {
3341 Con_Printf("Invalid entity reference from global %s.\n", PRVM_GetString(prog, d->s_name));
3344 ed = PRVM_EDICT_NUM(j);;
3345 ed->priv.required->mark = stage;
3348 // Future stages: all entities that are referenced by an entity of the previous stage.
3352 for(j = 0; j < prog->num_edicts; ++j)
3354 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3355 if(ed->priv.required->free)
3357 if(ed->priv.required->mark)
3359 if(PRVM_IsEdictReferenced(prog, ed, stage))
3361 ed->priv.required->mark = stage + 1;
3368 Con_DPrintf("leak check used %d stages to find all references\n", stage);
3371 void PRVM_LeakTest(prvm_prog_t *prog)
3374 qboolean leaked = false;
3376 if(!prog->leaktest_active)
3380 for (i = 0; i < prog->numknownstrings; ++i)
3382 if(prog->knownstrings[i])
3383 if(prog->knownstrings_freeable[i])
3384 if(prog->knownstrings_origin[i])
3385 if(!PRVM_IsStringReferenced(prog, PRVM_KNOWNSTRINGBASE + i))
3387 Con_Printf("Unreferenced string found!\n Value: %s\n Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
3393 PRVM_MarkReferencedEdicts(prog);
3394 for(j = 0; j < prog->num_edicts; ++j)
3396 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3397 if(ed->priv.required->free)
3399 if(!ed->priv.required->mark)
3400 if(ed->priv.required->allocation_origin)
3402 Con_Printf("Unreferenced edict found!\n Allocated at: %s\n", ed->priv.required->allocation_origin);
3403 PRVM_ED_Print(prog, ed, NULL);
3408 ed->priv.required->mark = 0; // clear marks again when done
3411 for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
3413 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
3415 if(stringbuffer->origin)
3417 Con_Printf("Open string buffer handle found!\n Allocated at: %s\n", stringbuffer->origin);
3422 for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
3424 if(prog->openfiles[i])
3425 if(prog->openfiles_origin[i])
3427 Con_Printf("Open file handle found!\n Allocated at: %s\n", prog->openfiles_origin[i]);
3432 for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
3434 if(prog->opensearches[i])
3435 if(prog->opensearches_origin[i])
3437 Con_Printf("Open search handle found!\n Allocated at: %s\n", prog->opensearches_origin[i]);
3443 Con_Printf("Congratulations. No leaks found.\n");