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_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)"};
42 cvar_t prvm_errordump = {0, "prvm_errordump", "0", "write a savegame on crash to crash-server.dmp"};
43 cvar_t prvm_breakpointdump = {0, "prvm_breakpointdump", "0", "write a savegame on breakpoint to breakpoint-server.dmp"};
44 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)"};
45 cvar_t prvm_reuseedicts_neverinsameframe = {0, "prvm_reuseedicts_neverinsameframe", "1", "never allows re-use of freed entity slots during same frame"};
47 static double prvm_reuseedicts_always_allow = 0;
48 qboolean prvm_runawaycheck = true;
50 //============================================================================
58 static void PRVM_MEM_Alloc(prvm_prog_t *prog)
62 // reserve space for the null entity aka world
63 // check bound of max_edicts
64 prog->max_edicts = bound(1 + prog->reserved_edicts, prog->max_edicts, prog->limit_edicts);
65 prog->num_edicts = bound(1 + prog->reserved_edicts, prog->num_edicts, prog->max_edicts);
67 // edictprivate_size has to be min as big prvm_edict_private_t
68 prog->edictprivate_size = max(prog->edictprivate_size,(int)sizeof(prvm_edict_private_t));
71 prog->edicts = (prvm_edict_t *)Mem_Alloc(prog->progs_mempool,prog->limit_edicts * sizeof(prvm_edict_t));
73 // alloc edict private space
74 prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size);
77 prog->entityfieldsarea = prog->entityfields * prog->max_edicts;
78 prog->edictsfields = (prvm_vec_t *)Mem_Alloc(prog->progs_mempool, prog->entityfieldsarea * sizeof(prvm_vec_t));
81 for(i = 0; i < prog->max_edicts; i++)
83 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
84 prog->edicts[i].fields.fp = prog->edictsfields + i * prog->entityfields;
90 PRVM_MEM_IncreaseEdicts
93 void PRVM_MEM_IncreaseEdicts(prvm_prog_t *prog)
97 if(prog->max_edicts >= prog->limit_edicts)
100 prog->begin_increase_edicts(prog);
103 prog->max_edicts = min(prog->max_edicts + 256, prog->limit_edicts);
105 prog->entityfieldsarea = prog->entityfields * prog->max_edicts;
106 prog->edictsfields = (prvm_vec_t*)Mem_Realloc(prog->progs_mempool, (void *)prog->edictsfields, prog->entityfieldsarea * sizeof(prvm_vec_t));
107 prog->edictprivate = (void *)Mem_Realloc(prog->progs_mempool, (void *)prog->edictprivate, prog->max_edicts * prog->edictprivate_size);
109 //set e and v pointers
110 for(i = 0; i < prog->max_edicts; i++)
112 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
113 prog->edicts[i].fields.fp = prog->edictsfields + i * prog->entityfields;
116 prog->end_increase_edicts(prog);
119 //============================================================================
122 int PRVM_ED_FindFieldOffset(prvm_prog_t *prog, const char *field)
125 d = PRVM_ED_FindField(prog, field);
131 int PRVM_ED_FindGlobalOffset(prvm_prog_t *prog, const char *global)
134 d = PRVM_ED_FindGlobal(prog, global);
140 func_t PRVM_ED_FindFunctionOffset(prvm_prog_t *prog, const char *function)
143 f = PRVM_ED_FindFunction(prog, function);
146 return (func_t)(f - prog->functions);
154 prvm_prog_t *PRVM_ProgFromString(const char *str)
156 if (!strcmp(str, "server"))
158 if (!strcmp(str, "client"))
161 if (!strcmp(str, "menu"))
169 PRVM_FriendlyProgFromString
172 prvm_prog_t *PRVM_FriendlyProgFromString(const char *str)
174 prvm_prog_t *prog = PRVM_ProgFromString(str);
177 Con_Printf("%s: unknown program name\n", str);
182 Con_Printf("%s: program is not loaded\n", str);
192 Sets everything to NULL
195 void PRVM_ED_ClearEdict(prvm_prog_t *prog, prvm_edict_t *e)
197 memset(e->fields.fp, 0, prog->entityfields * sizeof(prvm_vec_t));
198 e->priv.required->free = false;
200 // AK: Let the init_edict function determine if something needs to be initialized
201 prog->init_edict(prog, e);
204 const char *PRVM_AllocationOrigin(prvm_prog_t *prog)
207 if(prog->leaktest_active)
208 if(prog->depth > 0) // actually in QC code and not just parsing the entities block of a map/savegame
210 buf = (char *)PRVM_Alloc(128);
211 PRVM_ShortStackTrace(prog, buf, 128);
220 Returns if this particular edict could get allocated by PRVM_ED_Alloc
223 qboolean PRVM_ED_CanAlloc(prvm_prog_t *prog, prvm_edict_t *e)
225 if(!e->priv.required->free)
227 if(prvm_reuseedicts_always_allow == realtime)
229 if(realtime <= e->priv.required->freetime + 0.1 && prvm_reuseedicts_neverinsameframe.integer)
230 return false; // never allow reuse in same frame (causes networking trouble)
231 if(e->priv.required->freetime < prog->starttime + prvm_reuseedicts_startuptime.value)
233 if(realtime > e->priv.required->freetime + 1)
235 return false; // entity slot still blocked because the entity was freed less than one second ago
242 Either finds a free edict, or allocates a new one.
243 Try to avoid reusing an entity that was recently freed, because it
244 can cause the client to think the entity morphed into something else
245 instead of being removed and recreated, which can cause interpolated
246 angles and bad trails.
249 prvm_edict_t *PRVM_ED_Alloc(prvm_prog_t *prog)
254 // the client qc dont need maxclients
255 // thus it doesnt need to use svs.maxclients
256 // AK: changed i=svs.maxclients+1
257 // AK: changed so the edict 0 wont spawn -> used as reserved/world entity
258 // although the menu/client has no world
259 for (i = prog->reserved_edicts + 1;i < prog->num_edicts;i++)
261 e = PRVM_EDICT_NUM(i);
262 if(PRVM_ED_CanAlloc(prog, e))
264 PRVM_ED_ClearEdict (prog, e);
265 e->priv.required->allocation_origin = PRVM_AllocationOrigin(prog);
270 if (i == prog->limit_edicts)
271 prog->error_cmd("%s: PRVM_ED_Alloc: no free edicts", prog->name);
274 if (prog->num_edicts >= prog->max_edicts)
275 PRVM_MEM_IncreaseEdicts(prog);
277 e = PRVM_EDICT_NUM(i);
278 PRVM_ED_ClearEdict(prog, e);
280 e->priv.required->allocation_origin = PRVM_AllocationOrigin(prog);
289 Marks the edict as free
290 FIXME: walk all entities and NULL out references to this entity
293 void PRVM_ED_Free(prvm_prog_t *prog, prvm_edict_t *ed)
295 // dont delete the null entity (world) or reserved edicts
296 if (ed - prog->edicts <= prog->reserved_edicts)
299 prog->free_edict(prog, ed);
301 ed->priv.required->free = true;
302 ed->priv.required->freetime = realtime;
303 if(ed->priv.required->allocation_origin)
305 Mem_Free((char *)ed->priv.required->allocation_origin);
306 ed->priv.required->allocation_origin = NULL;
310 //===========================================================================
317 static ddef_t *PRVM_ED_GlobalAtOfs (prvm_prog_t *prog, int ofs)
322 for (i = 0;i < prog->numglobaldefs;i++)
324 def = &prog->globaldefs[i];
336 ddef_t *PRVM_ED_FieldAtOfs (prvm_prog_t *prog, int ofs)
341 for (i = 0;i < prog->numfielddefs;i++)
343 def = &prog->fielddefs[i];
355 ddef_t *PRVM_ED_FindField (prvm_prog_t *prog, const char *name)
360 for (i = 0;i < prog->numfielddefs;i++)
362 def = &prog->fielddefs[i];
363 if (!strcmp(PRVM_GetString(prog, def->s_name), name))
374 ddef_t *PRVM_ED_FindGlobal (prvm_prog_t *prog, const char *name)
379 for (i = 0;i < prog->numglobaldefs;i++)
381 def = &prog->globaldefs[i];
382 if (!strcmp(PRVM_GetString(prog, def->s_name), name))
394 mfunction_t *PRVM_ED_FindFunction (prvm_prog_t *prog, const char *name)
399 for (i = 0;i < prog->numfunctions;i++)
401 func = &prog->functions[i];
402 if (!strcmp(PRVM_GetString(prog, func->s_name), name))
413 Returns a string describing *data in a type specific manner
416 static char *PRVM_ValueString (prvm_prog_t *prog, etype_t type, prvm_eval_t *val, char *line, size_t linelength)
422 type = (etype_t)((int) type & ~DEF_SAVEGLOBAL);
427 strlcpy (line, PRVM_GetString (prog, val->string), linelength);
431 if (n < 0 || n >= prog->max_edicts)
432 dpsnprintf (line, linelength, "entity %i (invalid!)", n);
434 dpsnprintf (line, linelength, "entity %i", n);
437 f = prog->functions + val->function;
438 dpsnprintf (line, linelength, "%s()", PRVM_GetString(prog, f->s_name));
441 def = PRVM_ED_FieldAtOfs ( prog, val->_int );
442 dpsnprintf (line, linelength, ".%s", PRVM_GetString(prog, def->s_name));
445 dpsnprintf (line, linelength, "void");
448 // LordHavoc: changed from %5.1f to %10.4f
449 dpsnprintf (line, linelength, FLOAT_LOSSLESS_FORMAT, val->_float);
452 // LordHavoc: changed from %5.1f to %10.4f
453 dpsnprintf (line, linelength, "'" VECTOR_LOSSLESS_FORMAT "'", val->vector[0], val->vector[1], val->vector[2]);
456 dpsnprintf (line, linelength, "pointer");
459 dpsnprintf (line, linelength, "bad type %i", (int) type);
470 Returns a string describing *data in a type specific manner
471 Easier to parse than PR_ValueString
474 char *PRVM_UglyValueString (prvm_prog_t *prog, etype_t type, prvm_eval_t *val, char *line, size_t linelength)
481 type = (etype_t)((int)type & ~DEF_SAVEGLOBAL);
486 // Parse the string a bit to turn special characters
487 // (like newline, specifically) into escape codes,
488 // this fixes saving games from various mods
489 s = PRVM_GetString (prog, val->string);
490 for (i = 0;i < (int)linelength - 2 && *s;)
519 dpsnprintf (line, linelength, "%i", val->edict);
522 f = prog->functions + val->function;
523 strlcpy (line, PRVM_GetString (prog, f->s_name), linelength);
526 def = PRVM_ED_FieldAtOfs ( prog, val->_int );
527 dpsnprintf (line, linelength, ".%s", PRVM_GetString(prog, def->s_name));
530 dpsnprintf (line, linelength, "void");
533 dpsnprintf (line, linelength, FLOAT_LOSSLESS_FORMAT, val->_float);
536 dpsnprintf (line, linelength, VECTOR_LOSSLESS_FORMAT, val->vector[0], val->vector[1], val->vector[2]);
539 dpsnprintf (line, linelength, "bad type %i", type);
550 Returns a string with a description and the contents of a global,
551 padded to 20 field width
554 char *PRVM_GlobalString (prvm_prog_t *prog, int ofs, char *line, size_t linelength)
560 char valuebuf[MAX_INPUTLINE];
562 val = (prvm_eval_t *)&prog->globals.fp[ofs];
563 def = PRVM_ED_GlobalAtOfs(prog, ofs);
565 dpsnprintf (line, linelength, "GLOBAL%i", ofs);
568 s = PRVM_ValueString (prog, (etype_t)def->type, val, valuebuf, sizeof(valuebuf));
569 dpsnprintf (line, linelength, "%s (=%s)", PRVM_GetString(prog, def->s_name), s);
573 //for ( ; i<20 ; i++)
574 // strcat (line," ");
580 char *PRVM_GlobalStringNoContents (prvm_prog_t *prog, int ofs, char *line, size_t linelength)
585 def = PRVM_ED_GlobalAtOfs(prog, ofs);
587 dpsnprintf (line, linelength, "GLOBAL%i", ofs);
589 dpsnprintf (line, linelength, "%s", PRVM_GetString(prog, def->s_name));
592 //for ( ; i<20 ; i++)
593 // strcat (line," ");
607 // LordHavoc: optimized this to print out much more quickly (tempstring)
608 // LordHavoc: changed to print out every 4096 characters (incase there are a lot of fields to print)
609 void PRVM_ED_Print(prvm_prog_t *prog, prvm_edict_t *ed, const char *wildcard_fieldname)
617 char tempstring[MAX_INPUTLINE], tempstring2[260]; // temporary string buffers
618 char valuebuf[MAX_INPUTLINE];
620 if (ed->priv.required->free)
622 Con_Printf("%s: FREE\n",prog->name);
627 dpsnprintf(tempstring, sizeof(tempstring), "\n%s EDICT %i:\n", prog->name, PRVM_NUM_FOR_EDICT(ed));
628 for (i = 1;i < prog->numfielddefs;i++)
630 d = &prog->fielddefs[i];
631 name = PRVM_GetString(prog, d->s_name);
632 if(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
633 continue; // skip _x, _y, _z vars
635 // Check Field Name Wildcard
636 if(wildcard_fieldname)
637 if( !matchpattern(name, wildcard_fieldname, 1) )
638 // Didn't match; skip
641 val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
643 // if the value is still all 0, skip the field
644 type = d->type & ~DEF_SAVEGLOBAL;
646 for (j=0 ; j<prvm_type_size[type] ; j++)
649 if (j == prvm_type_size[type])
652 if (strlen(name) > sizeof(tempstring2)-4)
654 memcpy (tempstring2, name, sizeof(tempstring2)-4);
655 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
656 tempstring2[sizeof(tempstring2)-1] = 0;
659 strlcat(tempstring, name, sizeof(tempstring));
660 for (l = strlen(name);l < 14;l++)
661 strlcat(tempstring, " ", sizeof(tempstring));
662 strlcat(tempstring, " ", sizeof(tempstring));
664 name = PRVM_ValueString(prog, (etype_t)d->type, val, valuebuf, sizeof(valuebuf));
665 if (strlen(name) > sizeof(tempstring2)-4)
667 memcpy (tempstring2, name, sizeof(tempstring2)-4);
668 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
669 tempstring2[sizeof(tempstring2)-1] = 0;
672 strlcat(tempstring, name, sizeof(tempstring));
673 strlcat(tempstring, "\n", sizeof(tempstring));
674 if (strlen(tempstring) >= sizeof(tempstring)/2)
676 Con_Print(tempstring);
681 Con_Print(tempstring);
691 extern cvar_t developer_entityparsing;
692 void PRVM_ED_Write (prvm_prog_t *prog, qfile_t *f, prvm_edict_t *ed)
700 char valuebuf[MAX_INPUTLINE];
704 if (ed->priv.required->free)
710 for (i = 1;i < prog->numfielddefs;i++)
712 d = &prog->fielddefs[i];
713 name = PRVM_GetString(prog, d->s_name);
715 if(developer_entityparsing.integer)
716 Con_Printf("PRVM_ED_Write: at entity %d field %s\n", PRVM_NUM_FOR_EDICT(ed), name);
718 //if(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
719 if(strlen(name) > 1 && name[strlen(name)-2] == '_')
720 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?)
722 val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
724 // if the value is still all 0, skip the field
725 type = d->type & ~DEF_SAVEGLOBAL;
726 for (j=0 ; j<prvm_type_size[type] ; j++)
729 if (j == prvm_type_size[type])
732 FS_Printf(f,"\"%s\" ",name);
733 prog->statestring = va(vabuf, sizeof(vabuf), "PRVM_ED_Write, ent=%d, name=%s", i, name);
734 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString(prog, (etype_t)d->type, val, valuebuf, sizeof(valuebuf)));
735 prog->statestring = NULL;
741 void PRVM_ED_PrintNum (prvm_prog_t *prog, int ent, const char *wildcard_fieldname)
743 PRVM_ED_Print(prog, PRVM_EDICT_NUM(ent), wildcard_fieldname);
748 PRVM_ED_PrintEdicts_f
750 For debugging, prints all the entities in the current server
753 void PRVM_ED_PrintEdicts_f (void)
757 const char *wildcard_fieldname;
759 if(Cmd_Argc() < 2 || Cmd_Argc() > 3)
761 Con_Print("prvm_edicts <program name> <optional field name wildcard>\n");
765 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
769 wildcard_fieldname = Cmd_Argv(2);
771 wildcard_fieldname = NULL;
773 Con_Printf("%s: %i entities\n", prog->name, prog->num_edicts);
774 for (i=0 ; i<prog->num_edicts ; i++)
775 PRVM_ED_PrintNum (prog, i, wildcard_fieldname);
782 For debugging, prints a single edict
785 static void PRVM_ED_PrintEdict_f (void)
789 const char *wildcard_fieldname;
791 if(Cmd_Argc() < 3 || Cmd_Argc() > 4)
793 Con_Print("prvm_edict <program name> <edict number> <optional field name wildcard>\n");
797 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
800 i = atoi (Cmd_Argv(2));
801 if (i >= prog->num_edicts)
803 Con_Print("Bad edict number\n");
807 // Optional Wildcard Provided
808 wildcard_fieldname = Cmd_Argv(3);
811 wildcard_fieldname = NULL;
812 PRVM_ED_PrintNum (prog, i, wildcard_fieldname);
822 // 2 possibilities : 1. just displaying the active edict count
823 // 2. making a function pointer [x]
824 static void PRVM_ED_Count_f (void)
830 Con_Print("prvm_count <program name>\n");
834 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
837 prog->count_edicts(prog);
841 ==============================================================================
845 FIXME: need to tag constants, doesn't really work
846 ==============================================================================
854 void PRVM_ED_WriteGlobals (prvm_prog_t *prog, qfile_t *f)
861 char valuebuf[MAX_INPUTLINE];
864 for (i = 0;i < prog->numglobaldefs;i++)
866 def = &prog->globaldefs[i];
868 if ( !(def->type & DEF_SAVEGLOBAL) )
870 type &= ~DEF_SAVEGLOBAL;
872 if (type != ev_string && type != ev_float && type != ev_entity)
875 name = PRVM_GetString(prog, def->s_name);
877 if(developer_entityparsing.integer)
878 Con_Printf("PRVM_ED_WriteGlobals: at global %s\n", name);
880 prog->statestring = va(vabuf, sizeof(vabuf), "PRVM_ED_WriteGlobals, name=%s", name);
881 FS_Printf(f,"\"%s\" ", name);
882 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString(prog, (etype_t)type, (prvm_eval_t *)&prog->globals.fp[def->ofs], valuebuf, sizeof(valuebuf)));
883 prog->statestring = NULL;
893 void PRVM_ED_ParseGlobals (prvm_prog_t *prog, const char *data)
895 char keyname[MAX_INPUTLINE];
901 if (!COM_ParseToken_Simple(&data, false, false, true))
902 prog->error_cmd("PRVM_ED_ParseGlobals: EOF without closing brace");
903 if (com_token[0] == '}')
906 if (developer_entityparsing.integer)
907 Con_Printf("Key: \"%s\"", com_token);
909 strlcpy (keyname, com_token, sizeof(keyname));
912 if (!COM_ParseToken_Simple(&data, false, true, true))
913 prog->error_cmd("PRVM_ED_ParseGlobals: EOF without closing brace");
915 if (developer_entityparsing.integer)
916 Con_Printf(" \"%s\"\n", com_token);
918 if (com_token[0] == '}')
919 prog->error_cmd("PRVM_ED_ParseGlobals: closing brace without data");
921 key = PRVM_ED_FindGlobal (prog, keyname);
924 Con_DPrintf("'%s' is not a global on %s\n", keyname, prog->name);
928 if (!PRVM_ED_ParseEpair(prog, NULL, key, com_token, true))
929 prog->error_cmd("PRVM_ED_ParseGlobals: parse error");
933 //============================================================================
940 Can parse either fields or globals
941 returns false if error
944 qboolean PRVM_ED_ParseEpair(prvm_prog_t *prog, prvm_edict_t *ent, ddef_t *key, const char *s, qboolean parsebackslash)
953 val = (prvm_eval_t *)(ent->fields.fp + key->ofs);
955 val = (prvm_eval_t *)(prog->globals.fp + key->ofs);
956 switch (key->type & ~DEF_SAVEGLOBAL)
959 l = (int)strlen(s) + 1;
960 val->string = PRVM_AllocString(prog, l, &new_p);
961 for (i = 0;i < l;i++)
963 if (s[i] == '\\' && s[i+1] && parsebackslash)
968 else if (s[i] == 'r')
979 while (*s && ISWHITESPACE(*s))
981 val->_float = atof(s);
985 for (i = 0;i < 3;i++)
987 while (*s && ISWHITESPACE(*s))
991 val->vector[i] = atof(s);
992 while (!ISWHITESPACE(*s))
1000 while (*s && ISWHITESPACE(*s))
1003 if (i >= prog->limit_edicts)
1004 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);
1005 while (i >= prog->max_edicts)
1006 PRVM_MEM_IncreaseEdicts(prog);
1007 // if IncreaseEdicts was called the base pointer needs to be updated
1009 val = (prvm_eval_t *)(ent->fields.fp + key->ofs);
1010 val->edict = PRVM_EDICT_TO_PROG(PRVM_EDICT_NUM((int)i));
1016 Con_DPrintf("PRVM_ED_ParseEpair: Bogus field name %s in %s\n", s, prog->name);
1019 def = PRVM_ED_FindField(prog, s + 1);
1022 Con_DPrintf("PRVM_ED_ParseEpair: Can't find field %s in %s\n", s, prog->name);
1025 val->_int = def->ofs;
1029 func = PRVM_ED_FindFunction(prog, s);
1032 Con_Printf("PRVM_ED_ParseEpair: Can't find function %s in %s\n", s, prog->name);
1035 val->function = func - prog->functions;
1039 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);
1049 Console command to send a string to QC function GameCommand of the
1053 sv_cmd adminmsg 3 "do not teamkill"
1054 cl_cmd someclientcommand
1055 menu_cmd somemenucommand
1057 All progs can support this extension; sg calls it in server QC, cg in client
1061 static void PRVM_GameCommand(const char *whichprogs, const char *whichcmd)
1066 Con_Printf("%s text...\n", whichcmd);
1070 if (!(prog = PRVM_FriendlyProgFromString(whichprogs)))
1073 if(!PRVM_allfunction(GameCommand))
1075 Con_Printf("%s program do not support GameCommand!\n", whichprogs);
1079 int restorevm_tempstringsbuf_cursize;
1084 restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
1085 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(prog, s ? s : "");
1086 prog->ExecuteProgram(prog, PRVM_allfunction(GameCommand), "QC function GameCommand is missing");
1087 prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1090 static void PRVM_GameCommand_Server_f(void)
1092 PRVM_GameCommand("server", "sv_cmd");
1094 static void PRVM_GameCommand_Client_f(void)
1096 PRVM_GameCommand("client", "cl_cmd");
1098 static void PRVM_GameCommand_Menu_f(void)
1100 PRVM_GameCommand("menu", "menu_cmd");
1107 Console command to load a field of a specified edict
1110 static void PRVM_ED_EdictGet_f(void)
1117 char valuebuf[MAX_INPUTLINE];
1119 if(Cmd_Argc() != 4 && Cmd_Argc() != 5)
1121 Con_Print("prvm_edictget <program name> <edict number> <field> [<cvar>]\n");
1125 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
1128 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1130 if((key = PRVM_ED_FindField(prog, Cmd_Argv(3))) == 0)
1132 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1136 v = (prvm_eval_t *)(ed->fields.fp + key->ofs);
1137 s = PRVM_UglyValueString(prog, (etype_t)key->type, v, valuebuf, sizeof(valuebuf));
1140 cvar_t *cvar = Cvar_FindVar(Cmd_Argv(4));
1141 if (cvar && cvar->flags & CVAR_READONLY)
1143 Con_Printf("prvm_edictget: %s is read-only\n", cvar->name);
1146 Cvar_Get(Cmd_Argv(4), s, 0, NULL);
1149 Con_Printf("%s\n", s);
1155 static void PRVM_ED_GlobalGet_f(void)
1161 char valuebuf[MAX_INPUTLINE];
1163 if(Cmd_Argc() != 3 && Cmd_Argc() != 4)
1165 Con_Print("prvm_globalget <program name> <global> [<cvar>]\n");
1169 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
1172 key = PRVM_ED_FindGlobal(prog, Cmd_Argv(2));
1175 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
1179 v = (prvm_eval_t *) &prog->globals.fp[key->ofs];
1180 s = PRVM_UglyValueString(prog, (etype_t)key->type, v, valuebuf, sizeof(valuebuf));
1183 cvar_t *cvar = Cvar_FindVar(Cmd_Argv(3));
1184 if (cvar && cvar->flags & CVAR_READONLY)
1186 Con_Printf("prvm_globalget: %s is read-only\n", cvar->name);
1189 Cvar_Get(Cmd_Argv(3), s, 0, NULL);
1192 Con_Printf("%s\n", s);
1202 Console command to set a field of a specified edict
1205 static void PRVM_ED_EdictSet_f(void)
1213 Con_Print("prvm_edictset <program name> <edict number> <field> <value>\n");
1217 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
1220 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1222 if((key = PRVM_ED_FindField(prog, Cmd_Argv(3))) == 0)
1223 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1225 PRVM_ED_ParseEpair(prog, ed, key, Cmd_Argv(4), true);
1229 ====================
1232 Parses an edict out of the given string, returning the new position
1233 ed should be a properly initialized empty edict.
1234 Used for initial level load and for savegames.
1235 ====================
1237 const char *PRVM_ED_ParseEdict (prvm_prog_t *prog, const char *data, prvm_edict_t *ent)
1247 // go through all the dictionary pairs
1251 if (!COM_ParseToken_Simple(&data, false, false, true))
1252 prog->error_cmd("PRVM_ED_ParseEdict: EOF without closing brace");
1253 if (developer_entityparsing.integer)
1254 Con_Printf("Key: \"%s\"", com_token);
1255 if (com_token[0] == '}')
1258 // anglehack is to allow QuakeEd to write single scalar angles
1259 // and allow them to be turned into vectors. (FIXME...)
1260 if (!strcmp(com_token, "angle"))
1262 strlcpy (com_token, "angles", sizeof(com_token));
1268 // FIXME: change light to _light to get rid of this hack
1269 if (!strcmp(com_token, "light"))
1270 strlcpy (com_token, "light_lev", sizeof(com_token)); // hack for single light def
1272 strlcpy (keyname, com_token, sizeof(keyname));
1274 // another hack to fix keynames with trailing spaces
1275 n = strlen(keyname);
1276 while (n && keyname[n-1] == ' ')
1283 if (!COM_ParseToken_Simple(&data, false, false, true))
1284 prog->error_cmd("PRVM_ED_ParseEdict: EOF without closing brace");
1285 if (developer_entityparsing.integer)
1286 Con_Printf(" \"%s\"\n", com_token);
1288 if (com_token[0] == '}')
1289 prog->error_cmd("PRVM_ED_ParseEdict: closing brace without data");
1293 // ignore attempts to set key "" (this problem occurs in nehahra neh1m8.bsp)
1297 // keynames with a leading underscore are used for utility comments,
1298 // and are immediately discarded by quake
1299 if (keyname[0] == '_')
1302 key = PRVM_ED_FindField (prog, keyname);
1305 Con_DPrintf("%s: '%s' is not a field\n", prog->name, keyname);
1312 strlcpy (temp, com_token, sizeof(temp));
1313 dpsnprintf (com_token, sizeof(com_token), "0 %s 0", temp);
1316 if (!PRVM_ED_ParseEpair(prog, ent, key, com_token, strcmp(keyname, "wad") != 0))
1317 prog->error_cmd("PRVM_ED_ParseEdict: parse error");
1321 ent->priv.required->free = true;
1329 PRVM_ED_LoadFromFile
1331 The entities are directly placed in the array, rather than allocated with
1332 PRVM_ED_Alloc, because otherwise an error loading the map would have entity
1333 number references out of order.
1335 Creates a server's entity / program execution context by
1336 parsing textual entity definitions out of an ent file.
1338 Used for both fresh maps and savegame loads. A fresh map would also need
1339 to call PRVM_ED_CallSpawnFunctions () to let the objects initialize themselves.
1342 void PRVM_ED_LoadFromFile (prvm_prog_t *prog, const char *data)
1345 int parsed, inhibited, spawned, died;
1346 const char *funcname;
1355 prvm_reuseedicts_always_allow = realtime;
1360 // parse the opening brace
1361 if (!COM_ParseToken_Simple(&data, false, false, true))
1363 if (com_token[0] != '{')
1364 prog->error_cmd("PRVM_ED_LoadFromFile: %s: found %s when expecting {", prog->name, com_token);
1366 // CHANGED: this is not conform to PR_LoadFromFile
1367 if(prog->loadintoworld)
1369 prog->loadintoworld = false;
1370 ent = PRVM_EDICT_NUM(0);
1373 ent = PRVM_ED_Alloc(prog);
1376 if (ent != prog->edicts) // hack
1377 memset (ent->fields.fp, 0, prog->entityfields * sizeof(prvm_vec_t));
1379 data = PRVM_ED_ParseEdict (prog, data, ent);
1382 // remove the entity ?
1383 if(!prog->load_edict(prog, ent))
1385 PRVM_ED_Free(prog, ent);
1390 if (PRVM_serverfunction(SV_OnEntityPreSpawnFunction))
1393 PRVM_serverglobalfloat(time) = sv.time;
1394 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1395 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityPreSpawnFunction), "QC function SV_OnEntityPreSpawnFunction is missing");
1398 if(ent->priv.required->free)
1405 // immediately call spawn function, but only if there is a self global and a classname
1407 if(!ent->priv.required->free)
1409 if (!PRVM_alledictstring(ent, classname))
1411 Con_Print("No classname for:\n");
1412 PRVM_ED_Print(prog, ent, NULL);
1413 PRVM_ED_Free (prog, ent);
1417 // look for the spawn function
1418 funcname = PRVM_GetString(prog, PRVM_alledictstring(ent, classname));
1419 func = PRVM_ED_FindFunction (prog, va(vabuf, sizeof(vabuf), "spawnfunc_%s", funcname));
1421 if(!PRVM_allglobalfloat(require_spawnfunc_prefix))
1422 func = PRVM_ED_FindFunction (prog, funcname);
1426 // check for OnEntityNoSpawnFunction
1427 if (PRVM_serverfunction(SV_OnEntityNoSpawnFunction))
1430 PRVM_serverglobalfloat(time) = sv.time;
1431 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1432 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityNoSpawnFunction), "QC function SV_OnEntityNoSpawnFunction is missing");
1436 if (developer.integer > 0) // don't confuse non-developers with errors
1438 Con_Print("No spawn function for:\n");
1439 PRVM_ED_Print(prog, ent, NULL);
1441 PRVM_ED_Free (prog, ent);
1442 continue; // not included in "inhibited" count
1448 PRVM_serverglobalfloat(time) = sv.time;
1449 PRVM_allglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1450 prog->ExecuteProgram(prog, func - prog->functions, "");
1454 if(!ent->priv.required->free)
1455 if (PRVM_serverfunction(SV_OnEntityPostSpawnFunction))
1458 PRVM_serverglobalfloat(time) = sv.time;
1459 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1460 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityPostSpawnFunction), "QC function SV_OnEntityPostSpawnFunction is missing");
1464 if (ent->priv.required->free)
1468 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);
1470 prvm_reuseedicts_always_allow = 0;
1473 static void PRVM_FindOffsets(prvm_prog_t *prog)
1475 // field and global searches use -1 for NULL
1476 memset(&prog->fieldoffsets, -1, sizeof(prog->fieldoffsets));
1477 memset(&prog->globaloffsets, -1, sizeof(prog->globaloffsets));
1478 // function searches use 0 for NULL
1479 memset(&prog->funcoffsets, 0, sizeof(prog->funcoffsets));
1480 #define PRVM_DECLARE_serverglobalfloat(x)
1481 #define PRVM_DECLARE_serverglobalvector(x)
1482 #define PRVM_DECLARE_serverglobalstring(x)
1483 #define PRVM_DECLARE_serverglobaledict(x)
1484 #define PRVM_DECLARE_serverglobalfunction(x)
1485 #define PRVM_DECLARE_clientglobalfloat(x)
1486 #define PRVM_DECLARE_clientglobalvector(x)
1487 #define PRVM_DECLARE_clientglobalstring(x)
1488 #define PRVM_DECLARE_clientglobaledict(x)
1489 #define PRVM_DECLARE_clientglobalfunction(x)
1490 #define PRVM_DECLARE_menuglobalfloat(x)
1491 #define PRVM_DECLARE_menuglobalvector(x)
1492 #define PRVM_DECLARE_menuglobalstring(x)
1493 #define PRVM_DECLARE_menuglobaledict(x)
1494 #define PRVM_DECLARE_menuglobalfunction(x)
1495 #define PRVM_DECLARE_serverfieldfloat(x)
1496 #define PRVM_DECLARE_serverfieldvector(x)
1497 #define PRVM_DECLARE_serverfieldstring(x)
1498 #define PRVM_DECLARE_serverfieldedict(x)
1499 #define PRVM_DECLARE_serverfieldfunction(x)
1500 #define PRVM_DECLARE_clientfieldfloat(x)
1501 #define PRVM_DECLARE_clientfieldvector(x)
1502 #define PRVM_DECLARE_clientfieldstring(x)
1503 #define PRVM_DECLARE_clientfieldedict(x)
1504 #define PRVM_DECLARE_clientfieldfunction(x)
1505 #define PRVM_DECLARE_menufieldfloat(x)
1506 #define PRVM_DECLARE_menufieldvector(x)
1507 #define PRVM_DECLARE_menufieldstring(x)
1508 #define PRVM_DECLARE_menufieldedict(x)
1509 #define PRVM_DECLARE_menufieldfunction(x)
1510 #define PRVM_DECLARE_serverfunction(x)
1511 #define PRVM_DECLARE_clientfunction(x)
1512 #define PRVM_DECLARE_menufunction(x)
1513 #define PRVM_DECLARE_field(x) prog->fieldoffsets.x = PRVM_ED_FindFieldOffset(prog, #x);
1514 #define PRVM_DECLARE_global(x) prog->globaloffsets.x = PRVM_ED_FindGlobalOffset(prog, #x);
1515 #define PRVM_DECLARE_function(x) prog->funcoffsets.x = PRVM_ED_FindFunctionOffset(prog, #x);
1516 #include "prvm_offsets.h"
1517 #undef PRVM_DECLARE_serverglobalfloat
1518 #undef PRVM_DECLARE_serverglobalvector
1519 #undef PRVM_DECLARE_serverglobalstring
1520 #undef PRVM_DECLARE_serverglobaledict
1521 #undef PRVM_DECLARE_serverglobalfunction
1522 #undef PRVM_DECLARE_clientglobalfloat
1523 #undef PRVM_DECLARE_clientglobalvector
1524 #undef PRVM_DECLARE_clientglobalstring
1525 #undef PRVM_DECLARE_clientglobaledict
1526 #undef PRVM_DECLARE_clientglobalfunction
1527 #undef PRVM_DECLARE_menuglobalfloat
1528 #undef PRVM_DECLARE_menuglobalvector
1529 #undef PRVM_DECLARE_menuglobalstring
1530 #undef PRVM_DECLARE_menuglobaledict
1531 #undef PRVM_DECLARE_menuglobalfunction
1532 #undef PRVM_DECLARE_serverfieldfloat
1533 #undef PRVM_DECLARE_serverfieldvector
1534 #undef PRVM_DECLARE_serverfieldstring
1535 #undef PRVM_DECLARE_serverfieldedict
1536 #undef PRVM_DECLARE_serverfieldfunction
1537 #undef PRVM_DECLARE_clientfieldfloat
1538 #undef PRVM_DECLARE_clientfieldvector
1539 #undef PRVM_DECLARE_clientfieldstring
1540 #undef PRVM_DECLARE_clientfieldedict
1541 #undef PRVM_DECLARE_clientfieldfunction
1542 #undef PRVM_DECLARE_menufieldfloat
1543 #undef PRVM_DECLARE_menufieldvector
1544 #undef PRVM_DECLARE_menufieldstring
1545 #undef PRVM_DECLARE_menufieldedict
1546 #undef PRVM_DECLARE_menufieldfunction
1547 #undef PRVM_DECLARE_serverfunction
1548 #undef PRVM_DECLARE_clientfunction
1549 #undef PRVM_DECLARE_menufunction
1550 #undef PRVM_DECLARE_field
1551 #undef PRVM_DECLARE_global
1552 #undef PRVM_DECLARE_function
1557 typedef struct dpfield_s
1564 #define DPFIELDS (sizeof(dpfields) / sizeof(dpfield_t))
1566 dpfield_t dpfields[] =
1577 #define PO_HASHSIZE 16384
1578 typedef struct po_string_s
1581 struct po_string_s *nextonhashchain;
1586 po_string_t *hashtable[PO_HASHSIZE];
1589 static void PRVM_PO_UnparseString(char *out, const char *in, size_t outsize)
1598 case '\a': if(outsize >= 2) { *out++ = '\\'; *out++ = 'a'; outsize -= 2; } break;
1599 case '\b': if(outsize >= 2) { *out++ = '\\'; *out++ = 'b'; outsize -= 2; } break;
1600 case '\t': if(outsize >= 2) { *out++ = '\\'; *out++ = 't'; outsize -= 2; } break;
1601 case '\r': if(outsize >= 2) { *out++ = '\\'; *out++ = 'r'; outsize -= 2; } break;
1602 case '\n': if(outsize >= 2) { *out++ = '\\'; *out++ = 'n'; outsize -= 2; } break;
1603 case '\\': if(outsize >= 2) { *out++ = '\\'; *out++ = '\\'; outsize -= 2; } break;
1604 case '"': if(outsize >= 2) { *out++ = '\\'; *out++ = '"'; outsize -= 2; } break;
1606 if(*in >= 0 && *in <= 0x1F)
1611 *out++ = '0' + ((*in & 0700) >> 6);
1612 *out++ = '0' + ((*in & 0070) >> 3);
1613 *out++ = '0' + (*in & 0007) ;
1630 static void PRVM_PO_ParseString(char *out, const char *in, size_t outsize)
1643 case 'a': if(outsize > 0) { *out++ = '\a'; --outsize; } break;
1644 case 'b': if(outsize > 0) { *out++ = '\b'; --outsize; } break;
1645 case 't': if(outsize > 0) { *out++ = '\t'; --outsize; } break;
1646 case 'r': if(outsize > 0) { *out++ = '\r'; --outsize; } break;
1647 case 'n': if(outsize > 0) { *out++ = '\n'; --outsize; } break;
1648 case '\\': if(outsize > 0) { *out++ = '\\'; --outsize; } break;
1649 case '"': if(outsize > 0) { *out++ = '"'; --outsize; } break;
1650 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
1654 if(*in >= '0' && *in <= '7')
1657 *out = (*out << 3) | (*in - '0');
1660 if(*in >= '0' && *in <= '7')
1663 *out = (*out << 3) | (*in - '0');
1674 if(outsize > 0) { *out++ = *in; --outsize; }
1689 static po_t *PRVM_PO_Load(const char *filename, const char *filename2, mempool_t *pool)
1694 char inbuf[MAX_INPUTLINE];
1695 char decodedbuf[MAX_INPUTLINE];
1698 po_string_t thisstr;
1701 for (i = 0; i < 2; ++i)
1703 const char *buf = (const char *)
1704 FS_LoadFile((i > 0 ? filename : filename2), pool, true, NULL);
1705 // first read filename2, then read filename
1706 // so that progs.dat.de.po wins over common.de.po
1707 // and within file, last item wins
1714 po = (po_t *)Mem_Alloc(pool, sizeof(*po));
1715 memset(po, 0, sizeof(*po));
1718 memset(&thisstr, 0, sizeof(thisstr)); // hush compiler warning
1726 p = strchr(p, '\n');
1732 if(*p == '\r' || *p == '\n')
1737 if(!strncmp(p, "msgid \"", 7))
1742 else if(!strncmp(p, "msgstr \"", 8))
1749 p = strchr(p, '\n');
1759 q = strchr(p, '\n');
1766 if((size_t)(q - p) >= (size_t) sizeof(inbuf))
1768 strlcpy(inbuf, p, q - p); // not - 1, because this adds a NUL
1769 PRVM_PO_ParseString(decodedbuf + decodedpos, inbuf, sizeof(decodedbuf) - decodedpos);
1770 decodedpos += strlen(decodedbuf + decodedpos);
1780 Mem_Free(thisstr.key);
1781 thisstr.key = (char *)Mem_Alloc(pool, decodedpos + 1);
1782 memcpy(thisstr.key, decodedbuf, decodedpos + 1);
1784 else if(decodedpos > 0 && thisstr.key) // skip empty translation results
1786 thisstr.value = (char *)Mem_Alloc(pool, decodedpos + 1);
1787 memcpy(thisstr.value, decodedbuf, decodedpos + 1);
1788 hashindex = CRC_Block((const unsigned char *) thisstr.key, strlen(thisstr.key)) % PO_HASHSIZE;
1789 thisstr.nextonhashchain = po->hashtable[hashindex];
1790 po->hashtable[hashindex] = (po_string_t *)Mem_Alloc(pool, sizeof(thisstr));
1791 memcpy(po->hashtable[hashindex], &thisstr, sizeof(thisstr));
1792 memset(&thisstr, 0, sizeof(thisstr));
1796 Mem_Free((char *) buf);
1801 static const char *PRVM_PO_Lookup(po_t *po, const char *str)
1803 int hashindex = CRC_Block((const unsigned char *) str, strlen(str)) % PO_HASHSIZE;
1804 po_string_t *p = po->hashtable[hashindex];
1807 if(!strcmp(str, p->key))
1809 p = p->nextonhashchain;
1813 static void PRVM_PO_Destroy(po_t *po)
1816 for(i = 0; i < PO_HASHSIZE; ++i)
1818 po_string_t *p = po->hashtable[i];
1822 p = p->nextonhashchain;
1831 void PRVM_LeakTest(prvm_prog_t *prog);
1832 void PRVM_Prog_Reset(prvm_prog_t *prog)
1836 PRVM_LeakTest(prog);
1837 prog->reset_cmd(prog);
1838 Mem_FreePool(&prog->progs_mempool);
1840 PRVM_PO_Destroy((po_t *) prog->po);
1842 memset(prog,0,sizeof(prvm_prog_t));
1843 prog->break_statement = -1;
1844 prog->watch_global_type = ev_void;
1845 prog->watch_field_type = ev_void;
1853 static void PRVM_LoadLNO( prvm_prog_t *prog, const char *progname ) {
1854 fs_offset_t filesize;
1856 unsigned int *header;
1859 FS_StripExtension( progname, filename, sizeof( filename ) );
1860 strlcat( filename, ".lno", sizeof( filename ) );
1862 lno = FS_LoadFile( filename, tempmempool, false, &filesize );
1868 <Spike> SafeWrite (h, &lnotype, sizeof(int));
1869 <Spike> SafeWrite (h, &version, sizeof(int));
1870 <Spike> SafeWrite (h, &numglobaldefs, sizeof(int));
1871 <Spike> SafeWrite (h, &numpr_globals, sizeof(int));
1872 <Spike> SafeWrite (h, &numfielddefs, sizeof(int));
1873 <Spike> SafeWrite (h, &numstatements, sizeof(int));
1874 <Spike> SafeWrite (h, statement_linenums, numstatements*sizeof(int));
1876 if ((unsigned int)filesize < (6 + prog->progs_numstatements) * sizeof(int))
1882 header = (unsigned int *) lno;
1883 if( header[ 0 ] == *(unsigned int *) "LNOF" &&
1884 LittleLong( header[ 1 ] ) == 1 &&
1885 (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs_numglobaldefs &&
1886 (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs_numglobals &&
1887 (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs_numfielddefs &&
1888 (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs_numstatements )
1890 prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof( int ) );
1891 memcpy( prog->statement_linenums, header + 6, prog->progs_numstatements * sizeof( int ) );
1893 /* gmqcc suports columnums */
1894 if ((unsigned int)filesize > ((6 + 2 * prog->progs_numstatements) * sizeof( int )))
1896 prog->statement_columnnums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof( int ) );
1897 memcpy( prog->statement_columnnums, header + 6 + prog->progs_numstatements, prog->progs_numstatements * sizeof( int ) );
1908 static void PRVM_UpdateBreakpoints(prvm_prog_t *prog);
1909 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)
1912 dprograms_t *dprograms;
1913 dstatement_t *instatements;
1914 ddef_t *infielddefs;
1915 ddef_t *inglobaldefs;
1917 dfunction_t *infunctions;
1919 fs_offset_t filesize;
1920 int requiredglobalspace;
1936 prog->error_cmd("PRVM_LoadProgs: there is already a %s program loaded!", prog->name );
1938 Host_LockSession(); // all progs can use the session cvar
1939 Crypto_LoadKeys(); // all progs might use the keys at init time
1943 dprograms = (dprograms_t *) data;
1947 dprograms = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
1948 if (dprograms == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
1949 prog->error_cmd("PRVM_LoadProgs: couldn't load %s for %s", filename, prog->name);
1950 // TODO bounds check header fields (e.g. numstatements), they must never go behind end of file
1952 prog->profiletime = Sys_DirtyTime();
1953 prog->starttime = realtime;
1955 Con_DPrintf("%s programs occupy %iK.\n", prog->name, (int)(filesize/1024));
1957 requiredglobalspace = 0;
1958 for (i = 0;i < numrequiredglobals;i++)
1959 requiredglobalspace += required_global[i].type == ev_vector ? 3 : 1;
1961 prog->filecrc = CRC_Block((unsigned char *)dprograms, filesize);
1963 // byte swap the header
1964 prog->progs_version = LittleLong(dprograms->version);
1965 prog->progs_crc = LittleLong(dprograms->crc);
1966 if (prog->progs_version != PROG_VERSION)
1967 prog->error_cmd("%s: %s has wrong version number (%i should be %i)", prog->name, filename, prog->progs_version, PROG_VERSION);
1968 instatements = (dstatement_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_statements));
1969 prog->progs_numstatements = LittleLong(dprograms->numstatements);
1970 inglobaldefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globaldefs));
1971 prog->progs_numglobaldefs = LittleLong(dprograms->numglobaldefs);
1972 infielddefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_fielddefs));
1973 prog->progs_numfielddefs = LittleLong(dprograms->numfielddefs);
1974 infunctions = (dfunction_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_functions));
1975 prog->progs_numfunctions = LittleLong(dprograms->numfunctions);
1976 instrings = (char *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_strings));
1977 prog->progs_numstrings = LittleLong(dprograms->numstrings);
1978 inglobals = (int *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globals));
1979 prog->progs_numglobals = LittleLong(dprograms->numglobals);
1980 prog->progs_entityfields = LittleLong(dprograms->entityfields);
1982 prog->numstatements = prog->progs_numstatements;
1983 prog->numglobaldefs = prog->progs_numglobaldefs;
1984 prog->numfielddefs = prog->progs_numfielddefs;
1985 prog->numfunctions = prog->progs_numfunctions;
1986 prog->numstrings = prog->progs_numstrings;
1987 prog->numglobals = prog->progs_numglobals;
1988 prog->entityfields = prog->progs_entityfields;
1990 if (LittleLong(dprograms->ofs_strings) + prog->progs_numstrings > (int)filesize)
1991 prog->error_cmd("%s: %s strings go past end of file", prog->name, filename);
1992 prog->strings = (char *)Mem_Alloc(prog->progs_mempool, prog->progs_numstrings);
1993 memcpy(prog->strings, instrings, prog->progs_numstrings);
1994 prog->stringssize = prog->progs_numstrings;
1996 prog->numknownstrings = 0;
1997 prog->maxknownstrings = 0;
1998 prog->knownstrings = NULL;
1999 prog->knownstrings_freeable = NULL;
2001 Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
2003 // we need to expand the globaldefs and fielddefs to include engine defs
2004 prog->globaldefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobaldefs + numrequiredglobals) * sizeof(ddef_t));
2005 prog->globals.fp = (prvm_vec_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobals + requiredglobalspace + 2) * sizeof(prvm_vec_t));
2006 // + 2 is because of an otherwise occurring overrun in RETURN instruction
2007 // when trying to return the last or second-last global
2008 // (RETURN always returns a vector, there is no RETURN_F instruction)
2009 prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numfielddefs + numrequiredfields) * sizeof(ddef_t));
2010 // we need to convert the statements to our memory format
2011 prog->statements = (mstatement_t *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(mstatement_t));
2012 // allocate space for profiling statement usage
2013 prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(*prog->statement_profile));
2014 prog->explicit_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(*prog->statement_profile));
2015 // functions need to be converted to the memory format
2016 prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs_numfunctions);
2018 for (i = 0;i < prog->progs_numfunctions;i++)
2020 prog->functions[i].first_statement = LittleLong(infunctions[i].first_statement);
2021 prog->functions[i].parm_start = LittleLong(infunctions[i].parm_start);
2022 prog->functions[i].s_name = LittleLong(infunctions[i].s_name);
2023 prog->functions[i].s_file = LittleLong(infunctions[i].s_file);
2024 prog->functions[i].numparms = LittleLong(infunctions[i].numparms);
2025 prog->functions[i].locals = LittleLong(infunctions[i].locals);
2026 memcpy(prog->functions[i].parm_size, infunctions[i].parm_size, sizeof(infunctions[i].parm_size));
2027 if(prog->functions[i].first_statement >= prog->numstatements)
2028 prog->error_cmd("PRVM_LoadProgs: out of bounds function statement (function %d) in %s", i, prog->name);
2029 // TODO bounds check parm_start, s_name, s_file, numparms, locals, parm_size
2032 // copy the globaldefs to the new globaldefs list
2033 for (i=0 ; i<prog->numglobaldefs ; i++)
2035 prog->globaldefs[i].type = LittleShort(inglobaldefs[i].type);
2036 prog->globaldefs[i].ofs = LittleShort(inglobaldefs[i].ofs);
2037 prog->globaldefs[i].s_name = LittleLong(inglobaldefs[i].s_name);
2038 // TODO bounds check ofs, s_name
2041 // append the required globals
2042 for (i = 0;i < numrequiredglobals;i++)
2044 prog->globaldefs[prog->numglobaldefs].type = required_global[i].type;
2045 prog->globaldefs[prog->numglobaldefs].ofs = prog->numglobals;
2046 prog->globaldefs[prog->numglobaldefs].s_name = PRVM_SetEngineString(prog, required_global[i].name);
2047 if (prog->globaldefs[prog->numglobaldefs].type == ev_vector)
2048 prog->numglobals += 3;
2051 prog->numglobaldefs++;
2054 // copy the progs fields to the new fields list
2055 for (i = 0;i < prog->numfielddefs;i++)
2057 prog->fielddefs[i].type = LittleShort(infielddefs[i].type);
2058 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
2059 prog->error_cmd("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", prog->name);
2060 prog->fielddefs[i].ofs = LittleShort(infielddefs[i].ofs);
2061 prog->fielddefs[i].s_name = LittleLong(infielddefs[i].s_name);
2062 // TODO bounds check ofs, s_name
2065 // append the required fields
2066 for (i = 0;i < numrequiredfields;i++)
2068 prog->fielddefs[prog->numfielddefs].type = required_field[i].type;
2069 prog->fielddefs[prog->numfielddefs].ofs = prog->entityfields;
2070 prog->fielddefs[prog->numfielddefs].s_name = PRVM_SetEngineString(prog, required_field[i].name);
2071 if (prog->fielddefs[prog->numfielddefs].type == ev_vector)
2072 prog->entityfields += 3;
2074 prog->entityfields++;
2075 prog->numfielddefs++;
2078 // LordHavoc: TODO: reorder globals to match engine struct
2079 // LordHavoc: TODO: reorder fields to match engine struct
2080 #define remapglobal(index) (index)
2081 #define remapfield(index) (index)
2084 // FIXME: LordHavoc: this uses a crude way to identify integer constants, rather than checking for matching globaldefs and checking their type
2085 for (i = 0;i < prog->progs_numglobals;i++)
2087 u.i = LittleLong(inglobals[i]);
2088 // most globals are 0, we only need to deal with the ones that are not
2091 d = u.i & 0xFF800000;
2092 if ((d == 0xFF800000) || (d == 0))
2094 // Looks like an integer (expand to int64)
2095 prog->globals.ip[remapglobal(i)] = u.i;
2099 // Looks like a float (expand to double)
2100 prog->globals.fp[remapglobal(i)] = u.f;
2105 // LordHavoc: TODO: support 32bit progs statement formats
2106 // copy, remap globals in statements, bounds check
2107 for (i = 0;i < prog->progs_numstatements;i++)
2109 op = (opcode_t)LittleShort(instatements[i].op);
2110 a = (unsigned short)LittleShort(instatements[i].a);
2111 b = (unsigned short)LittleShort(instatements[i].b);
2112 c = (unsigned short)LittleShort(instatements[i].c);
2118 if (a >= prog->progs_numglobals || b + i < 0 || b + i >= prog->progs_numstatements)
2119 prog->error_cmd("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", i, prog->name);
2120 prog->statements[i].op = op;
2121 prog->statements[i].operand[0] = remapglobal(a);
2122 prog->statements[i].operand[1] = -1;
2123 prog->statements[i].operand[2] = -1;
2124 prog->statements[i].jumpabsolute = i + b;
2128 if (a + i < 0 || a + i >= prog->progs_numstatements)
2129 prog->error_cmd("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, prog->name);
2130 prog->statements[i].op = op;
2131 prog->statements[i].operand[0] = -1;
2132 prog->statements[i].operand[1] = -1;
2133 prog->statements[i].operand[2] = -1;
2134 prog->statements[i].jumpabsolute = i + a;
2137 Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", (int)op, i, prog->name);
2139 // global global global
2174 if (a >= prog->progs_numglobals || b >= prog->progs_numglobals || c >= prog->progs_numglobals)
2175 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
2176 prog->statements[i].op = op;
2177 prog->statements[i].operand[0] = remapglobal(a);
2178 prog->statements[i].operand[1] = remapglobal(b);
2179 prog->statements[i].operand[2] = remapglobal(c);
2180 prog->statements[i].jumpabsolute = -1;
2182 // global none global
2188 if (a >= prog->progs_numglobals || c >= prog->progs_numglobals)
2189 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2190 prog->statements[i].op = op;
2191 prog->statements[i].operand[0] = remapglobal(a);
2192 prog->statements[i].operand[1] = -1;
2193 prog->statements[i].operand[2] = remapglobal(c);
2194 prog->statements[i].jumpabsolute = -1;
2210 if (a >= prog->progs_numglobals || b >= prog->progs_numglobals)
2211 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2212 prog->statements[i].op = op;
2213 prog->statements[i].operand[0] = remapglobal(a);
2214 prog->statements[i].operand[1] = remapglobal(b);
2215 prog->statements[i].operand[2] = -1;
2216 prog->statements[i].jumpabsolute = -1;
2220 if ( a < prog->progs_numglobals)
2221 if ( prog->globals.ip[remapglobal(a)] >= 0 )
2222 if ( prog->globals.ip[remapglobal(a)] < prog->progs_numfunctions )
2223 if ( prog->functions[prog->globals.ip[remapglobal(a)]].first_statement == -642 )
2224 ++prog->numexplicitcoveragestatements;
2235 if ( a >= prog->progs_numglobals)
2236 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2237 prog->statements[i].op = op;
2238 prog->statements[i].operand[0] = remapglobal(a);
2239 prog->statements[i].operand[1] = -1;
2240 prog->statements[i].operand[2] = -1;
2241 prog->statements[i].jumpabsolute = -1;
2245 if(prog->numstatements < 1)
2247 prog->error_cmd("PRVM_LoadProgs: empty program in %s", prog->name);
2249 else switch(prog->statements[prog->numstatements - 1].op)
2256 prog->error_cmd("PRVM_LoadProgs: program may fall off the edge (does not end with RETURN, GOTO or DONE) in %s", prog->name);
2260 // we're done with the file now
2262 Mem_Free(dprograms);
2265 // check required functions
2266 for(i=0 ; i < numrequiredfunc ; i++)
2267 if(PRVM_ED_FindFunction(prog, required_func[i]) == 0)
2268 prog->error_cmd("%s: %s not found in %s",prog->name, required_func[i], filename);
2270 PRVM_LoadLNO(prog, filename);
2272 PRVM_Init_Exec(prog);
2274 if(*prvm_language.string)
2275 // in CSQC we really shouldn't be able to change how stuff works... sorry for now
2276 // later idea: include a list of authorized .po file checksums with the csprogs
2278 qboolean deftrans = prog == CLVM_prog;
2279 const char *realfilename = (prog != CLVM_prog ? filename : csqc_progname.string);
2280 if(deftrans) // once we have dotranslate_ strings, ALWAYS use the opt-in method!
2282 for (i=0 ; i<prog->numglobaldefs ; i++)
2285 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2286 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2287 if(name && !strncmp(name, "dotranslate_", 12))
2294 if(!strcmp(prvm_language.string, "dump"))
2296 qfile_t *f = FS_OpenRealFile(va(vabuf, sizeof(vabuf), "%s.pot", realfilename), "w", false);
2297 Con_Printf("Dumping to %s.pot\n", realfilename);
2300 for (i=0 ; i<prog->numglobaldefs ; i++)
2303 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2304 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2305 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2307 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2308 const char *value = PRVM_GetString(prog, val->string);
2311 char buf[MAX_INPUTLINE];
2312 PRVM_PO_UnparseString(buf, value, sizeof(buf));
2313 FS_Printf(f, "msgid \"%s\"\nmsgstr \"\"\n\n", buf);
2322 po_t *po = PRVM_PO_Load(
2323 va(vabuf, sizeof(vabuf), "%s.%s.po", realfilename, prvm_language.string),
2324 va(vabuf2, sizeof(vabuf2), "common.%s.po", prvm_language.string),
2325 prog->progs_mempool);
2328 for (i=0 ; i<prog->numglobaldefs ; i++)
2331 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2332 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2333 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2335 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2336 const char *value = PRVM_GetString(prog, val->string);
2339 value = PRVM_PO_Lookup(po, value);
2341 val->string = PRVM_SetEngineString(prog, value);
2349 for (i=0 ; i<prog->numglobaldefs ; i++)
2352 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2353 //Con_Printf("found var %s\n", name);
2355 && !strncmp(name, "autocvar_", 9)
2356 && !(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
2359 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2360 cvar_t *cvar = Cvar_FindVar(name + 9);
2361 //Con_Printf("PRVM_LoadProgs: autocvar global %s in %s, processing...\n", name, prog->name);
2366 Con_DPrintf("PRVM_LoadProgs: no cvar for autocvar global %s in %s, creating...\n", name, prog->name);
2367 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2370 if((float)((int)(val->_float)) == val->_float)
2371 dpsnprintf(buf, sizeof(buf), "%i", (int)(val->_float));
2373 dpsnprintf(buf, sizeof(buf), "%.9g", val->_float);
2377 dpsnprintf(buf, sizeof(buf), "%.9g %.9g %.9g", val->vector[0], val->vector[1], val->vector[2]); value = buf;
2380 value = PRVM_GetString(prog, val->string);
2383 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2386 cvar = Cvar_Get(name + 9, value, 0, NULL);
2387 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2389 val->string = PRVM_SetEngineString(prog, cvar->string);
2390 cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2393 prog->error_cmd("PRVM_LoadProgs: could not create cvar for autocvar global %s in %s", name, prog->name);
2394 cvar->globaldefindex_progid[prog - prvm_prog_list] = prog->id;
2395 cvar->globaldefindex[prog - prvm_prog_list] = i;
2397 else if((cvar->flags & CVAR_PRIVATE) == 0)
2399 // MUST BE SYNCED WITH cvar.c Cvar_Set
2402 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2405 val->_float = cvar->value;
2409 VectorClear(val->vector);
2410 for (j = 0;j < 3;j++)
2412 while (*s && ISWHITESPACE(*s))
2416 val->vector[j] = atof(s);
2417 while (!ISWHITESPACE(*s))
2424 val->string = PRVM_SetEngineString(prog, cvar->string);
2425 cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2428 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2431 cvar->globaldefindex_progid[prog - prvm_prog_list] = prog->id;
2432 cvar->globaldefindex[prog - prvm_prog_list] = i;
2435 Con_Printf("PRVM_LoadProgs: private cvar for autocvar global %s in %s\n", name, prog->name);
2441 prog->loaded = TRUE;
2443 PRVM_UpdateBreakpoints(prog);
2445 // set flags & ddef_ts in prog
2449 PRVM_FindOffsets(prog);
2451 prog->init_cmd(prog);
2454 PRVM_MEM_Alloc(prog);
2458 static void PRVM_Fields_f (void)
2461 int i, j, ednum, used, usedamount;
2463 char tempstring[MAX_INPUTLINE], tempstring2[260];
2473 Con_Print("no progs loaded\n");
2480 Con_Print("prvm_fields <program name>\n");
2484 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2487 counts = (int *)Mem_Alloc(tempmempool, prog->numfielddefs * sizeof(int));
2488 for (ednum = 0;ednum < prog->max_edicts;ednum++)
2490 ed = PRVM_EDICT_NUM(ednum);
2491 if (ed->priv.required->free)
2493 for (i = 1;i < prog->numfielddefs;i++)
2495 d = &prog->fielddefs[i];
2496 name = PRVM_GetString(prog, d->s_name);
2497 if (name[strlen(name)-2] == '_')
2498 continue; // skip _x, _y, _z vars
2499 val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
2500 // if the value is still all 0, skip the field
2501 for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
2503 if (val->ivector[j])
2514 for (i = 0;i < prog->numfielddefs;i++)
2516 d = &prog->fielddefs[i];
2517 name = PRVM_GetString(prog, d->s_name);
2518 if (name[strlen(name)-2] == '_')
2519 continue; // skip _x, _y, _z vars
2520 switch(d->type & ~DEF_SAVEGLOBAL)
2523 strlcat(tempstring, "string ", sizeof(tempstring));
2526 strlcat(tempstring, "entity ", sizeof(tempstring));
2529 strlcat(tempstring, "function ", sizeof(tempstring));
2532 strlcat(tempstring, "field ", sizeof(tempstring));
2535 strlcat(tempstring, "void ", sizeof(tempstring));
2538 strlcat(tempstring, "float ", sizeof(tempstring));
2541 strlcat(tempstring, "vector ", sizeof(tempstring));
2544 strlcat(tempstring, "pointer ", sizeof(tempstring));
2547 dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
2548 strlcat(tempstring, tempstring2, sizeof(tempstring));
2551 if (strlen(name) > sizeof(tempstring2)-4)
2553 memcpy (tempstring2, name, sizeof(tempstring2)-4);
2554 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
2555 tempstring2[sizeof(tempstring2)-1] = 0;
2558 strlcat(tempstring, name, sizeof(tempstring));
2559 for (j = (int)strlen(name);j < 25;j++)
2560 strlcat(tempstring, " ", sizeof(tempstring));
2561 dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
2562 strlcat(tempstring, tempstring2, sizeof(tempstring));
2563 strlcat(tempstring, "\n", sizeof(tempstring));
2564 if (strlen(tempstring) >= sizeof(tempstring)/2)
2566 Con_Print(tempstring);
2572 usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
2576 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);
2579 static void PRVM_Globals_f (void)
2583 const char *wildcard;
2589 Con_Print("no progs loaded\n");
2592 if(Cmd_Argc () < 2 || Cmd_Argc() > 3)
2594 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
2598 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2601 if( Cmd_Argc() == 3)
2602 wildcard = Cmd_Argv(2);
2606 Con_Printf("%s :", prog->name);
2608 for (i = 0;i < prog->numglobaldefs;i++)
2611 if( !matchpattern( PRVM_GetString(prog, prog->globaldefs[i].s_name), wildcard, 1) )
2616 Con_Printf("%s\n", PRVM_GetString(prog, prog->globaldefs[i].s_name));
2618 Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->numglobals, numculled, prog->numglobals * 4);
2626 static void PRVM_Global_f(void)
2630 char valuebuf[MAX_INPUTLINE];
2631 if( Cmd_Argc() != 3 ) {
2632 Con_Printf( "prvm_global <program name> <global name>\n" );
2636 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2639 global = PRVM_ED_FindGlobal( prog, Cmd_Argv(2) );
2641 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2643 Con_Printf( "%s: %s\n", Cmd_Argv(2), PRVM_ValueString( prog, (etype_t)global->type, PRVM_GLOBALFIELDVALUE(global->ofs), valuebuf, sizeof(valuebuf) ) );
2651 static void PRVM_GlobalSet_f(void)
2655 if( Cmd_Argc() != 4 ) {
2656 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2660 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2663 global = PRVM_ED_FindGlobal( prog, Cmd_Argv(2) );
2665 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2667 PRVM_ED_ParseEpair( prog, NULL, global, Cmd_Argv(3), true );
2671 ======================
2672 Break- and Watchpoints
2673 ======================
2677 char break_statement[256];
2678 char watch_global[256];
2680 char watch_field[256];
2683 static debug_data_t debug_data[PRVM_PROG_MAX];
2685 void PRVM_Breakpoint(prvm_prog_t *prog, int stack_index, const char *text)
2688 Con_Printf("PRVM_Breakpoint: %s\n", text);
2689 PRVM_PrintState(prog, stack_index);
2690 if (prvm_breakpointdump.integer)
2691 Host_Savegame_to(prog, va(vabuf, sizeof(vabuf), "breakpoint-%s.dmp", prog->name));
2694 void PRVM_Watchpoint(prvm_prog_t *prog, int stack_index, const char *text, etype_t type, prvm_eval_t *o, prvm_eval_t *n)
2696 size_t sz = sizeof(prvm_vec_t) * ((type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2697 if (memcmp(o, n, sz))
2700 char valuebuf_o[128];
2701 char valuebuf_n[128];
2702 PRVM_UglyValueString(prog, type, o, valuebuf_o, sizeof(valuebuf_o));
2703 PRVM_UglyValueString(prog, type, n, valuebuf_n, sizeof(valuebuf_n));
2704 dpsnprintf(buf, sizeof(buf), "%s: %s -> %s", text, valuebuf_o, valuebuf_n);
2705 PRVM_Breakpoint(prog, stack_index, buf);
2710 static void PRVM_UpdateBreakpoints(prvm_prog_t *prog)
2712 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2715 if (debug->break_statement[0])
2717 if (debug->break_statement[0] >= '0' && debug->break_statement[0] <= '9')
2719 prog->break_statement = atoi(debug->break_statement);
2720 prog->break_stack_index = 0;
2725 func = PRVM_ED_FindFunction (prog, debug->break_statement);
2728 Con_Printf("%s progs: no function or statement named %s to break on!\n", prog->name, debug->break_statement);
2729 prog->break_statement = -1;
2733 prog->break_statement = func->first_statement;
2734 prog->break_stack_index = 1;
2737 if (prog->break_statement >= -1)
2738 Con_Printf("%s progs: breakpoint is at statement %d\n", prog->name, prog->break_statement);
2741 prog->break_statement = -1;
2743 if (debug->watch_global[0])
2745 ddef_t *global = PRVM_ED_FindGlobal( prog, debug->watch_global );
2748 Con_Printf( "%s progs: no global named '%s' to watch!\n", prog->name, debug->watch_global );
2749 prog->watch_global_type = ev_void;
2753 size_t sz = sizeof(prvm_vec_t) * ((global->type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2754 prog->watch_global = global->ofs;
2755 prog->watch_global_type = (etype_t)global->type;
2756 memcpy(&prog->watch_global_value, PRVM_GLOBALFIELDVALUE(prog->watch_global), sz);
2758 if (prog->watch_global_type != ev_void)
2759 Con_Printf("%s progs: global watchpoint is at global index %d\n", prog->name, prog->watch_global);
2762 prog->watch_global_type = ev_void;
2764 if (debug->watch_field[0])
2766 ddef_t *field = PRVM_ED_FindField( prog, debug->watch_field );
2769 Con_Printf( "%s progs: no field named '%s' to watch!\n", prog->name, debug->watch_field );
2770 prog->watch_field_type = ev_void;
2774 size_t sz = sizeof(prvm_vec_t) * ((field->type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2775 prog->watch_edict = debug->watch_edict;
2776 prog->watch_field = field->ofs;
2777 prog->watch_field_type = (etype_t)field->type;
2778 if (prog->watch_edict < prog->num_edicts)
2779 memcpy(&prog->watch_edictfield_value, PRVM_EDICTFIELDVALUE(PRVM_EDICT_NUM(prog->watch_edict), prog->watch_field), sz);
2781 memset(&prog->watch_edictfield_value, 0, sz);
2783 if (prog->watch_edict != ev_void)
2784 Con_Printf("%s progs: edict field watchpoint is at edict %d field index %d\n", prog->name, prog->watch_edict, prog->watch_field);
2787 prog->watch_field_type = ev_void;
2790 static void PRVM_Breakpoint_f(void)
2794 if( Cmd_Argc() == 2 ) {
2795 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2798 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2799 debug->break_statement[0] = 0;
2801 PRVM_UpdateBreakpoints(prog);
2804 if( Cmd_Argc() != 3 ) {
2805 Con_Printf( "prvm_breakpoint <program name> <function name | statement>\n" );
2809 if (!(prog = PRVM_ProgFromString(Cmd_Argv(1))))
2813 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2814 strlcpy(debug->break_statement, Cmd_Argv(2), sizeof(debug->break_statement));
2816 PRVM_UpdateBreakpoints(prog);
2819 static void PRVM_GlobalWatchpoint_f(void)
2823 if( Cmd_Argc() == 2 ) {
2824 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2827 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2828 debug->watch_global[0] = 0;
2830 PRVM_UpdateBreakpoints(prog);
2833 if( Cmd_Argc() != 3 ) {
2834 Con_Printf( "prvm_globalwatchpoint <program name> <global name>\n" );
2838 if (!(prog = PRVM_ProgFromString(Cmd_Argv(1))))
2842 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2843 strlcpy(debug->watch_global, Cmd_Argv(2), sizeof(debug->watch_global));
2845 PRVM_UpdateBreakpoints(prog);
2848 static void PRVM_EdictWatchpoint_f(void)
2852 if( Cmd_Argc() == 2 ) {
2853 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2856 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2857 debug->watch_field[0] = 0;
2859 PRVM_UpdateBreakpoints(prog);
2862 if( Cmd_Argc() != 4 ) {
2863 Con_Printf( "prvm_edictwatchpoint <program name> <edict number> <field name>\n" );
2867 if (!(prog = PRVM_ProgFromString(Cmd_Argv(1))))
2871 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2872 debug->watch_edict = atoi(Cmd_Argv(2));
2873 strlcpy(debug->watch_field, Cmd_Argv(3), sizeof(debug->watch_field));
2875 PRVM_UpdateBreakpoints(prog);
2883 void PRVM_Init (void)
2885 Cmd_AddCommand ("prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2886 Cmd_AddCommand ("prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2887 Cmd_AddCommand ("prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2888 Cmd_AddCommand ("prvm_profile", PRVM_Profile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu)");
2889 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");
2890 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)");
2891 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)");
2892 Cmd_AddCommand ("prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2893 Cmd_AddCommand ("prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2894 Cmd_AddCommand ("prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2895 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)");
2896 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");
2897 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");
2898 Cmd_AddCommand ("prvm_printfunction", PRVM_PrintFunction_f, "prints a disassembly (QuakeC instructions) of the specified function in the selected VM (server, client, menu)");
2899 Cmd_AddCommand ("cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2900 Cmd_AddCommand ("menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2901 Cmd_AddCommand ("sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2903 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");
2904 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");
2905 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");
2907 Cvar_RegisterVariable (&prvm_language);
2908 Cvar_RegisterVariable (&prvm_traceqc);
2909 Cvar_RegisterVariable (&prvm_statementprofiling);
2910 Cvar_RegisterVariable (&prvm_timeprofiling);
2911 Cvar_RegisterVariable (&prvm_coverage);
2912 Cvar_RegisterVariable (&prvm_backtraceforwarnings);
2913 Cvar_RegisterVariable (&prvm_leaktest);
2914 Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames);
2915 Cvar_RegisterVariable (&prvm_errordump);
2916 Cvar_RegisterVariable (&prvm_breakpointdump);
2917 Cvar_RegisterVariable (&prvm_reuseedicts_startuptime);
2918 Cvar_RegisterVariable (&prvm_reuseedicts_neverinsameframe);
2920 // COMMANDLINEOPTION: PRVM: -norunaway disables the runaway loop check (it might be impossible to exit DarkPlaces if used!)
2921 prvm_runawaycheck = !COM_CheckParm("-norunaway");
2931 void PRVM_Prog_Init(prvm_prog_t *prog)
2933 PRVM_Prog_Reset(prog);
2934 prog->leaktest_active = prvm_leaktest.integer != 0;
2937 // LordHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
2938 unsigned int PRVM_EDICT_NUM_ERROR(prvm_prog_t *prog, unsigned int n, const char *filename, int fileline)
2940 prog->error_cmd("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", prog->name, n, filename, fileline);
2944 #define PRVM_KNOWNSTRINGBASE 0x40000000
2946 const char *PRVM_GetString(prvm_prog_t *prog, int num)
2951 VM_Warning(prog, "PRVM_GetString: Invalid string offset (%i < 0)\n", num);
2954 else if (num < prog->stringssize)
2956 // constant string from progs.dat
2957 return prog->strings + num;
2959 else if (num <= prog->stringssize + prog->tempstringsbuf.maxsize)
2961 // tempstring returned by engine to QC (becomes invalid after returning to engine)
2962 num -= prog->stringssize;
2963 if (num < prog->tempstringsbuf.cursize)
2964 return (char *)prog->tempstringsbuf.data + num;
2967 VM_Warning(prog, "PRVM_GetString: Invalid temp-string offset (%i >= %i prog->tempstringsbuf.cursize)\n", num, prog->tempstringsbuf.cursize);
2971 else if (num & PRVM_KNOWNSTRINGBASE)
2974 num = num - PRVM_KNOWNSTRINGBASE;
2975 if (num >= 0 && num < prog->numknownstrings)
2977 if (!prog->knownstrings[num])
2979 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
2982 return prog->knownstrings[num];
2986 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
2992 // invalid string offset
2993 VM_Warning(prog, "PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
2998 const char *PRVM_ChangeEngineString(prvm_prog_t *prog, int i, const char *s)
3001 i = i - PRVM_KNOWNSTRINGBASE;
3002 if(i < 0 || i >= prog->numknownstrings)
3003 prog->error_cmd("PRVM_ChangeEngineString: s is not an engine string");
3004 old = prog->knownstrings[i];
3005 prog->knownstrings[i] = s;
3009 int PRVM_SetEngineString(prvm_prog_t *prog, const char *s)
3014 if (s >= prog->strings && s <= prog->strings + prog->stringssize)
3015 prog->error_cmd("PRVM_SetEngineString: s in prog->strings area");
3016 // if it's in the tempstrings area, use a reserved range
3017 // (otherwise we'd get millions of useless string offsets cluttering the database)
3018 if (s >= (char *)prog->tempstringsbuf.data && s < (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.maxsize)
3019 return prog->stringssize + (s - (char *)prog->tempstringsbuf.data);
3020 // see if it's a known string address
3021 for (i = 0;i < prog->numknownstrings;i++)
3022 if (prog->knownstrings[i] == s)
3023 return PRVM_KNOWNSTRINGBASE + i;
3024 // new unknown engine string
3025 if (developer_insane.integer)
3026 Con_DPrintf("new engine string %p = \"%s\"\n", s, s);
3027 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3028 if (!prog->knownstrings[i])
3030 if (i >= prog->numknownstrings)
3032 if (i >= prog->maxknownstrings)
3034 const char **oldstrings = prog->knownstrings;
3035 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
3036 const char **oldstrings_origin = prog->knownstrings_origin;
3037 prog->maxknownstrings += 128;
3038 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3039 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
3040 if(prog->leaktest_active)
3041 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3042 if (prog->numknownstrings)
3044 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
3045 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
3046 if(prog->leaktest_active)
3047 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
3050 prog->numknownstrings++;
3052 prog->firstfreeknownstring = i + 1;
3053 prog->knownstrings[i] = s;
3054 prog->knownstrings_freeable[i] = false;
3055 if(prog->leaktest_active)
3056 prog->knownstrings_origin[i] = NULL;
3057 return PRVM_KNOWNSTRINGBASE + i;
3060 // temp string handling
3062 // all tempstrings go into this buffer consecutively, and it is reset
3063 // whenever PRVM_ExecuteProgram returns to the engine
3064 // (technically each PRVM_ExecuteProgram call saves the cursize value and
3065 // restores it on return, so multiple recursive calls can share the same
3067 // the buffer size is automatically grown as needed
3069 int PRVM_SetTempString(prvm_prog_t *prog, const char *s)
3075 size = (int)strlen(s) + 1;
3076 if (developer_insane.integer)
3077 Con_DPrintf("PRVM_SetTempString: cursize %i, size %i\n", prog->tempstringsbuf.cursize, size);
3078 if (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
3080 sizebuf_t old = prog->tempstringsbuf;
3081 if (prog->tempstringsbuf.cursize + size >= 1<<28)
3082 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);
3083 prog->tempstringsbuf.maxsize = max(prog->tempstringsbuf.maxsize, 65536);
3084 while (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
3085 prog->tempstringsbuf.maxsize *= 2;
3086 if (prog->tempstringsbuf.maxsize != old.maxsize || prog->tempstringsbuf.data == NULL)
3088 Con_DPrintf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, prog->tempstringsbuf.maxsize/1024);
3089 prog->tempstringsbuf.data = (unsigned char *) Mem_Alloc(prog->progs_mempool, prog->tempstringsbuf.maxsize);
3091 memcpy(prog->tempstringsbuf.data, old.data, old.cursize);
3096 t = (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.cursize;
3098 prog->tempstringsbuf.cursize += size;
3099 return PRVM_SetEngineString(prog, t);
3102 int PRVM_AllocString(prvm_prog_t *prog, size_t bufferlength, char **pointer)
3107 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3108 if (!prog->knownstrings[i])
3110 if (i >= prog->numknownstrings)
3112 if (i >= prog->maxknownstrings)
3114 const char **oldstrings = prog->knownstrings;
3115 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
3116 const char **oldstrings_origin = prog->knownstrings_origin;
3117 prog->maxknownstrings += 128;
3118 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3119 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
3120 if(prog->leaktest_active)
3121 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3122 if (prog->numknownstrings)
3124 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
3125 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
3126 if(prog->leaktest_active)
3127 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
3130 Mem_Free((char **)oldstrings);
3131 if (oldstrings_freeable)
3132 Mem_Free((unsigned char *)oldstrings_freeable);
3133 if (oldstrings_origin)
3134 Mem_Free((char **)oldstrings_origin);
3136 prog->numknownstrings++;
3138 prog->firstfreeknownstring = i + 1;
3139 prog->knownstrings[i] = (char *)PRVM_Alloc(bufferlength);
3140 prog->knownstrings_freeable[i] = true;
3141 if(prog->leaktest_active)
3142 prog->knownstrings_origin[i] = PRVM_AllocationOrigin(prog);
3144 *pointer = (char *)(prog->knownstrings[i]);
3145 return PRVM_KNOWNSTRINGBASE + i;
3148 void PRVM_FreeString(prvm_prog_t *prog, int num)
3151 prog->error_cmd("PRVM_FreeString: attempt to free a NULL string");
3152 else if (num >= 0 && num < prog->stringssize)
3153 prog->error_cmd("PRVM_FreeString: attempt to free a constant string");
3154 else if (num >= PRVM_KNOWNSTRINGBASE && num < PRVM_KNOWNSTRINGBASE + prog->numknownstrings)
3156 num = num - PRVM_KNOWNSTRINGBASE;
3157 if (!prog->knownstrings[num])
3158 prog->error_cmd("PRVM_FreeString: attempt to free a non-existent or already freed string");
3159 if (!prog->knownstrings_freeable[num])
3160 prog->error_cmd("PRVM_FreeString: attempt to free a string owned by the engine");
3161 PRVM_Free((char *)prog->knownstrings[num]);
3162 if(prog->leaktest_active)
3163 if(prog->knownstrings_origin[num])
3164 PRVM_Free((char *)prog->knownstrings_origin[num]);
3165 prog->knownstrings[num] = NULL;
3166 prog->knownstrings_freeable[num] = false;
3167 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
3170 prog->error_cmd("PRVM_FreeString: invalid string offset %i", num);
3173 static qboolean PRVM_IsStringReferenced(prvm_prog_t *prog, string_t string)
3177 for (i = 0;i < prog->numglobaldefs;i++)
3179 ddef_t *d = &prog->globaldefs[i];
3180 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3182 if(string == PRVM_GLOBALFIELDSTRING(d->ofs))
3186 for(j = 0; j < prog->num_edicts; ++j)
3188 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3189 if (ed->priv.required->free)
3191 for (i=0; i<prog->numfielddefs; ++i)
3193 ddef_t *d = &prog->fielddefs[i];
3194 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3196 if(string == PRVM_EDICTFIELDSTRING(ed, d->ofs))
3204 static qboolean PRVM_IsEdictRelevant(prvm_prog_t *prog, prvm_edict_t *edict)
3208 if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
3209 return true; // world or clients
3210 if (prog == SVVM_prog)
3212 if(PRVM_serveredictfloat(edict, solid)) // can block other stuff, or is a trigger?
3214 if(PRVM_serveredictfloat(edict, modelindex)) // visible ent?
3216 if(PRVM_serveredictfloat(edict, effects)) // particle effect?
3218 if(PRVM_serveredictfunction(edict, think)) // has a think function?
3219 if(PRVM_serveredictfloat(edict, nextthink) > 0) // that actually will eventually run?
3221 if(PRVM_serveredictfloat(edict, takedamage))
3223 if(*prvm_leaktest_ignore_classnames.string)
3225 if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_serveredictstring(edict, classname)))))
3229 else if (prog == CLVM_prog)
3231 // TODO someone add more stuff here
3232 if(PRVM_clientedictfloat(edict, entnum)) // csqc networked
3234 if(PRVM_clientedictfloat(edict, modelindex)) // visible ent?
3236 if(PRVM_clientedictfloat(edict, effects)) // particle effect?
3238 if(PRVM_clientedictfunction(edict, think)) // has a think function?
3239 if(PRVM_clientedictfloat(edict, nextthink) > 0) // that actually will eventually run?
3241 if(*prvm_leaktest_ignore_classnames.string)
3243 if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_clientedictstring(edict, classname)))))
3249 // menu prog does not have classnames
3254 static qboolean PRVM_IsEdictReferenced(prvm_prog_t *prog, prvm_edict_t *edict, int mark)
3257 int edictnum = PRVM_NUM_FOR_EDICT(edict);
3258 const char *targetname = NULL;
3260 if (prog == SVVM_prog)
3261 targetname = PRVM_GetString(prog, PRVM_serveredictstring(edict, targetname));
3264 if(!*targetname) // ""
3269 for (i = 0;i < prog->numglobaldefs;i++)
3271 ddef_t *d = &prog->globaldefs[i];
3272 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3274 if(edictnum == PRVM_GLOBALFIELDEDICT(d->ofs))
3279 for(j = 0; j < prog->num_edicts; ++j)
3281 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3282 if (ed->priv.required->mark < mark)
3288 const char *target = PRVM_GetString(prog, PRVM_serveredictstring(ed, target));
3290 if(!strcmp(target, targetname))
3293 for (i=0; i<prog->numfielddefs; ++i)
3295 ddef_t *d = &prog->fielddefs[i];
3296 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3298 if(edictnum == PRVM_EDICTFIELDEDICT(ed, d->ofs))
3306 static void PRVM_MarkReferencedEdicts(prvm_prog_t *prog)
3312 for(j = 0; j < prog->num_edicts; ++j)
3314 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3315 if(ed->priv.required->free)
3317 ed->priv.required->mark = PRVM_IsEdictRelevant(prog, ed) ? 1 : 0;
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 if(ed->priv.required->mark)
3331 if(PRVM_IsEdictReferenced(prog, ed, stage))
3333 ed->priv.required->mark = stage + 1;
3340 Con_DPrintf("leak check used %d stages to find all references\n", stage);
3343 void PRVM_LeakTest(prvm_prog_t *prog)
3346 qboolean leaked = false;
3348 if(!prog->leaktest_active)
3352 for (i = 0; i < prog->numknownstrings; ++i)
3354 if(prog->knownstrings[i])
3355 if(prog->knownstrings_freeable[i])
3356 if(prog->knownstrings_origin[i])
3357 if(!PRVM_IsStringReferenced(prog, PRVM_KNOWNSTRINGBASE + i))
3359 Con_Printf("Unreferenced string found!\n Value: %s\n Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
3365 PRVM_MarkReferencedEdicts(prog);
3366 for(j = 0; j < prog->num_edicts; ++j)
3368 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3369 if(ed->priv.required->free)
3371 if(!ed->priv.required->mark)
3372 if(ed->priv.required->allocation_origin)
3374 Con_Printf("Unreferenced edict found!\n Allocated at: %s\n", ed->priv.required->allocation_origin);
3375 PRVM_ED_Print(prog, ed, NULL);
3380 ed->priv.required->mark = 0; // clear marks again when done
3383 for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
3385 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
3387 if(stringbuffer->origin)
3389 Con_Printf("Open string buffer handle found!\n Allocated at: %s\n", stringbuffer->origin);
3394 for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
3396 if(prog->openfiles[i])
3397 if(prog->openfiles_origin[i])
3399 Con_Printf("Open file handle found!\n Allocated at: %s\n", prog->openfiles_origin[i]);
3404 for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
3406 if(prog->opensearches[i])
3407 if(prog->opensearches_origin[i])
3409 Con_Printf("Open search handle found!\n Allocated at: %s\n", prog->opensearches_origin[i]);
3415 Con_Printf("Congratulations. No leaks found.\n");