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[prog - prvm_prog_list] = i;
2407 else if((cvar->flags & CVAR_PRIVATE) == 0)
2409 // MUST BE SYNCED WITH cvar.c Cvar_Set
2412 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2415 val->_float = cvar->value;
2419 VectorClear(val->vector);
2420 for (j = 0;j < 3;j++)
2422 while (*s && ISWHITESPACE(*s))
2426 val->vector[j] = atof(s);
2427 while (!ISWHITESPACE(*s))
2434 val->string = PRVM_SetEngineString(prog, cvar->string);
2435 cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2438 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2441 cvar->globaldefindex[prog - prvm_prog_list] = i;
2444 Con_Printf("PRVM_LoadProgs: private cvar for autocvar global %s in %s\n", name, prog->name);
2450 prog->loaded = TRUE;
2452 PRVM_UpdateBreakpoints(prog);
2454 // set flags & ddef_ts in prog
2458 PRVM_FindOffsets(prog);
2460 prog->init_cmd(prog);
2463 PRVM_MEM_Alloc(prog);
2465 // Inittime is at least the time when this function finished. However,
2466 // later events may bump it.
2467 prog->inittime = realtime;
2471 static void PRVM_Fields_f (void)
2474 int i, j, ednum, used, usedamount;
2476 char tempstring[MAX_INPUTLINE], tempstring2[260];
2486 Con_Print("no progs loaded\n");
2493 Con_Print("prvm_fields <program name>\n");
2497 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2500 counts = (int *)Mem_Alloc(tempmempool, prog->numfielddefs * sizeof(int));
2501 for (ednum = 0;ednum < prog->max_edicts;ednum++)
2503 ed = PRVM_EDICT_NUM(ednum);
2504 if (ed->priv.required->free)
2506 for (i = 1;i < prog->numfielddefs;i++)
2508 d = &prog->fielddefs[i];
2509 name = PRVM_GetString(prog, d->s_name);
2510 if (name[strlen(name)-2] == '_')
2511 continue; // skip _x, _y, _z vars
2512 val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
2513 // if the value is still all 0, skip the field
2514 for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
2516 if (val->ivector[j])
2527 for (i = 0;i < prog->numfielddefs;i++)
2529 d = &prog->fielddefs[i];
2530 name = PRVM_GetString(prog, d->s_name);
2531 if (name[strlen(name)-2] == '_')
2532 continue; // skip _x, _y, _z vars
2533 switch(d->type & ~DEF_SAVEGLOBAL)
2536 strlcat(tempstring, "string ", sizeof(tempstring));
2539 strlcat(tempstring, "entity ", sizeof(tempstring));
2542 strlcat(tempstring, "function ", sizeof(tempstring));
2545 strlcat(tempstring, "field ", sizeof(tempstring));
2548 strlcat(tempstring, "void ", sizeof(tempstring));
2551 strlcat(tempstring, "float ", sizeof(tempstring));
2554 strlcat(tempstring, "vector ", sizeof(tempstring));
2557 strlcat(tempstring, "pointer ", sizeof(tempstring));
2560 dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
2561 strlcat(tempstring, tempstring2, sizeof(tempstring));
2564 if (strlen(name) > sizeof(tempstring2)-4)
2566 memcpy (tempstring2, name, sizeof(tempstring2)-4);
2567 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
2568 tempstring2[sizeof(tempstring2)-1] = 0;
2571 strlcat(tempstring, name, sizeof(tempstring));
2572 for (j = (int)strlen(name);j < 25;j++)
2573 strlcat(tempstring, " ", sizeof(tempstring));
2574 dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
2575 strlcat(tempstring, tempstring2, sizeof(tempstring));
2576 strlcat(tempstring, "\n", sizeof(tempstring));
2577 if (strlen(tempstring) >= sizeof(tempstring)/2)
2579 Con_Print(tempstring);
2585 usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
2589 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);
2592 static void PRVM_Globals_f (void)
2596 const char *wildcard;
2602 Con_Print("no progs loaded\n");
2605 if(Cmd_Argc () < 2 || Cmd_Argc() > 3)
2607 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
2611 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2614 if( Cmd_Argc() == 3)
2615 wildcard = Cmd_Argv(2);
2619 Con_Printf("%s :", prog->name);
2621 for (i = 0;i < prog->numglobaldefs;i++)
2624 if( !matchpattern( PRVM_GetString(prog, prog->globaldefs[i].s_name), wildcard, 1) )
2629 Con_Printf("%s\n", PRVM_GetString(prog, prog->globaldefs[i].s_name));
2631 Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->numglobals, numculled, prog->numglobals * 4);
2639 static void PRVM_Global_f(void)
2643 char valuebuf[MAX_INPUTLINE];
2644 if( Cmd_Argc() != 3 ) {
2645 Con_Printf( "prvm_global <program name> <global name>\n" );
2649 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2652 global = PRVM_ED_FindGlobal( prog, Cmd_Argv(2) );
2654 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2656 Con_Printf( "%s: %s\n", Cmd_Argv(2), PRVM_ValueString( prog, (etype_t)global->type, PRVM_GLOBALFIELDVALUE(global->ofs), valuebuf, sizeof(valuebuf) ) );
2664 static void PRVM_GlobalSet_f(void)
2668 if( Cmd_Argc() != 4 ) {
2669 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2673 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2676 global = PRVM_ED_FindGlobal( prog, Cmd_Argv(2) );
2678 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2680 PRVM_ED_ParseEpair( prog, NULL, global, Cmd_Argv(3), true );
2684 ======================
2685 Break- and Watchpoints
2686 ======================
2690 char break_statement[256];
2691 char watch_global[256];
2693 char watch_field[256];
2696 static debug_data_t debug_data[PRVM_PROG_MAX];
2698 void PRVM_Breakpoint(prvm_prog_t *prog, int stack_index, const char *text)
2701 Con_Printf("PRVM_Breakpoint: %s\n", text);
2702 PRVM_PrintState(prog, stack_index);
2703 if (prvm_breakpointdump.integer)
2704 Host_Savegame_to(prog, va(vabuf, sizeof(vabuf), "breakpoint-%s.dmp", prog->name));
2707 void PRVM_Watchpoint(prvm_prog_t *prog, int stack_index, const char *text, etype_t type, prvm_eval_t *o, prvm_eval_t *n)
2709 size_t sz = sizeof(prvm_vec_t) * ((type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2710 if (memcmp(o, n, sz))
2713 char valuebuf_o[128];
2714 char valuebuf_n[128];
2715 PRVM_UglyValueString(prog, type, o, valuebuf_o, sizeof(valuebuf_o));
2716 PRVM_UglyValueString(prog, type, n, valuebuf_n, sizeof(valuebuf_n));
2717 dpsnprintf(buf, sizeof(buf), "%s: %s -> %s", text, valuebuf_o, valuebuf_n);
2718 PRVM_Breakpoint(prog, stack_index, buf);
2723 static void PRVM_UpdateBreakpoints(prvm_prog_t *prog)
2725 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2728 if (debug->break_statement[0])
2730 if (debug->break_statement[0] >= '0' && debug->break_statement[0] <= '9')
2732 prog->break_statement = atoi(debug->break_statement);
2733 prog->break_stack_index = 0;
2738 func = PRVM_ED_FindFunction (prog, debug->break_statement);
2741 Con_Printf("%s progs: no function or statement named %s to break on!\n", prog->name, debug->break_statement);
2742 prog->break_statement = -1;
2746 prog->break_statement = func->first_statement;
2747 prog->break_stack_index = 1;
2750 if (prog->break_statement >= -1)
2751 Con_Printf("%s progs: breakpoint is at statement %d\n", prog->name, prog->break_statement);
2754 prog->break_statement = -1;
2756 if (debug->watch_global[0])
2758 ddef_t *global = PRVM_ED_FindGlobal( prog, debug->watch_global );
2761 Con_Printf( "%s progs: no global named '%s' to watch!\n", prog->name, debug->watch_global );
2762 prog->watch_global_type = ev_void;
2766 size_t sz = sizeof(prvm_vec_t) * ((global->type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2767 prog->watch_global = global->ofs;
2768 prog->watch_global_type = (etype_t)global->type;
2769 memcpy(&prog->watch_global_value, PRVM_GLOBALFIELDVALUE(prog->watch_global), sz);
2771 if (prog->watch_global_type != ev_void)
2772 Con_Printf("%s progs: global watchpoint is at global index %d\n", prog->name, prog->watch_global);
2775 prog->watch_global_type = ev_void;
2777 if (debug->watch_field[0])
2779 ddef_t *field = PRVM_ED_FindField( prog, debug->watch_field );
2782 Con_Printf( "%s progs: no field named '%s' to watch!\n", prog->name, debug->watch_field );
2783 prog->watch_field_type = ev_void;
2787 size_t sz = sizeof(prvm_vec_t) * ((field->type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2788 prog->watch_edict = debug->watch_edict;
2789 prog->watch_field = field->ofs;
2790 prog->watch_field_type = (etype_t)field->type;
2791 if (prog->watch_edict < prog->num_edicts)
2792 memcpy(&prog->watch_edictfield_value, PRVM_EDICTFIELDVALUE(PRVM_EDICT_NUM(prog->watch_edict), prog->watch_field), sz);
2794 memset(&prog->watch_edictfield_value, 0, sz);
2796 if (prog->watch_edict != ev_void)
2797 Con_Printf("%s progs: edict field watchpoint is at edict %d field index %d\n", prog->name, prog->watch_edict, prog->watch_field);
2800 prog->watch_field_type = ev_void;
2803 static void PRVM_Breakpoint_f(void)
2807 if( Cmd_Argc() == 2 ) {
2808 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2811 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2812 debug->break_statement[0] = 0;
2814 PRVM_UpdateBreakpoints(prog);
2817 if( Cmd_Argc() != 3 ) {
2818 Con_Printf( "prvm_breakpoint <program name> <function name | statement>\n" );
2822 if (!(prog = PRVM_ProgFromString(Cmd_Argv(1))))
2826 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2827 strlcpy(debug->break_statement, Cmd_Argv(2), sizeof(debug->break_statement));
2829 PRVM_UpdateBreakpoints(prog);
2832 static void PRVM_GlobalWatchpoint_f(void)
2836 if( Cmd_Argc() == 2 ) {
2837 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2840 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2841 debug->watch_global[0] = 0;
2843 PRVM_UpdateBreakpoints(prog);
2846 if( Cmd_Argc() != 3 ) {
2847 Con_Printf( "prvm_globalwatchpoint <program name> <global name>\n" );
2851 if (!(prog = PRVM_ProgFromString(Cmd_Argv(1))))
2855 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2856 strlcpy(debug->watch_global, Cmd_Argv(2), sizeof(debug->watch_global));
2858 PRVM_UpdateBreakpoints(prog);
2861 static void PRVM_EdictWatchpoint_f(void)
2865 if( Cmd_Argc() == 2 ) {
2866 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2869 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2870 debug->watch_field[0] = 0;
2872 PRVM_UpdateBreakpoints(prog);
2875 if( Cmd_Argc() != 4 ) {
2876 Con_Printf( "prvm_edictwatchpoint <program name> <edict number> <field name>\n" );
2880 if (!(prog = PRVM_ProgFromString(Cmd_Argv(1))))
2884 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2885 debug->watch_edict = atoi(Cmd_Argv(2));
2886 strlcpy(debug->watch_field, Cmd_Argv(3), sizeof(debug->watch_field));
2888 PRVM_UpdateBreakpoints(prog);
2896 void PRVM_Init (void)
2898 Cmd_AddCommand ("prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2899 Cmd_AddCommand ("prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2900 Cmd_AddCommand ("prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2901 Cmd_AddCommand ("prvm_profile", PRVM_Profile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu)");
2902 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");
2903 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)");
2904 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)");
2905 Cmd_AddCommand ("prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2906 Cmd_AddCommand ("prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2907 Cmd_AddCommand ("prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2908 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)");
2909 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");
2910 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");
2911 Cmd_AddCommand ("prvm_printfunction", PRVM_PrintFunction_f, "prints a disassembly (QuakeC instructions) of the specified function in the selected VM (server, client, menu)");
2912 Cmd_AddCommand ("cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2913 Cmd_AddCommand ("menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2914 Cmd_AddCommand ("sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2916 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");
2917 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");
2918 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");
2920 Cvar_RegisterVariable (&prvm_language);
2921 Cvar_RegisterVariable (&prvm_traceqc);
2922 Cvar_RegisterVariable (&prvm_statementprofiling);
2923 Cvar_RegisterVariable (&prvm_timeprofiling);
2924 Cvar_RegisterVariable (&prvm_coverage);
2925 Cvar_RegisterVariable (&prvm_backtraceforwarnings);
2926 Cvar_RegisterVariable (&prvm_leaktest);
2927 Cvar_RegisterVariable (&prvm_leaktest_follow_targetname);
2928 Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames);
2929 Cvar_RegisterVariable (&prvm_errordump);
2930 Cvar_RegisterVariable (&prvm_breakpointdump);
2931 Cvar_RegisterVariable (&prvm_reuseedicts_startuptime);
2932 Cvar_RegisterVariable (&prvm_reuseedicts_neverinsameframe);
2934 // COMMANDLINEOPTION: PRVM: -norunaway disables the runaway loop check (it might be impossible to exit DarkPlaces if used!)
2935 prvm_runawaycheck = !COM_CheckParm("-norunaway");
2945 void PRVM_Prog_Init(prvm_prog_t *prog)
2947 PRVM_Prog_Reset(prog);
2948 prog->leaktest_active = prvm_leaktest.integer != 0;
2951 // LordHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
2952 unsigned int PRVM_EDICT_NUM_ERROR(prvm_prog_t *prog, unsigned int n, const char *filename, int fileline)
2954 prog->error_cmd("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", prog->name, n, filename, fileline);
2958 #define PRVM_KNOWNSTRINGBASE 0x40000000
2960 const char *PRVM_GetString(prvm_prog_t *prog, int num)
2965 VM_Warning(prog, "PRVM_GetString: Invalid string offset (%i < 0)\n", num);
2968 else if (num < prog->stringssize)
2970 // constant string from progs.dat
2971 return prog->strings + num;
2973 else if (num <= prog->stringssize + prog->tempstringsbuf.maxsize)
2975 // tempstring returned by engine to QC (becomes invalid after returning to engine)
2976 num -= prog->stringssize;
2977 if (num < prog->tempstringsbuf.cursize)
2978 return (char *)prog->tempstringsbuf.data + num;
2981 VM_Warning(prog, "PRVM_GetString: Invalid temp-string offset (%i >= %i prog->tempstringsbuf.cursize)\n", num, prog->tempstringsbuf.cursize);
2985 else if (num & PRVM_KNOWNSTRINGBASE)
2988 num = num - PRVM_KNOWNSTRINGBASE;
2989 if (num >= 0 && num < prog->numknownstrings)
2991 if (!prog->knownstrings[num])
2993 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
2996 return prog->knownstrings[num];
3000 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
3006 // invalid string offset
3007 VM_Warning(prog, "PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
3012 const char *PRVM_ChangeEngineString(prvm_prog_t *prog, int i, const char *s)
3015 i = i - PRVM_KNOWNSTRINGBASE;
3016 if(i < 0 || i >= prog->numknownstrings)
3017 prog->error_cmd("PRVM_ChangeEngineString: s is not an engine string");
3018 old = prog->knownstrings[i];
3019 prog->knownstrings[i] = s;
3023 int PRVM_SetEngineString(prvm_prog_t *prog, const char *s)
3028 if (s >= prog->strings && s <= prog->strings + prog->stringssize)
3029 prog->error_cmd("PRVM_SetEngineString: s in prog->strings area");
3030 // if it's in the tempstrings area, use a reserved range
3031 // (otherwise we'd get millions of useless string offsets cluttering the database)
3032 if (s >= (char *)prog->tempstringsbuf.data && s < (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.maxsize)
3033 return prog->stringssize + (s - (char *)prog->tempstringsbuf.data);
3034 // see if it's a known string address
3035 for (i = 0;i < prog->numknownstrings;i++)
3036 if (prog->knownstrings[i] == s)
3037 return PRVM_KNOWNSTRINGBASE + i;
3038 // new unknown engine string
3039 if (developer_insane.integer)
3040 Con_DPrintf("new engine string %p = \"%s\"\n", s, s);
3041 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3042 if (!prog->knownstrings[i])
3044 if (i >= prog->numknownstrings)
3046 if (i >= prog->maxknownstrings)
3048 const char **oldstrings = prog->knownstrings;
3049 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
3050 const char **oldstrings_origin = prog->knownstrings_origin;
3051 prog->maxknownstrings += 128;
3052 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3053 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
3054 if(prog->leaktest_active)
3055 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3056 if (prog->numknownstrings)
3058 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
3059 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
3060 if(prog->leaktest_active)
3061 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
3064 prog->numknownstrings++;
3066 prog->firstfreeknownstring = i + 1;
3067 prog->knownstrings[i] = s;
3068 prog->knownstrings_freeable[i] = false;
3069 if(prog->leaktest_active)
3070 prog->knownstrings_origin[i] = NULL;
3071 return PRVM_KNOWNSTRINGBASE + i;
3074 // temp string handling
3076 // all tempstrings go into this buffer consecutively, and it is reset
3077 // whenever PRVM_ExecuteProgram returns to the engine
3078 // (technically each PRVM_ExecuteProgram call saves the cursize value and
3079 // restores it on return, so multiple recursive calls can share the same
3081 // the buffer size is automatically grown as needed
3083 int PRVM_SetTempString(prvm_prog_t *prog, const char *s)
3089 size = (int)strlen(s) + 1;
3090 if (developer_insane.integer)
3091 Con_DPrintf("PRVM_SetTempString: cursize %i, size %i\n", prog->tempstringsbuf.cursize, size);
3092 if (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
3094 sizebuf_t old = prog->tempstringsbuf;
3095 if (prog->tempstringsbuf.cursize + size >= 1<<28)
3096 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);
3097 prog->tempstringsbuf.maxsize = max(prog->tempstringsbuf.maxsize, 65536);
3098 while (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
3099 prog->tempstringsbuf.maxsize *= 2;
3100 if (prog->tempstringsbuf.maxsize != old.maxsize || prog->tempstringsbuf.data == NULL)
3102 Con_DPrintf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, prog->tempstringsbuf.maxsize/1024);
3103 prog->tempstringsbuf.data = (unsigned char *) Mem_Alloc(prog->progs_mempool, prog->tempstringsbuf.maxsize);
3107 memcpy(prog->tempstringsbuf.data, old.data, old.cursize);
3112 t = (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.cursize;
3114 prog->tempstringsbuf.cursize += size;
3115 return PRVM_SetEngineString(prog, t);
3118 int PRVM_AllocString(prvm_prog_t *prog, size_t bufferlength, char **pointer)
3127 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3128 if (!prog->knownstrings[i])
3130 if (i >= prog->numknownstrings)
3132 if (i >= prog->maxknownstrings)
3134 const char **oldstrings = prog->knownstrings;
3135 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
3136 const char **oldstrings_origin = prog->knownstrings_origin;
3137 prog->maxknownstrings += 128;
3138 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3139 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
3140 if(prog->leaktest_active)
3141 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3142 if (prog->numknownstrings)
3144 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
3145 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
3146 if(prog->leaktest_active)
3147 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
3150 Mem_Free((char **)oldstrings);
3151 if (oldstrings_freeable)
3152 Mem_Free((unsigned char *)oldstrings_freeable);
3153 if (oldstrings_origin)
3154 Mem_Free((char **)oldstrings_origin);
3156 prog->numknownstrings++;
3158 prog->firstfreeknownstring = i + 1;
3159 prog->knownstrings[i] = (char *)PRVM_Alloc(bufferlength);
3160 prog->knownstrings_freeable[i] = true;
3161 if(prog->leaktest_active)
3162 prog->knownstrings_origin[i] = PRVM_AllocationOrigin(prog);
3164 *pointer = (char *)(prog->knownstrings[i]);
3165 return PRVM_KNOWNSTRINGBASE + i;
3168 void PRVM_FreeString(prvm_prog_t *prog, int num)
3171 prog->error_cmd("PRVM_FreeString: attempt to free a NULL string");
3172 else if (num >= 0 && num < prog->stringssize)
3173 prog->error_cmd("PRVM_FreeString: attempt to free a constant string");
3174 else if (num >= PRVM_KNOWNSTRINGBASE && num < PRVM_KNOWNSTRINGBASE + prog->numknownstrings)
3176 num = num - PRVM_KNOWNSTRINGBASE;
3177 if (!prog->knownstrings[num])
3178 prog->error_cmd("PRVM_FreeString: attempt to free a non-existent or already freed string");
3179 if (!prog->knownstrings_freeable[num])
3180 prog->error_cmd("PRVM_FreeString: attempt to free a string owned by the engine");
3181 PRVM_Free((char *)prog->knownstrings[num]);
3182 if(prog->leaktest_active)
3183 if(prog->knownstrings_origin[num])
3184 PRVM_Free((char *)prog->knownstrings_origin[num]);
3185 prog->knownstrings[num] = NULL;
3186 prog->knownstrings_freeable[num] = false;
3187 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
3190 prog->error_cmd("PRVM_FreeString: invalid string offset %i", num);
3193 static qboolean PRVM_IsStringReferenced(prvm_prog_t *prog, string_t string)
3197 for (i = 0;i < prog->numglobaldefs;i++)
3199 ddef_t *d = &prog->globaldefs[i];
3200 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3202 if(string == PRVM_GLOBALFIELDSTRING(d->ofs))
3206 for(j = 0; j < prog->num_edicts; ++j)
3208 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3209 if (ed->priv.required->free)
3211 for (i=0; i<prog->numfielddefs; ++i)
3213 ddef_t *d = &prog->fielddefs[i];
3214 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3216 if(string == PRVM_EDICTFIELDSTRING(ed, d->ofs))
3224 static qboolean PRVM_IsEdictRelevant(prvm_prog_t *prog, prvm_edict_t *edict)
3228 if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
3229 return true; // world or clients
3230 if (edict->priv.required->freetime <= prog->inittime)
3231 return true; // created during startup
3232 if (prog == SVVM_prog)
3234 if(PRVM_serveredictfloat(edict, solid)) // can block other stuff, or is a trigger?
3236 if(PRVM_serveredictfloat(edict, modelindex)) // visible ent?
3238 if(PRVM_serveredictfloat(edict, effects)) // particle effect?
3240 if(PRVM_serveredictfunction(edict, think)) // has a think function?
3241 if(PRVM_serveredictfloat(edict, nextthink) > 0) // that actually will eventually run?
3243 if(PRVM_serveredictfloat(edict, takedamage))
3245 if(*prvm_leaktest_ignore_classnames.string)
3247 if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_serveredictstring(edict, classname)))))
3251 else if (prog == CLVM_prog)
3253 // TODO someone add more stuff here
3254 if(PRVM_clientedictfloat(edict, entnum)) // csqc networked
3256 if(PRVM_clientedictfloat(edict, modelindex)) // visible ent?
3258 if(PRVM_clientedictfloat(edict, effects)) // particle effect?
3260 if(PRVM_clientedictfunction(edict, think)) // has a think function?
3261 if(PRVM_clientedictfloat(edict, nextthink) > 0) // that actually will eventually run?
3263 if(*prvm_leaktest_ignore_classnames.string)
3265 if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_clientedictstring(edict, classname)))))
3271 // menu prog does not have classnames
3276 static qboolean PRVM_IsEdictReferenced(prvm_prog_t *prog, prvm_edict_t *edict, int mark)
3279 int edictnum = PRVM_NUM_FOR_EDICT(edict);
3280 const char *targetname = NULL;
3282 if (prog == SVVM_prog && prvm_leaktest_follow_targetname.integer)
3283 targetname = PRVM_GetString(prog, PRVM_serveredictstring(edict, targetname));
3286 if(!*targetname) // ""
3289 for(j = 0; j < prog->num_edicts; ++j)
3291 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3292 if (ed->priv.required->mark < mark)
3298 const char *target = PRVM_GetString(prog, PRVM_serveredictstring(ed, target));
3300 if(!strcmp(target, targetname))
3303 for (i=0; i<prog->numfielddefs; ++i)
3305 ddef_t *d = &prog->fielddefs[i];
3306 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3308 if(edictnum == PRVM_EDICTFIELDEDICT(ed, d->ofs))
3316 static void PRVM_MarkReferencedEdicts(prvm_prog_t *prog)
3322 // Stage 1: world, all entities that are relevant, and all entities that are referenced by globals.
3324 for(j = 0; j < prog->num_edicts; ++j)
3326 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3327 if(ed->priv.required->free)
3329 ed->priv.required->mark = PRVM_IsEdictRelevant(prog, ed) ? stage : 0;
3331 for (i = 0;i < prog->numglobaldefs;i++)
3333 ddef_t *d = &prog->globaldefs[i];
3335 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3337 j = PRVM_GLOBALFIELDEDICT(d->ofs);
3338 if (i < 0 || j >= prog->max_edicts) {
3339 Con_Printf("Invalid entity reference from global %s.\n", PRVM_GetString(prog, d->s_name));
3342 ed = PRVM_EDICT_NUM(j);;
3343 ed->priv.required->mark = stage;
3346 // Future stages: all entities that are referenced by an entity of the previous stage.
3350 for(j = 0; j < prog->num_edicts; ++j)
3352 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3353 if(ed->priv.required->free)
3355 if(ed->priv.required->mark)
3357 if(PRVM_IsEdictReferenced(prog, ed, stage))
3359 ed->priv.required->mark = stage + 1;
3366 Con_DPrintf("leak check used %d stages to find all references\n", stage);
3369 void PRVM_LeakTest(prvm_prog_t *prog)
3372 qboolean leaked = false;
3374 if(!prog->leaktest_active)
3378 for (i = 0; i < prog->numknownstrings; ++i)
3380 if(prog->knownstrings[i])
3381 if(prog->knownstrings_freeable[i])
3382 if(prog->knownstrings_origin[i])
3383 if(!PRVM_IsStringReferenced(prog, PRVM_KNOWNSTRINGBASE + i))
3385 Con_Printf("Unreferenced string found!\n Value: %s\n Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
3391 PRVM_MarkReferencedEdicts(prog);
3392 for(j = 0; j < prog->num_edicts; ++j)
3394 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3395 if(ed->priv.required->free)
3397 if(!ed->priv.required->mark)
3398 if(ed->priv.required->allocation_origin)
3400 Con_Printf("Unreferenced edict found!\n Allocated at: %s\n", ed->priv.required->allocation_origin);
3401 PRVM_ED_Print(prog, ed, NULL);
3406 ed->priv.required->mark = 0; // clear marks again when done
3409 for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
3411 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
3413 if(stringbuffer->origin)
3415 Con_Printf("Open string buffer handle found!\n Allocated at: %s\n", stringbuffer->origin);
3420 for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
3422 if(prog->openfiles[i])
3423 if(prog->openfiles_origin[i])
3425 Con_Printf("Open file handle found!\n Allocated at: %s\n", prog->openfiles_origin[i]);
3430 for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
3432 if(prog->opensearches[i])
3433 if(prog->opensearches_origin[i])
3435 Con_Printf("Open search handle found!\n Allocated at: %s\n", prog->opensearches_origin[i]);
3441 Con_Printf("Congratulations. No leaks found.\n");