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 progs.dat.LANGUAGENAME.po for string translations; when set to dump, progs.dat.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_backtraceforwarnings = {0, "prvm_backtraceforwarnings", "0", "print a backtrace for warnings too"};
39 cvar_t prvm_leaktest = {0, "prvm_leaktest", "0", "try to detect memory leaks in strings or entities"};
40 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)"};
41 cvar_t prvm_errordump = {0, "prvm_errordump", "0", "write a savegame on crash to crash-server.dmp"};
42 cvar_t prvm_breakpointdump = {0, "prvm_breakpointdump", "0", "write a savegame on breakpoint to breakpoint-server.dmp"};
43 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)"};
44 cvar_t prvm_reuseedicts_neverinsameframe = {0, "prvm_reuseedicts_neverinsameframe", "1", "never allows re-use of freed entity slots during same frame"};
46 static double prvm_reuseedicts_always_allow = 0;
47 qboolean prvm_runawaycheck = true;
49 //============================================================================
57 static void PRVM_MEM_Alloc(prvm_prog_t *prog)
61 // reserve space for the null entity aka world
62 // check bound of max_edicts
63 prog->max_edicts = bound(1 + prog->reserved_edicts, prog->max_edicts, prog->limit_edicts);
64 prog->num_edicts = bound(1 + prog->reserved_edicts, prog->num_edicts, prog->max_edicts);
66 // edictprivate_size has to be min as big prvm_edict_private_t
67 prog->edictprivate_size = max(prog->edictprivate_size,(int)sizeof(prvm_edict_private_t));
70 prog->edicts = (prvm_edict_t *)Mem_Alloc(prog->progs_mempool,prog->limit_edicts * sizeof(prvm_edict_t));
72 // alloc edict private space
73 prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size);
76 prog->entityfieldsarea = prog->entityfields * prog->max_edicts;
77 prog->edictsfields = (prvm_vec_t *)Mem_Alloc(prog->progs_mempool, prog->entityfieldsarea * sizeof(prvm_vec_t));
80 for(i = 0; i < prog->max_edicts; i++)
82 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
83 prog->edicts[i].fields.fp = prog->edictsfields + i * prog->entityfields;
89 PRVM_MEM_IncreaseEdicts
92 void PRVM_MEM_IncreaseEdicts(prvm_prog_t *prog)
96 if(prog->max_edicts >= prog->limit_edicts)
99 prog->begin_increase_edicts(prog);
102 prog->max_edicts = min(prog->max_edicts + 256, prog->limit_edicts);
104 prog->entityfieldsarea = prog->entityfields * prog->max_edicts;
105 prog->edictsfields = (prvm_vec_t*)Mem_Realloc(prog->progs_mempool, (void *)prog->edictsfields, prog->entityfieldsarea * sizeof(prvm_vec_t));
106 prog->edictprivate = (void *)Mem_Realloc(prog->progs_mempool, (void *)prog->edictprivate, prog->max_edicts * prog->edictprivate_size);
108 //set e and v pointers
109 for(i = 0; i < prog->max_edicts; i++)
111 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
112 prog->edicts[i].fields.fp = prog->edictsfields + i * prog->entityfields;
115 prog->end_increase_edicts(prog);
118 //============================================================================
121 int PRVM_ED_FindFieldOffset(prvm_prog_t *prog, const char *field)
124 d = PRVM_ED_FindField(prog, field);
130 int PRVM_ED_FindGlobalOffset(prvm_prog_t *prog, const char *global)
133 d = PRVM_ED_FindGlobal(prog, global);
139 func_t PRVM_ED_FindFunctionOffset(prvm_prog_t *prog, const char *function)
142 f = PRVM_ED_FindFunction(prog, function);
145 return (func_t)(f - prog->functions);
153 prvm_prog_t *PRVM_ProgFromString(const char *str)
155 if (!strcmp(str, "server"))
157 if (!strcmp(str, "client"))
159 if (!strcmp(str, "menu"))
166 PRVM_FriendlyProgFromString
169 prvm_prog_t *PRVM_FriendlyProgFromString(const char *str)
171 prvm_prog_t *prog = PRVM_ProgFromString(str);
174 Con_Printf("%s: unknown program name\n", str);
179 Con_Printf("%s: program is not loaded\n", str);
189 Sets everything to NULL
192 void PRVM_ED_ClearEdict(prvm_prog_t *prog, prvm_edict_t *e)
194 memset(e->fields.fp, 0, prog->entityfields * sizeof(prvm_vec_t));
195 e->priv.required->free = false;
197 // AK: Let the init_edict function determine if something needs to be initialized
198 prog->init_edict(prog, e);
201 const char *PRVM_AllocationOrigin(prvm_prog_t *prog)
204 if(prog->leaktest_active)
205 if(prog->depth > 0) // actually in QC code and not just parsing the entities block of a map/savegame
207 buf = (char *)PRVM_Alloc(128);
208 PRVM_ShortStackTrace(prog, buf, 128);
217 Returns if this particular edict could get allocated by PRVM_ED_Alloc
220 qboolean PRVM_ED_CanAlloc(prvm_prog_t *prog, prvm_edict_t *e)
222 if(!e->priv.required->free)
224 if(prvm_reuseedicts_always_allow == realtime)
226 if(realtime <= e->priv.required->freetime + 0.1 && prvm_reuseedicts_neverinsameframe.integer)
227 return false; // never allow reuse in same frame (causes networking trouble)
228 if(e->priv.required->freetime < prog->starttime + prvm_reuseedicts_startuptime.value)
230 if(realtime > e->priv.required->freetime + 1)
232 return false; // entity slot still blocked because the entity was freed less than one second ago
239 Either finds a free edict, or allocates a new one.
240 Try to avoid reusing an entity that was recently freed, because it
241 can cause the client to think the entity morphed into something else
242 instead of being removed and recreated, which can cause interpolated
243 angles and bad trails.
246 prvm_edict_t *PRVM_ED_Alloc(prvm_prog_t *prog)
251 // the client qc dont need maxclients
252 // thus it doesnt need to use svs.maxclients
253 // AK: changed i=svs.maxclients+1
254 // AK: changed so the edict 0 wont spawn -> used as reserved/world entity
255 // although the menu/client has no world
256 for (i = prog->reserved_edicts + 1;i < prog->num_edicts;i++)
258 e = PRVM_EDICT_NUM(i);
259 if(PRVM_ED_CanAlloc(prog, e))
261 PRVM_ED_ClearEdict (prog, e);
262 e->priv.required->allocation_origin = PRVM_AllocationOrigin(prog);
267 if (i == prog->limit_edicts)
268 prog->error_cmd("%s: PRVM_ED_Alloc: no free edicts", prog->name);
271 if (prog->num_edicts >= prog->max_edicts)
272 PRVM_MEM_IncreaseEdicts(prog);
274 e = PRVM_EDICT_NUM(i);
275 PRVM_ED_ClearEdict(prog, e);
277 e->priv.required->allocation_origin = PRVM_AllocationOrigin(prog);
286 Marks the edict as free
287 FIXME: walk all entities and NULL out references to this entity
290 void PRVM_ED_Free(prvm_prog_t *prog, prvm_edict_t *ed)
292 // dont delete the null entity (world) or reserved edicts
293 if (ed - prog->edicts <= prog->reserved_edicts)
296 prog->free_edict(prog, ed);
298 ed->priv.required->free = true;
299 ed->priv.required->freetime = realtime;
300 if(ed->priv.required->allocation_origin)
302 Mem_Free((char *)ed->priv.required->allocation_origin);
303 ed->priv.required->allocation_origin = NULL;
307 //===========================================================================
314 static ddef_t *PRVM_ED_GlobalAtOfs (prvm_prog_t *prog, int ofs)
319 for (i = 0;i < prog->numglobaldefs;i++)
321 def = &prog->globaldefs[i];
333 ddef_t *PRVM_ED_FieldAtOfs (prvm_prog_t *prog, int ofs)
338 for (i = 0;i < prog->numfielddefs;i++)
340 def = &prog->fielddefs[i];
352 ddef_t *PRVM_ED_FindField (prvm_prog_t *prog, const char *name)
357 for (i = 0;i < prog->numfielddefs;i++)
359 def = &prog->fielddefs[i];
360 if (!strcmp(PRVM_GetString(prog, def->s_name), name))
371 ddef_t *PRVM_ED_FindGlobal (prvm_prog_t *prog, const char *name)
376 for (i = 0;i < prog->numglobaldefs;i++)
378 def = &prog->globaldefs[i];
379 if (!strcmp(PRVM_GetString(prog, def->s_name), name))
391 mfunction_t *PRVM_ED_FindFunction (prvm_prog_t *prog, const char *name)
396 for (i = 0;i < prog->numfunctions;i++)
398 func = &prog->functions[i];
399 if (!strcmp(PRVM_GetString(prog, func->s_name), name))
410 Returns a string describing *data in a type specific manner
413 static char *PRVM_ValueString (prvm_prog_t *prog, etype_t type, prvm_eval_t *val, char *line, size_t linelength)
419 type = (etype_t)((int) type & ~DEF_SAVEGLOBAL);
424 strlcpy (line, PRVM_GetString (prog, val->string), linelength);
428 if (n < 0 || n >= prog->max_edicts)
429 dpsnprintf (line, linelength, "entity %i (invalid!)", n);
431 dpsnprintf (line, linelength, "entity %i", n);
434 f = prog->functions + val->function;
435 dpsnprintf (line, linelength, "%s()", PRVM_GetString(prog, f->s_name));
438 def = PRVM_ED_FieldAtOfs ( prog, val->_int );
439 dpsnprintf (line, linelength, ".%s", PRVM_GetString(prog, def->s_name));
442 dpsnprintf (line, linelength, "void");
445 // LordHavoc: changed from %5.1f to %10.4f
446 dpsnprintf (line, linelength, FLOAT_LOSSLESS_FORMAT, val->_float);
449 // LordHavoc: changed from %5.1f to %10.4f
450 dpsnprintf (line, linelength, "'" VECTOR_LOSSLESS_FORMAT "'", val->vector[0], val->vector[1], val->vector[2]);
453 dpsnprintf (line, linelength, "pointer");
456 dpsnprintf (line, linelength, "bad type %i", (int) type);
467 Returns a string describing *data in a type specific manner
468 Easier to parse than PR_ValueString
471 char *PRVM_UglyValueString (prvm_prog_t *prog, etype_t type, prvm_eval_t *val, char *line, size_t linelength)
478 type = (etype_t)((int)type & ~DEF_SAVEGLOBAL);
483 // Parse the string a bit to turn special characters
484 // (like newline, specifically) into escape codes,
485 // this fixes saving games from various mods
486 s = PRVM_GetString (prog, val->string);
487 for (i = 0;i < (int)linelength - 2 && *s;)
516 dpsnprintf (line, linelength, "%i", val->edict);
519 f = prog->functions + val->function;
520 strlcpy (line, PRVM_GetString (prog, f->s_name), linelength);
523 def = PRVM_ED_FieldAtOfs ( prog, val->_int );
524 dpsnprintf (line, linelength, ".%s", PRVM_GetString(prog, def->s_name));
527 dpsnprintf (line, linelength, "void");
530 dpsnprintf (line, linelength, FLOAT_LOSSLESS_FORMAT, val->_float);
533 dpsnprintf (line, linelength, VECTOR_LOSSLESS_FORMAT, val->vector[0], val->vector[1], val->vector[2]);
536 dpsnprintf (line, linelength, "bad type %i", type);
547 Returns a string with a description and the contents of a global,
548 padded to 20 field width
551 char *PRVM_GlobalString (prvm_prog_t *prog, int ofs, char *line, size_t linelength)
557 char valuebuf[MAX_INPUTLINE];
559 val = (prvm_eval_t *)&prog->globals.fp[ofs];
560 def = PRVM_ED_GlobalAtOfs(prog, ofs);
562 dpsnprintf (line, linelength, "GLOBAL%i", ofs);
565 s = PRVM_ValueString (prog, (etype_t)def->type, val, valuebuf, sizeof(valuebuf));
566 dpsnprintf (line, linelength, "%s (=%s)", PRVM_GetString(prog, def->s_name), s);
570 //for ( ; i<20 ; i++)
571 // strcat (line," ");
577 char *PRVM_GlobalStringNoContents (prvm_prog_t *prog, int ofs, char *line, size_t linelength)
582 def = PRVM_ED_GlobalAtOfs(prog, ofs);
584 dpsnprintf (line, linelength, "GLOBAL%i", ofs);
586 dpsnprintf (line, linelength, "%s", PRVM_GetString(prog, def->s_name));
589 //for ( ; i<20 ; i++)
590 // strcat (line," ");
604 // LordHavoc: optimized this to print out much more quickly (tempstring)
605 // LordHavoc: changed to print out every 4096 characters (incase there are a lot of fields to print)
606 void PRVM_ED_Print(prvm_prog_t *prog, prvm_edict_t *ed, const char *wildcard_fieldname)
614 char tempstring[MAX_INPUTLINE], tempstring2[260]; // temporary string buffers
615 char valuebuf[MAX_INPUTLINE];
617 if (ed->priv.required->free)
619 Con_Printf("%s: FREE\n",prog->name);
624 dpsnprintf(tempstring, sizeof(tempstring), "\n%s EDICT %i:\n", prog->name, PRVM_NUM_FOR_EDICT(ed));
625 for (i = 1;i < prog->numfielddefs;i++)
627 d = &prog->fielddefs[i];
628 name = PRVM_GetString(prog, d->s_name);
629 if(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
630 continue; // skip _x, _y, _z vars
632 // Check Field Name Wildcard
633 if(wildcard_fieldname)
634 if( !matchpattern(name, wildcard_fieldname, 1) )
635 // Didn't match; skip
638 val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
640 // if the value is still all 0, skip the field
641 type = d->type & ~DEF_SAVEGLOBAL;
643 for (j=0 ; j<prvm_type_size[type] ; j++)
646 if (j == prvm_type_size[type])
649 if (strlen(name) > sizeof(tempstring2)-4)
651 memcpy (tempstring2, name, sizeof(tempstring2)-4);
652 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
653 tempstring2[sizeof(tempstring2)-1] = 0;
656 strlcat(tempstring, name, sizeof(tempstring));
657 for (l = strlen(name);l < 14;l++)
658 strlcat(tempstring, " ", sizeof(tempstring));
659 strlcat(tempstring, " ", sizeof(tempstring));
661 name = PRVM_ValueString(prog, (etype_t)d->type, val, valuebuf, sizeof(valuebuf));
662 if (strlen(name) > sizeof(tempstring2)-4)
664 memcpy (tempstring2, name, sizeof(tempstring2)-4);
665 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
666 tempstring2[sizeof(tempstring2)-1] = 0;
669 strlcat(tempstring, name, sizeof(tempstring));
670 strlcat(tempstring, "\n", sizeof(tempstring));
671 if (strlen(tempstring) >= sizeof(tempstring)/2)
673 Con_Print(tempstring);
678 Con_Print(tempstring);
688 extern cvar_t developer_entityparsing;
689 void PRVM_ED_Write (prvm_prog_t *prog, qfile_t *f, prvm_edict_t *ed)
697 char valuebuf[MAX_INPUTLINE];
701 if (ed->priv.required->free)
707 for (i = 1;i < prog->numfielddefs;i++)
709 d = &prog->fielddefs[i];
710 name = PRVM_GetString(prog, d->s_name);
712 if(developer_entityparsing.integer)
713 Con_Printf("PRVM_ED_Write: at entity %d field %s\n", PRVM_NUM_FOR_EDICT(ed), name);
715 //if(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
716 if(strlen(name) > 1 && name[strlen(name)-2] == '_')
717 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?)
719 val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
721 // if the value is still all 0, skip the field
722 type = d->type & ~DEF_SAVEGLOBAL;
723 for (j=0 ; j<prvm_type_size[type] ; j++)
726 if (j == prvm_type_size[type])
729 FS_Printf(f,"\"%s\" ",name);
730 prog->statestring = va(vabuf, sizeof(vabuf), "PRVM_ED_Write, ent=%d, name=%s", i, name);
731 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString(prog, (etype_t)d->type, val, valuebuf, sizeof(valuebuf)));
732 prog->statestring = NULL;
738 void PRVM_ED_PrintNum (prvm_prog_t *prog, int ent, const char *wildcard_fieldname)
740 PRVM_ED_Print(prog, PRVM_EDICT_NUM(ent), wildcard_fieldname);
745 PRVM_ED_PrintEdicts_f
747 For debugging, prints all the entities in the current server
750 void PRVM_ED_PrintEdicts_f (void)
754 const char *wildcard_fieldname;
756 if(Cmd_Argc() < 2 || Cmd_Argc() > 3)
758 Con_Print("prvm_edicts <program name> <optional field name wildcard>\n");
762 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
766 wildcard_fieldname = Cmd_Argv(2);
768 wildcard_fieldname = NULL;
770 Con_Printf("%s: %i entities\n", prog->name, prog->num_edicts);
771 for (i=0 ; i<prog->num_edicts ; i++)
772 PRVM_ED_PrintNum (prog, i, wildcard_fieldname);
779 For debugging, prints a single edict
782 static void PRVM_ED_PrintEdict_f (void)
786 const char *wildcard_fieldname;
788 if(Cmd_Argc() < 3 || Cmd_Argc() > 4)
790 Con_Print("prvm_edict <program name> <edict number> <optional field name wildcard>\n");
794 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
797 i = atoi (Cmd_Argv(2));
798 if (i >= prog->num_edicts)
800 Con_Print("Bad edict number\n");
804 // Optional Wildcard Provided
805 wildcard_fieldname = Cmd_Argv(3);
808 wildcard_fieldname = NULL;
809 PRVM_ED_PrintNum (prog, i, wildcard_fieldname);
819 // 2 possibilities : 1. just displaying the active edict count
820 // 2. making a function pointer [x]
821 static void PRVM_ED_Count_f (void)
827 Con_Print("prvm_count <program name>\n");
831 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
834 prog->count_edicts(prog);
838 ==============================================================================
842 FIXME: need to tag constants, doesn't really work
843 ==============================================================================
851 void PRVM_ED_WriteGlobals (prvm_prog_t *prog, qfile_t *f)
858 char valuebuf[MAX_INPUTLINE];
861 for (i = 0;i < prog->numglobaldefs;i++)
863 def = &prog->globaldefs[i];
865 if ( !(def->type & DEF_SAVEGLOBAL) )
867 type &= ~DEF_SAVEGLOBAL;
869 if (type != ev_string && type != ev_float && type != ev_entity)
872 name = PRVM_GetString(prog, def->s_name);
874 if(developer_entityparsing.integer)
875 Con_Printf("PRVM_ED_WriteGlobals: at global %s\n", name);
877 prog->statestring = va(vabuf, sizeof(vabuf), "PRVM_ED_WriteGlobals, name=%s", name);
878 FS_Printf(f,"\"%s\" ", name);
879 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString(prog, (etype_t)type, (prvm_eval_t *)&prog->globals.fp[def->ofs], valuebuf, sizeof(valuebuf)));
880 prog->statestring = NULL;
890 void PRVM_ED_ParseGlobals (prvm_prog_t *prog, const char *data)
892 char keyname[MAX_INPUTLINE];
898 if (!COM_ParseToken_Simple(&data, false, false, true))
899 prog->error_cmd("PRVM_ED_ParseGlobals: EOF without closing brace");
900 if (com_token[0] == '}')
903 if (developer_entityparsing.integer)
904 Con_Printf("Key: \"%s\"", com_token);
906 strlcpy (keyname, com_token, sizeof(keyname));
909 if (!COM_ParseToken_Simple(&data, false, true, true))
910 prog->error_cmd("PRVM_ED_ParseGlobals: EOF without closing brace");
912 if (developer_entityparsing.integer)
913 Con_Printf(" \"%s\"\n", com_token);
915 if (com_token[0] == '}')
916 prog->error_cmd("PRVM_ED_ParseGlobals: closing brace without data");
918 key = PRVM_ED_FindGlobal (prog, keyname);
921 Con_DPrintf("'%s' is not a global on %s\n", keyname, prog->name);
925 if (!PRVM_ED_ParseEpair(prog, NULL, key, com_token, true))
926 prog->error_cmd("PRVM_ED_ParseGlobals: parse error");
930 //============================================================================
937 Can parse either fields or globals
938 returns false if error
941 qboolean PRVM_ED_ParseEpair(prvm_prog_t *prog, prvm_edict_t *ent, ddef_t *key, const char *s, qboolean parsebackslash)
950 val = (prvm_eval_t *)(ent->fields.fp + key->ofs);
952 val = (prvm_eval_t *)(prog->globals.fp + key->ofs);
953 switch (key->type & ~DEF_SAVEGLOBAL)
956 l = (int)strlen(s) + 1;
957 val->string = PRVM_AllocString(prog, l, &new_p);
958 for (i = 0;i < l;i++)
960 if (s[i] == '\\' && s[i+1] && parsebackslash)
965 else if (s[i] == 'r')
976 while (*s && ISWHITESPACE(*s))
978 val->_float = atof(s);
982 for (i = 0;i < 3;i++)
984 while (*s && ISWHITESPACE(*s))
988 val->vector[i] = atof(s);
989 while (!ISWHITESPACE(*s))
997 while (*s && ISWHITESPACE(*s))
1000 if (i >= prog->limit_edicts)
1001 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);
1002 while (i >= prog->max_edicts)
1003 PRVM_MEM_IncreaseEdicts(prog);
1004 // if IncreaseEdicts was called the base pointer needs to be updated
1006 val = (prvm_eval_t *)(ent->fields.fp + key->ofs);
1007 val->edict = PRVM_EDICT_TO_PROG(PRVM_EDICT_NUM((int)i));
1013 Con_DPrintf("PRVM_ED_ParseEpair: Bogus field name %s in %s\n", s, prog->name);
1016 def = PRVM_ED_FindField(prog, s + 1);
1019 Con_DPrintf("PRVM_ED_ParseEpair: Can't find field %s in %s\n", s, prog->name);
1022 val->_int = def->ofs;
1026 func = PRVM_ED_FindFunction(prog, s);
1029 Con_Printf("PRVM_ED_ParseEpair: Can't find function %s in %s\n", s, prog->name);
1032 val->function = func - prog->functions;
1036 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);
1046 Console command to send a string to QC function GameCommand of the
1050 sv_cmd adminmsg 3 "do not teamkill"
1051 cl_cmd someclientcommand
1052 menu_cmd somemenucommand
1054 All progs can support this extension; sg calls it in server QC, cg in client
1058 static void PRVM_GameCommand(const char *whichprogs, const char *whichcmd)
1063 Con_Printf("%s text...\n", whichcmd);
1067 if (!(prog = PRVM_FriendlyProgFromString(whichprogs)))
1070 if(!PRVM_allfunction(GameCommand))
1072 Con_Printf("%s program do not support GameCommand!\n", whichprogs);
1076 int restorevm_tempstringsbuf_cursize;
1081 restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
1082 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(prog, s ? s : "");
1083 prog->ExecuteProgram(prog, PRVM_allfunction(GameCommand), "QC function GameCommand is missing");
1084 prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1087 static void PRVM_GameCommand_Server_f(void)
1089 PRVM_GameCommand("server", "sv_cmd");
1091 static void PRVM_GameCommand_Client_f(void)
1093 PRVM_GameCommand("client", "cl_cmd");
1095 static void PRVM_GameCommand_Menu_f(void)
1097 PRVM_GameCommand("menu", "menu_cmd");
1104 Console command to load a field of a specified edict
1107 static void PRVM_ED_EdictGet_f(void)
1114 char valuebuf[MAX_INPUTLINE];
1116 if(Cmd_Argc() != 4 && Cmd_Argc() != 5)
1118 Con_Print("prvm_edictget <program name> <edict number> <field> [<cvar>]\n");
1122 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
1125 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1127 if((key = PRVM_ED_FindField(prog, Cmd_Argv(3))) == 0)
1129 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1133 v = (prvm_eval_t *)(ed->fields.fp + key->ofs);
1134 s = PRVM_UglyValueString(prog, (etype_t)key->type, v, valuebuf, sizeof(valuebuf));
1137 cvar_t *cvar = Cvar_FindVar(Cmd_Argv(4));
1138 if (cvar && cvar->flags & CVAR_READONLY)
1140 Con_Printf("prvm_edictget: %s is read-only\n", cvar->name);
1143 Cvar_Get(Cmd_Argv(4), s, 0, NULL);
1146 Con_Printf("%s\n", s);
1152 static void PRVM_ED_GlobalGet_f(void)
1158 char valuebuf[MAX_INPUTLINE];
1160 if(Cmd_Argc() != 3 && Cmd_Argc() != 4)
1162 Con_Print("prvm_globalget <program name> <global> [<cvar>]\n");
1166 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
1169 key = PRVM_ED_FindGlobal(prog, Cmd_Argv(2));
1172 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
1176 v = (prvm_eval_t *) &prog->globals.fp[key->ofs];
1177 s = PRVM_UglyValueString(prog, (etype_t)key->type, v, valuebuf, sizeof(valuebuf));
1180 cvar_t *cvar = Cvar_FindVar(Cmd_Argv(3));
1181 if (cvar && cvar->flags & CVAR_READONLY)
1183 Con_Printf("prvm_globalget: %s is read-only\n", cvar->name);
1186 Cvar_Get(Cmd_Argv(3), s, 0, NULL);
1189 Con_Printf("%s\n", s);
1199 Console command to set a field of a specified edict
1202 static void PRVM_ED_EdictSet_f(void)
1210 Con_Print("prvm_edictset <program name> <edict number> <field> <value>\n");
1214 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
1217 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1219 if((key = PRVM_ED_FindField(prog, Cmd_Argv(3))) == 0)
1220 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1222 PRVM_ED_ParseEpair(prog, ed, key, Cmd_Argv(4), true);
1226 ====================
1229 Parses an edict out of the given string, returning the new position
1230 ed should be a properly initialized empty edict.
1231 Used for initial level load and for savegames.
1232 ====================
1234 const char *PRVM_ED_ParseEdict (prvm_prog_t *prog, const char *data, prvm_edict_t *ent)
1244 // go through all the dictionary pairs
1248 if (!COM_ParseToken_Simple(&data, false, false, true))
1249 prog->error_cmd("PRVM_ED_ParseEdict: EOF without closing brace");
1250 if (developer_entityparsing.integer)
1251 Con_Printf("Key: \"%s\"", com_token);
1252 if (com_token[0] == '}')
1255 // anglehack is to allow QuakeEd to write single scalar angles
1256 // and allow them to be turned into vectors. (FIXME...)
1257 if (!strcmp(com_token, "angle"))
1259 strlcpy (com_token, "angles", sizeof(com_token));
1265 // FIXME: change light to _light to get rid of this hack
1266 if (!strcmp(com_token, "light"))
1267 strlcpy (com_token, "light_lev", sizeof(com_token)); // hack for single light def
1269 strlcpy (keyname, com_token, sizeof(keyname));
1271 // another hack to fix keynames with trailing spaces
1272 n = strlen(keyname);
1273 while (n && keyname[n-1] == ' ')
1280 if (!COM_ParseToken_Simple(&data, false, false, true))
1281 prog->error_cmd("PRVM_ED_ParseEdict: EOF without closing brace");
1282 if (developer_entityparsing.integer)
1283 Con_Printf(" \"%s\"\n", com_token);
1285 if (com_token[0] == '}')
1286 prog->error_cmd("PRVM_ED_ParseEdict: closing brace without data");
1290 // ignore attempts to set key "" (this problem occurs in nehahra neh1m8.bsp)
1294 // keynames with a leading underscore are used for utility comments,
1295 // and are immediately discarded by quake
1296 if (keyname[0] == '_')
1299 key = PRVM_ED_FindField (prog, keyname);
1302 Con_DPrintf("%s: '%s' is not a field\n", prog->name, keyname);
1309 strlcpy (temp, com_token, sizeof(temp));
1310 dpsnprintf (com_token, sizeof(com_token), "0 %s 0", temp);
1313 if (!PRVM_ED_ParseEpair(prog, ent, key, com_token, strcmp(keyname, "wad") != 0))
1314 prog->error_cmd("PRVM_ED_ParseEdict: parse error");
1318 ent->priv.required->free = true;
1326 PRVM_ED_LoadFromFile
1328 The entities are directly placed in the array, rather than allocated with
1329 PRVM_ED_Alloc, because otherwise an error loading the map would have entity
1330 number references out of order.
1332 Creates a server's entity / program execution context by
1333 parsing textual entity definitions out of an ent file.
1335 Used for both fresh maps and savegame loads. A fresh map would also need
1336 to call PRVM_ED_CallSpawnFunctions () to let the objects initialize themselves.
1339 void PRVM_ED_LoadFromFile (prvm_prog_t *prog, const char *data)
1342 int parsed, inhibited, spawned, died;
1343 const char *funcname;
1352 prvm_reuseedicts_always_allow = realtime;
1357 // parse the opening brace
1358 if (!COM_ParseToken_Simple(&data, false, false, true))
1360 if (com_token[0] != '{')
1361 prog->error_cmd("PRVM_ED_LoadFromFile: %s: found %s when expecting {", prog->name, com_token);
1363 // CHANGED: this is not conform to PR_LoadFromFile
1364 if(prog->loadintoworld)
1366 prog->loadintoworld = false;
1367 ent = PRVM_EDICT_NUM(0);
1370 ent = PRVM_ED_Alloc(prog);
1373 if (ent != prog->edicts) // hack
1374 memset (ent->fields.fp, 0, prog->entityfields * sizeof(prvm_vec_t));
1376 data = PRVM_ED_ParseEdict (prog, data, ent);
1379 // remove the entity ?
1380 if(!prog->load_edict(prog, ent))
1382 PRVM_ED_Free(prog, ent);
1387 if (PRVM_serverfunction(SV_OnEntityPreSpawnFunction))
1390 PRVM_serverglobalfloat(time) = sv.time;
1391 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1392 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityPreSpawnFunction), "QC function SV_OnEntityPreSpawnFunction is missing");
1395 if(ent->priv.required->free)
1402 // immediately call spawn function, but only if there is a self global and a classname
1404 if(!ent->priv.required->free)
1406 if (!PRVM_alledictstring(ent, classname))
1408 Con_Print("No classname for:\n");
1409 PRVM_ED_Print(prog, ent, NULL);
1410 PRVM_ED_Free (prog, ent);
1414 // look for the spawn function
1415 funcname = PRVM_GetString(prog, PRVM_alledictstring(ent, classname));
1416 func = PRVM_ED_FindFunction (prog, va(vabuf, sizeof(vabuf), "spawnfunc_%s", funcname));
1418 if(!PRVM_allglobalfloat(require_spawnfunc_prefix))
1419 func = PRVM_ED_FindFunction (prog, funcname);
1423 // check for OnEntityNoSpawnFunction
1424 if (PRVM_serverfunction(SV_OnEntityNoSpawnFunction))
1427 PRVM_serverglobalfloat(time) = sv.time;
1428 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1429 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityNoSpawnFunction), "QC function SV_OnEntityNoSpawnFunction is missing");
1433 if (developer.integer > 0) // don't confuse non-developers with errors
1435 Con_Print("No spawn function for:\n");
1436 PRVM_ED_Print(prog, ent, NULL);
1438 PRVM_ED_Free (prog, ent);
1439 continue; // not included in "inhibited" count
1445 PRVM_serverglobalfloat(time) = sv.time;
1446 PRVM_allglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1447 prog->ExecuteProgram(prog, func - prog->functions, "");
1451 if(!ent->priv.required->free)
1452 if (PRVM_serverfunction(SV_OnEntityPostSpawnFunction))
1455 PRVM_serverglobalfloat(time) = sv.time;
1456 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1457 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityPostSpawnFunction), "QC function SV_OnEntityPostSpawnFunction is missing");
1461 if (ent->priv.required->free)
1465 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);
1467 prvm_reuseedicts_always_allow = 0;
1470 static void PRVM_FindOffsets(prvm_prog_t *prog)
1472 // field and global searches use -1 for NULL
1473 memset(&prog->fieldoffsets, -1, sizeof(prog->fieldoffsets));
1474 memset(&prog->globaloffsets, -1, sizeof(prog->globaloffsets));
1475 // function searches use 0 for NULL
1476 memset(&prog->funcoffsets, 0, sizeof(prog->funcoffsets));
1477 #define PRVM_DECLARE_serverglobalfloat(x)
1478 #define PRVM_DECLARE_serverglobalvector(x)
1479 #define PRVM_DECLARE_serverglobalstring(x)
1480 #define PRVM_DECLARE_serverglobaledict(x)
1481 #define PRVM_DECLARE_serverglobalfunction(x)
1482 #define PRVM_DECLARE_clientglobalfloat(x)
1483 #define PRVM_DECLARE_clientglobalvector(x)
1484 #define PRVM_DECLARE_clientglobalstring(x)
1485 #define PRVM_DECLARE_clientglobaledict(x)
1486 #define PRVM_DECLARE_clientglobalfunction(x)
1487 #define PRVM_DECLARE_menuglobalfloat(x)
1488 #define PRVM_DECLARE_menuglobalvector(x)
1489 #define PRVM_DECLARE_menuglobalstring(x)
1490 #define PRVM_DECLARE_menuglobaledict(x)
1491 #define PRVM_DECLARE_menuglobalfunction(x)
1492 #define PRVM_DECLARE_serverfieldfloat(x)
1493 #define PRVM_DECLARE_serverfieldvector(x)
1494 #define PRVM_DECLARE_serverfieldstring(x)
1495 #define PRVM_DECLARE_serverfieldedict(x)
1496 #define PRVM_DECLARE_serverfieldfunction(x)
1497 #define PRVM_DECLARE_clientfieldfloat(x)
1498 #define PRVM_DECLARE_clientfieldvector(x)
1499 #define PRVM_DECLARE_clientfieldstring(x)
1500 #define PRVM_DECLARE_clientfieldedict(x)
1501 #define PRVM_DECLARE_clientfieldfunction(x)
1502 #define PRVM_DECLARE_menufieldfloat(x)
1503 #define PRVM_DECLARE_menufieldvector(x)
1504 #define PRVM_DECLARE_menufieldstring(x)
1505 #define PRVM_DECLARE_menufieldedict(x)
1506 #define PRVM_DECLARE_menufieldfunction(x)
1507 #define PRVM_DECLARE_serverfunction(x)
1508 #define PRVM_DECLARE_clientfunction(x)
1509 #define PRVM_DECLARE_menufunction(x)
1510 #define PRVM_DECLARE_field(x) prog->fieldoffsets.x = PRVM_ED_FindFieldOffset(prog, #x);
1511 #define PRVM_DECLARE_global(x) prog->globaloffsets.x = PRVM_ED_FindGlobalOffset(prog, #x);
1512 #define PRVM_DECLARE_function(x) prog->funcoffsets.x = PRVM_ED_FindFunctionOffset(prog, #x);
1513 #include "prvm_offsets.h"
1514 #undef PRVM_DECLARE_serverglobalfloat
1515 #undef PRVM_DECLARE_serverglobalvector
1516 #undef PRVM_DECLARE_serverglobalstring
1517 #undef PRVM_DECLARE_serverglobaledict
1518 #undef PRVM_DECLARE_serverglobalfunction
1519 #undef PRVM_DECLARE_clientglobalfloat
1520 #undef PRVM_DECLARE_clientglobalvector
1521 #undef PRVM_DECLARE_clientglobalstring
1522 #undef PRVM_DECLARE_clientglobaledict
1523 #undef PRVM_DECLARE_clientglobalfunction
1524 #undef PRVM_DECLARE_menuglobalfloat
1525 #undef PRVM_DECLARE_menuglobalvector
1526 #undef PRVM_DECLARE_menuglobalstring
1527 #undef PRVM_DECLARE_menuglobaledict
1528 #undef PRVM_DECLARE_menuglobalfunction
1529 #undef PRVM_DECLARE_serverfieldfloat
1530 #undef PRVM_DECLARE_serverfieldvector
1531 #undef PRVM_DECLARE_serverfieldstring
1532 #undef PRVM_DECLARE_serverfieldedict
1533 #undef PRVM_DECLARE_serverfieldfunction
1534 #undef PRVM_DECLARE_clientfieldfloat
1535 #undef PRVM_DECLARE_clientfieldvector
1536 #undef PRVM_DECLARE_clientfieldstring
1537 #undef PRVM_DECLARE_clientfieldedict
1538 #undef PRVM_DECLARE_clientfieldfunction
1539 #undef PRVM_DECLARE_menufieldfloat
1540 #undef PRVM_DECLARE_menufieldvector
1541 #undef PRVM_DECLARE_menufieldstring
1542 #undef PRVM_DECLARE_menufieldedict
1543 #undef PRVM_DECLARE_menufieldfunction
1544 #undef PRVM_DECLARE_serverfunction
1545 #undef PRVM_DECLARE_clientfunction
1546 #undef PRVM_DECLARE_menufunction
1547 #undef PRVM_DECLARE_field
1548 #undef PRVM_DECLARE_global
1549 #undef PRVM_DECLARE_function
1554 typedef struct dpfield_s
1561 #define DPFIELDS (sizeof(dpfields) / sizeof(dpfield_t))
1563 dpfield_t dpfields[] =
1574 #define PO_HASHSIZE 16384
1575 typedef struct po_string_s
1578 struct po_string_s *nextonhashchain;
1583 po_string_t *hashtable[PO_HASHSIZE];
1586 static void PRVM_PO_UnparseString(char *out, const char *in, size_t outsize)
1595 case '\a': if(outsize >= 2) { *out++ = '\\'; *out++ = 'a'; outsize -= 2; } break;
1596 case '\b': if(outsize >= 2) { *out++ = '\\'; *out++ = 'b'; outsize -= 2; } break;
1597 case '\t': if(outsize >= 2) { *out++ = '\\'; *out++ = 't'; outsize -= 2; } break;
1598 case '\r': if(outsize >= 2) { *out++ = '\\'; *out++ = 'r'; outsize -= 2; } break;
1599 case '\n': if(outsize >= 2) { *out++ = '\\'; *out++ = 'n'; outsize -= 2; } break;
1600 case '\\': if(outsize >= 2) { *out++ = '\\'; *out++ = '\\'; outsize -= 2; } break;
1601 case '"': if(outsize >= 2) { *out++ = '\\'; *out++ = '"'; outsize -= 2; } break;
1603 if(*in >= 0 && *in <= 0x1F)
1608 *out++ = '0' + ((*in & 0700) >> 6);
1609 *out++ = '0' + ((*in & 0070) >> 3);
1610 *out++ = '0' + (*in & 0007) ;
1627 static void PRVM_PO_ParseString(char *out, const char *in, size_t outsize)
1640 case 'a': if(outsize > 0) { *out++ = '\a'; --outsize; } break;
1641 case 'b': if(outsize > 0) { *out++ = '\b'; --outsize; } break;
1642 case 't': if(outsize > 0) { *out++ = '\t'; --outsize; } break;
1643 case 'r': if(outsize > 0) { *out++ = '\r'; --outsize; } break;
1644 case 'n': if(outsize > 0) { *out++ = '\n'; --outsize; } break;
1645 case '\\': if(outsize > 0) { *out++ = '\\'; --outsize; } break;
1646 case '"': if(outsize > 0) { *out++ = '"'; --outsize; } break;
1647 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
1651 if(*in >= '0' && *in <= '7')
1654 *out = (*out << 3) | (*in - '0');
1657 if(*in >= '0' && *in <= '7')
1660 *out = (*out << 3) | (*in - '0');
1671 if(outsize > 0) { *out++ = *in; --outsize; }
1686 static po_t *PRVM_PO_Load(const char *filename, mempool_t *pool)
1691 char inbuf[MAX_INPUTLINE];
1692 char decodedbuf[MAX_INPUTLINE];
1695 po_string_t thisstr;
1696 const char *buf = (const char *) FS_LoadFile(filename, pool, true, NULL);
1701 memset(&thisstr, 0, sizeof(thisstr)); // hush compiler warning
1703 po = (po_t *)Mem_Alloc(pool, sizeof(*po));
1704 memset(po, 0, sizeof(*po));
1712 p = strchr(p, '\n');
1718 if(*p == '\r' || *p == '\n')
1723 if(!strncmp(p, "msgid \"", 7))
1728 else if(!strncmp(p, "msgstr \"", 8))
1735 p = strchr(p, '\n');
1745 q = strchr(p, '\n');
1752 if((size_t)(q - p) >= (size_t) sizeof(inbuf))
1754 strlcpy(inbuf, p, q - p); // not - 1, because this adds a NUL
1755 PRVM_PO_ParseString(decodedbuf + decodedpos, inbuf, sizeof(decodedbuf) - decodedpos);
1756 decodedpos += strlen(decodedbuf + decodedpos);
1766 Mem_Free(thisstr.key);
1767 thisstr.key = (char *)Mem_Alloc(pool, decodedpos + 1);
1768 memcpy(thisstr.key, decodedbuf, decodedpos + 1);
1770 else if(decodedpos > 0 && thisstr.key) // skip empty translation results
1772 thisstr.value = (char *)Mem_Alloc(pool, decodedpos + 1);
1773 memcpy(thisstr.value, decodedbuf, decodedpos + 1);
1774 hashindex = CRC_Block((const unsigned char *) thisstr.key, strlen(thisstr.key)) % PO_HASHSIZE;
1775 thisstr.nextonhashchain = po->hashtable[hashindex];
1776 po->hashtable[hashindex] = (po_string_t *)Mem_Alloc(pool, sizeof(thisstr));
1777 memcpy(po->hashtable[hashindex], &thisstr, sizeof(thisstr));
1778 memset(&thisstr, 0, sizeof(thisstr));
1782 Mem_Free((char *) buf);
1785 static const char *PRVM_PO_Lookup(po_t *po, const char *str)
1787 int hashindex = CRC_Block((const unsigned char *) str, strlen(str)) % PO_HASHSIZE;
1788 po_string_t *p = po->hashtable[hashindex];
1791 if(!strcmp(str, p->key))
1793 p = p->nextonhashchain;
1797 static void PRVM_PO_Destroy(po_t *po)
1800 for(i = 0; i < PO_HASHSIZE; ++i)
1802 po_string_t *p = po->hashtable[i];
1806 p = p->nextonhashchain;
1815 void PRVM_LeakTest(prvm_prog_t *prog);
1816 void PRVM_Prog_Reset(prvm_prog_t *prog)
1820 PRVM_LeakTest(prog);
1821 prog->reset_cmd(prog);
1822 Mem_FreePool(&prog->progs_mempool);
1824 PRVM_PO_Destroy((po_t *) prog->po);
1826 memset(prog,0,sizeof(prvm_prog_t));
1827 prog->break_statement = -1;
1828 prog->watch_global_type = ev_void;
1829 prog->watch_field_type = ev_void;
1837 static void PRVM_LoadLNO( prvm_prog_t *prog, const char *progname ) {
1838 fs_offset_t filesize;
1840 unsigned int *header;
1843 FS_StripExtension( progname, filename, sizeof( filename ) );
1844 strlcat( filename, ".lno", sizeof( filename ) );
1846 lno = FS_LoadFile( filename, tempmempool, false, &filesize );
1852 <Spike> SafeWrite (h, &lnotype, sizeof(int));
1853 <Spike> SafeWrite (h, &version, sizeof(int));
1854 <Spike> SafeWrite (h, &numglobaldefs, sizeof(int));
1855 <Spike> SafeWrite (h, &numpr_globals, sizeof(int));
1856 <Spike> SafeWrite (h, &numfielddefs, sizeof(int));
1857 <Spike> SafeWrite (h, &numstatements, sizeof(int));
1858 <Spike> SafeWrite (h, statement_linenums, numstatements*sizeof(int));
1860 if ((unsigned int)filesize < (6 + prog->progs_numstatements) * sizeof(int))
1866 header = (unsigned int *) lno;
1867 if( header[ 0 ] == *(unsigned int *) "LNOF" &&
1868 LittleLong( header[ 1 ] ) == 1 &&
1869 (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs_numglobaldefs &&
1870 (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs_numglobals &&
1871 (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs_numfielddefs &&
1872 (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs_numstatements )
1874 prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof( int ) );
1875 memcpy( prog->statement_linenums, (int *) lno + 6, prog->progs_numstatements * sizeof( int ) );
1885 static void PRVM_UpdateBreakpoints(prvm_prog_t *prog);
1886 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)
1889 dprograms_t *dprograms;
1890 dstatement_t *instatements;
1891 ddef_t *infielddefs;
1892 ddef_t *inglobaldefs;
1894 dfunction_t *infunctions;
1896 fs_offset_t filesize;
1897 int requiredglobalspace;
1912 prog->error_cmd("PRVM_LoadProgs: there is already a %s program loaded!", prog->name );
1914 Host_LockSession(); // all progs can use the session cvar
1915 Crypto_LoadKeys(); // all progs might use the keys at init time
1919 dprograms = (dprograms_t *) data;
1923 dprograms = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
1924 if (dprograms == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
1925 prog->error_cmd("PRVM_LoadProgs: couldn't load %s for %s", filename, prog->name);
1926 // TODO bounds check header fields (e.g. numstatements), they must never go behind end of file
1928 prog->profiletime = Sys_DirtyTime();
1929 prog->starttime = realtime;
1931 Con_DPrintf("%s programs occupy %iK.\n", prog->name, (int)(filesize/1024));
1933 requiredglobalspace = 0;
1934 for (i = 0;i < numrequiredglobals;i++)
1935 requiredglobalspace += required_global[i].type == ev_vector ? 3 : 1;
1937 prog->filecrc = CRC_Block((unsigned char *)dprograms, filesize);
1939 // byte swap the header
1940 prog->progs_version = LittleLong(dprograms->version);
1941 prog->progs_crc = LittleLong(dprograms->crc);
1942 if (prog->progs_version != PROG_VERSION)
1943 prog->error_cmd("%s: %s has wrong version number (%i should be %i)", prog->name, filename, prog->progs_version, PROG_VERSION);
1944 instatements = (dstatement_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_statements));
1945 prog->progs_numstatements = LittleLong(dprograms->numstatements);
1946 inglobaldefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globaldefs));
1947 prog->progs_numglobaldefs = LittleLong(dprograms->numglobaldefs);
1948 infielddefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_fielddefs));
1949 prog->progs_numfielddefs = LittleLong(dprograms->numfielddefs);
1950 infunctions = (dfunction_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_functions));
1951 prog->progs_numfunctions = LittleLong(dprograms->numfunctions);
1952 instrings = (char *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_strings));
1953 prog->progs_numstrings = LittleLong(dprograms->numstrings);
1954 inglobals = (int *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globals));
1955 prog->progs_numglobals = LittleLong(dprograms->numglobals);
1956 prog->progs_entityfields = LittleLong(dprograms->entityfields);
1958 prog->numstatements = prog->progs_numstatements;
1959 prog->numglobaldefs = prog->progs_numglobaldefs;
1960 prog->numfielddefs = prog->progs_numfielddefs;
1961 prog->numfunctions = prog->progs_numfunctions;
1962 prog->numstrings = prog->progs_numstrings;
1963 prog->numglobals = prog->progs_numglobals;
1964 prog->entityfields = prog->progs_entityfields;
1966 if (LittleLong(dprograms->ofs_strings) + prog->progs_numstrings > (int)filesize)
1967 prog->error_cmd("%s: %s strings go past end of file", prog->name, filename);
1968 prog->strings = (char *)Mem_Alloc(prog->progs_mempool, prog->progs_numstrings);
1969 memcpy(prog->strings, instrings, prog->progs_numstrings);
1970 prog->stringssize = prog->progs_numstrings;
1972 prog->numknownstrings = 0;
1973 prog->maxknownstrings = 0;
1974 prog->knownstrings = NULL;
1975 prog->knownstrings_freeable = NULL;
1977 Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
1979 // we need to expand the globaldefs and fielddefs to include engine defs
1980 prog->globaldefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobaldefs + numrequiredglobals) * sizeof(ddef_t));
1981 prog->globals.fp = (prvm_vec_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobals + requiredglobalspace + 2) * sizeof(prvm_vec_t));
1982 // + 2 is because of an otherwise occurring overrun in RETURN instruction
1983 // when trying to return the last or second-last global
1984 // (RETURN always returns a vector, there is no RETURN_F instruction)
1985 prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numfielddefs + numrequiredfields) * sizeof(ddef_t));
1986 // we need to convert the statements to our memory format
1987 prog->statements = (mstatement_t *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(mstatement_t));
1988 // allocate space for profiling statement usage
1989 prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(*prog->statement_profile));
1990 // functions need to be converted to the memory format
1991 prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs_numfunctions);
1993 for (i = 0;i < prog->progs_numfunctions;i++)
1995 prog->functions[i].first_statement = LittleLong(infunctions[i].first_statement);
1996 prog->functions[i].parm_start = LittleLong(infunctions[i].parm_start);
1997 prog->functions[i].s_name = LittleLong(infunctions[i].s_name);
1998 prog->functions[i].s_file = LittleLong(infunctions[i].s_file);
1999 prog->functions[i].numparms = LittleLong(infunctions[i].numparms);
2000 prog->functions[i].locals = LittleLong(infunctions[i].locals);
2001 memcpy(prog->functions[i].parm_size, infunctions[i].parm_size, sizeof(infunctions[i].parm_size));
2002 if(prog->functions[i].first_statement >= prog->numstatements)
2003 prog->error_cmd("PRVM_LoadProgs: out of bounds function statement (function %d) in %s", i, prog->name);
2004 // TODO bounds check parm_start, s_name, s_file, numparms, locals, parm_size
2007 // copy the globaldefs to the new globaldefs list
2008 for (i=0 ; i<prog->numglobaldefs ; i++)
2010 prog->globaldefs[i].type = LittleShort(inglobaldefs[i].type);
2011 prog->globaldefs[i].ofs = LittleShort(inglobaldefs[i].ofs);
2012 prog->globaldefs[i].s_name = LittleLong(inglobaldefs[i].s_name);
2013 // TODO bounds check ofs, s_name
2016 // append the required globals
2017 for (i = 0;i < numrequiredglobals;i++)
2019 prog->globaldefs[prog->numglobaldefs].type = required_global[i].type;
2020 prog->globaldefs[prog->numglobaldefs].ofs = prog->numglobals;
2021 prog->globaldefs[prog->numglobaldefs].s_name = PRVM_SetEngineString(prog, required_global[i].name);
2022 if (prog->globaldefs[prog->numglobaldefs].type == ev_vector)
2023 prog->numglobals += 3;
2026 prog->numglobaldefs++;
2029 // copy the progs fields to the new fields list
2030 for (i = 0;i < prog->numfielddefs;i++)
2032 prog->fielddefs[i].type = LittleShort(infielddefs[i].type);
2033 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
2034 prog->error_cmd("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", prog->name);
2035 prog->fielddefs[i].ofs = LittleShort(infielddefs[i].ofs);
2036 prog->fielddefs[i].s_name = LittleLong(infielddefs[i].s_name);
2037 // TODO bounds check ofs, s_name
2040 // append the required fields
2041 for (i = 0;i < numrequiredfields;i++)
2043 prog->fielddefs[prog->numfielddefs].type = required_field[i].type;
2044 prog->fielddefs[prog->numfielddefs].ofs = prog->entityfields;
2045 prog->fielddefs[prog->numfielddefs].s_name = PRVM_SetEngineString(prog, required_field[i].name);
2046 if (prog->fielddefs[prog->numfielddefs].type == ev_vector)
2047 prog->entityfields += 3;
2049 prog->entityfields++;
2050 prog->numfielddefs++;
2053 // LordHavoc: TODO: reorder globals to match engine struct
2054 // LordHavoc: TODO: reorder fields to match engine struct
2055 #define remapglobal(index) (index)
2056 #define remapfield(index) (index)
2059 // FIXME: LordHavoc: this uses a crude way to identify integer constants, rather than checking for matching globaldefs and checking their type
2060 for (i = 0;i < prog->progs_numglobals;i++)
2062 u.i = LittleLong(inglobals[i]);
2063 // most globals are 0, we only need to deal with the ones that are not
2066 d = u.i & 0xFF800000;
2067 if ((d == 0xFF800000) || (d == 0))
2069 // Looks like an integer (expand to int64)
2070 prog->globals.ip[remapglobal(i)] = u.i;
2074 // Looks like a float (expand to double)
2075 prog->globals.fp[remapglobal(i)] = u.f;
2080 // LordHavoc: TODO: support 32bit progs statement formats
2081 // copy, remap globals in statements, bounds check
2082 for (i = 0;i < prog->progs_numstatements;i++)
2084 op = (opcode_t)LittleShort(instatements[i].op);
2085 a = (unsigned short)LittleShort(instatements[i].a);
2086 b = (unsigned short)LittleShort(instatements[i].b);
2087 c = (unsigned short)LittleShort(instatements[i].c);
2093 if (a >= prog->progs_numglobals || b + i < 0 || b + i >= prog->progs_numstatements)
2094 prog->error_cmd("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", i, prog->name);
2095 prog->statements[i].op = op;
2096 prog->statements[i].operand[0] = remapglobal(a);
2097 prog->statements[i].operand[1] = -1;
2098 prog->statements[i].operand[2] = -1;
2099 prog->statements[i].jumpabsolute = i + b;
2103 if (a + i < 0 || a + i >= prog->progs_numstatements)
2104 prog->error_cmd("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, prog->name);
2105 prog->statements[i].op = op;
2106 prog->statements[i].operand[0] = -1;
2107 prog->statements[i].operand[1] = -1;
2108 prog->statements[i].operand[2] = -1;
2109 prog->statements[i].jumpabsolute = i + a;
2112 Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", (int)op, i, prog->name);
2113 // global global global
2148 if (a >= prog->progs_numglobals || b >= prog->progs_numglobals || c >= prog->progs_numglobals)
2149 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
2150 prog->statements[i].op = op;
2151 prog->statements[i].operand[0] = remapglobal(a);
2152 prog->statements[i].operand[1] = remapglobal(b);
2153 prog->statements[i].operand[2] = remapglobal(c);
2154 prog->statements[i].jumpabsolute = -1;
2156 // global none global
2162 if (a >= prog->progs_numglobals || c >= prog->progs_numglobals)
2163 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2164 prog->statements[i].op = op;
2165 prog->statements[i].operand[0] = remapglobal(a);
2166 prog->statements[i].operand[1] = -1;
2167 prog->statements[i].operand[2] = remapglobal(c);
2168 prog->statements[i].jumpabsolute = -1;
2184 if (a >= prog->progs_numglobals || b >= prog->progs_numglobals)
2185 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2186 prog->statements[i].op = op;
2187 prog->statements[i].operand[0] = remapglobal(a);
2188 prog->statements[i].operand[1] = remapglobal(b);
2189 prog->statements[i].operand[2] = -1;
2190 prog->statements[i].jumpabsolute = -1;
2204 if ( a >= prog->progs_numglobals)
2205 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2206 prog->statements[i].op = op;
2207 prog->statements[i].operand[0] = remapglobal(a);
2208 prog->statements[i].operand[1] = -1;
2209 prog->statements[i].operand[2] = -1;
2210 prog->statements[i].jumpabsolute = -1;
2214 if(prog->numstatements < 1)
2216 prog->error_cmd("PRVM_LoadProgs: empty program in %s", prog->name);
2218 else switch(prog->statements[prog->numstatements - 1].op)
2225 prog->error_cmd("PRVM_LoadProgs: program may fall off the edge (does not end with RETURN, GOTO or DONE) in %s", prog->name);
2229 // we're done with the file now
2231 Mem_Free(dprograms);
2234 // check required functions
2235 for(i=0 ; i < numrequiredfunc ; i++)
2236 if(PRVM_ED_FindFunction(prog, required_func[i]) == 0)
2237 prog->error_cmd("%s: %s not found in %s",prog->name, required_func[i], filename);
2239 PRVM_LoadLNO(prog, filename);
2241 PRVM_Init_Exec(prog);
2243 if(*prvm_language.string)
2244 // in CSQC we really shouldn't be able to change how stuff works... sorry for now
2245 // later idea: include a list of authorized .po file checksums with the csprogs
2247 qboolean deftrans = prog == CLVM_prog;
2248 const char *realfilename = (prog != CLVM_prog ? filename : csqc_progname.string);
2249 if(deftrans) // once we have dotranslate_ strings, ALWAYS use the opt-in method!
2251 for (i=0 ; i<prog->numglobaldefs ; i++)
2254 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2255 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2256 if(name && !strncmp(name, "dotranslate_", 12))
2263 if(!strcmp(prvm_language.string, "dump"))
2265 qfile_t *f = FS_OpenRealFile(va(vabuf, sizeof(vabuf), "%s.pot", realfilename), "w", false);
2266 Con_Printf("Dumping to %s.pot\n", realfilename);
2269 for (i=0 ; i<prog->numglobaldefs ; i++)
2272 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2273 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2274 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2276 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2277 const char *value = PRVM_GetString(prog, val->string);
2280 char buf[MAX_INPUTLINE];
2281 PRVM_PO_UnparseString(buf, value, sizeof(buf));
2282 FS_Printf(f, "msgid \"%s\"\nmsgstr \"\"\n\n", buf);
2291 po_t *po = PRVM_PO_Load(va(vabuf, sizeof(vabuf), "%s.%s.po", realfilename, prvm_language.string), prog->progs_mempool);
2294 for (i=0 ; i<prog->numglobaldefs ; i++)
2297 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2298 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2299 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2301 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2302 const char *value = PRVM_GetString(prog, val->string);
2305 value = PRVM_PO_Lookup(po, value);
2307 val->string = PRVM_SetEngineString(prog, value);
2315 for (i=0 ; i<prog->numglobaldefs ; i++)
2318 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2319 //Con_Printf("found var %s\n", name);
2321 && !strncmp(name, "autocvar_", 9)
2322 && !(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
2325 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2326 cvar_t *cvar = Cvar_FindVar(name + 9);
2327 //Con_Printf("PRVM_LoadProgs: autocvar global %s in %s, processing...\n", name, prog->name);
2332 Con_DPrintf("PRVM_LoadProgs: no cvar for autocvar global %s in %s, creating...\n", name, prog->name);
2333 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2336 if((float)((int)(val->_float)) == val->_float)
2337 dpsnprintf(buf, sizeof(buf), "%i", (int)(val->_float));
2339 dpsnprintf(buf, sizeof(buf), "%.9g", val->_float);
2343 dpsnprintf(buf, sizeof(buf), "%.9g %.9g %.9g", val->vector[0], val->vector[1], val->vector[2]); value = buf;
2346 value = PRVM_GetString(prog, val->string);
2349 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2352 cvar = Cvar_Get(name + 9, value, 0, NULL);
2353 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2355 val->string = PRVM_SetEngineString(prog, cvar->string);
2356 cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2359 prog->error_cmd("PRVM_LoadProgs: could not create cvar for autocvar global %s in %s", name, prog->name);
2360 cvar->globaldefindex_progid[prog - prvm_prog_list] = prog->id;
2361 cvar->globaldefindex[prog - prvm_prog_list] = i;
2363 else if((cvar->flags & CVAR_PRIVATE) == 0)
2365 // MUST BE SYNCED WITH cvar.c Cvar_Set
2368 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2371 val->_float = cvar->value;
2375 VectorClear(val->vector);
2376 for (j = 0;j < 3;j++)
2378 while (*s && ISWHITESPACE(*s))
2382 val->vector[j] = atof(s);
2383 while (!ISWHITESPACE(*s))
2390 val->string = PRVM_SetEngineString(prog, cvar->string);
2391 cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2394 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2397 cvar->globaldefindex_progid[prog - prvm_prog_list] = prog->id;
2398 cvar->globaldefindex[prog - prvm_prog_list] = i;
2401 Con_Printf("PRVM_LoadProgs: private cvar for autocvar global %s in %s\n", name, prog->name);
2407 prog->loaded = TRUE;
2409 PRVM_UpdateBreakpoints(prog);
2411 // set flags & ddef_ts in prog
2415 PRVM_FindOffsets(prog);
2417 prog->init_cmd(prog);
2420 PRVM_MEM_Alloc(prog);
2424 static void PRVM_Fields_f (void)
2427 int i, j, ednum, used, usedamount;
2429 char tempstring[MAX_INPUTLINE], tempstring2[260];
2439 Con_Print("no progs loaded\n");
2446 Con_Print("prvm_fields <program name>\n");
2450 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2453 counts = (int *)Mem_Alloc(tempmempool, prog->numfielddefs * sizeof(int));
2454 for (ednum = 0;ednum < prog->max_edicts;ednum++)
2456 ed = PRVM_EDICT_NUM(ednum);
2457 if (ed->priv.required->free)
2459 for (i = 1;i < prog->numfielddefs;i++)
2461 d = &prog->fielddefs[i];
2462 name = PRVM_GetString(prog, d->s_name);
2463 if (name[strlen(name)-2] == '_')
2464 continue; // skip _x, _y, _z vars
2465 val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
2466 // if the value is still all 0, skip the field
2467 for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
2469 if (val->ivector[j])
2480 for (i = 0;i < prog->numfielddefs;i++)
2482 d = &prog->fielddefs[i];
2483 name = PRVM_GetString(prog, d->s_name);
2484 if (name[strlen(name)-2] == '_')
2485 continue; // skip _x, _y, _z vars
2486 switch(d->type & ~DEF_SAVEGLOBAL)
2489 strlcat(tempstring, "string ", sizeof(tempstring));
2492 strlcat(tempstring, "entity ", sizeof(tempstring));
2495 strlcat(tempstring, "function ", sizeof(tempstring));
2498 strlcat(tempstring, "field ", sizeof(tempstring));
2501 strlcat(tempstring, "void ", sizeof(tempstring));
2504 strlcat(tempstring, "float ", sizeof(tempstring));
2507 strlcat(tempstring, "vector ", sizeof(tempstring));
2510 strlcat(tempstring, "pointer ", sizeof(tempstring));
2513 dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
2514 strlcat(tempstring, tempstring2, sizeof(tempstring));
2517 if (strlen(name) > sizeof(tempstring2)-4)
2519 memcpy (tempstring2, name, sizeof(tempstring2)-4);
2520 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
2521 tempstring2[sizeof(tempstring2)-1] = 0;
2524 strlcat(tempstring, name, sizeof(tempstring));
2525 for (j = (int)strlen(name);j < 25;j++)
2526 strlcat(tempstring, " ", sizeof(tempstring));
2527 dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
2528 strlcat(tempstring, tempstring2, sizeof(tempstring));
2529 strlcat(tempstring, "\n", sizeof(tempstring));
2530 if (strlen(tempstring) >= sizeof(tempstring)/2)
2532 Con_Print(tempstring);
2538 usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
2542 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);
2545 static void PRVM_Globals_f (void)
2549 const char *wildcard;
2555 Con_Print("no progs loaded\n");
2558 if(Cmd_Argc () < 2 || Cmd_Argc() > 3)
2560 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
2564 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2567 if( Cmd_Argc() == 3)
2568 wildcard = Cmd_Argv(2);
2572 Con_Printf("%s :", prog->name);
2574 for (i = 0;i < prog->numglobaldefs;i++)
2577 if( !matchpattern( PRVM_GetString(prog, prog->globaldefs[i].s_name), wildcard, 1) )
2582 Con_Printf("%s\n", PRVM_GetString(prog, prog->globaldefs[i].s_name));
2584 Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->numglobals, numculled, prog->numglobals * 4);
2592 static void PRVM_Global_f(void)
2596 char valuebuf[MAX_INPUTLINE];
2597 if( Cmd_Argc() != 3 ) {
2598 Con_Printf( "prvm_global <program name> <global name>\n" );
2602 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2605 global = PRVM_ED_FindGlobal( prog, Cmd_Argv(2) );
2607 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2609 Con_Printf( "%s: %s\n", Cmd_Argv(2), PRVM_ValueString( prog, (etype_t)global->type, PRVM_GLOBALFIELDVALUE(global->ofs), valuebuf, sizeof(valuebuf) ) );
2617 static void PRVM_GlobalSet_f(void)
2621 if( Cmd_Argc() != 4 ) {
2622 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2626 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2629 global = PRVM_ED_FindGlobal( prog, Cmd_Argv(2) );
2631 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2633 PRVM_ED_ParseEpair( prog, NULL, global, Cmd_Argv(3), true );
2637 ======================
2638 Break- and Watchpoints
2639 ======================
2643 char break_statement[256];
2644 char watch_global[256];
2646 char watch_field[256];
2649 static debug_data_t debug_data[PRVM_PROG_MAX];
2651 void PRVM_Breakpoint(prvm_prog_t *prog, int stack_index, const char *text)
2654 Con_Printf("PRVM_Breakpoint: %s\n", text);
2655 PRVM_PrintState(prog, stack_index);
2656 if (prvm_breakpointdump.integer)
2657 Host_Savegame_to(prog, va(vabuf, sizeof(vabuf), "breakpoint-%s.dmp", prog->name));
2660 void PRVM_Watchpoint(prvm_prog_t *prog, int stack_index, const char *text, etype_t type, prvm_eval_t *o, prvm_eval_t *n)
2662 size_t sz = sizeof(prvm_vec_t) * ((type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2663 if (memcmp(o, n, sz))
2666 char valuebuf_o[128];
2667 char valuebuf_n[128];
2668 PRVM_UglyValueString(prog, type, o, valuebuf_o, sizeof(valuebuf_o));
2669 PRVM_UglyValueString(prog, type, n, valuebuf_n, sizeof(valuebuf_n));
2670 dpsnprintf(buf, sizeof(buf), "%s: %s -> %s", text, valuebuf_o, valuebuf_n);
2671 PRVM_Breakpoint(prog, stack_index, buf);
2676 static void PRVM_UpdateBreakpoints(prvm_prog_t *prog)
2678 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2681 if (debug->break_statement[0])
2683 if (debug->break_statement[0] >= '0' && debug->break_statement[0] <= '9')
2685 prog->break_statement = atoi(debug->break_statement);
2686 prog->break_stack_index = 0;
2691 func = PRVM_ED_FindFunction (prog, debug->break_statement);
2694 Con_Printf("%s progs: no function or statement named %s to break on!\n", prog->name, debug->break_statement);
2695 prog->break_statement = -1;
2699 prog->break_statement = func->first_statement;
2700 prog->break_stack_index = 1;
2703 if (prog->break_statement >= -1)
2704 Con_Printf("%s progs: breakpoint is at statement %d\n", prog->name, prog->break_statement);
2707 prog->break_statement = -1;
2709 if (debug->watch_global[0])
2711 ddef_t *global = PRVM_ED_FindGlobal( prog, debug->watch_global );
2714 Con_Printf( "%s progs: no global named '%s' to watch!\n", prog->name, debug->watch_global );
2715 prog->watch_global_type = ev_void;
2719 size_t sz = sizeof(prvm_vec_t) * ((global->type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2720 prog->watch_global = global->ofs;
2721 prog->watch_global_type = (etype_t)global->type;
2722 memcpy(&prog->watch_global_value, PRVM_GLOBALFIELDVALUE(prog->watch_global), sz);
2724 if (prog->watch_global_type != ev_void)
2725 Con_Printf("%s progs: global watchpoint is at global index %d\n", prog->name, prog->watch_global);
2728 prog->watch_global_type = ev_void;
2730 if (debug->watch_field[0])
2732 ddef_t *field = PRVM_ED_FindField( prog, debug->watch_field );
2735 Con_Printf( "%s progs: no field named '%s' to watch!\n", prog->name, debug->watch_field );
2736 prog->watch_field_type = ev_void;
2740 size_t sz = sizeof(prvm_vec_t) * ((field->type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2741 prog->watch_edict = debug->watch_edict;
2742 prog->watch_field = field->ofs;
2743 prog->watch_field_type = (etype_t)field->type;
2744 if (prog->watch_edict < prog->num_edicts)
2745 memcpy(&prog->watch_edictfield_value, PRVM_EDICTFIELDVALUE(PRVM_EDICT_NUM(prog->watch_edict), prog->watch_field), sz);
2747 memset(&prog->watch_edictfield_value, 0, sz);
2749 if (prog->watch_edict != ev_void)
2750 Con_Printf("%s progs: edict field watchpoint is at edict %d field index %d\n", prog->name, prog->watch_edict, prog->watch_field);
2753 prog->watch_field_type = ev_void;
2756 static void PRVM_Breakpoint_f(void)
2760 if( Cmd_Argc() == 2 ) {
2761 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2764 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2765 debug->break_statement[0] = 0;
2767 PRVM_UpdateBreakpoints(prog);
2770 if( Cmd_Argc() != 3 ) {
2771 Con_Printf( "prvm_breakpoint <program name> <function name | statement>\n" );
2775 if (!(prog = PRVM_ProgFromString(Cmd_Argv(1))))
2779 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2780 strlcpy(debug->break_statement, Cmd_Argv(2), sizeof(debug->break_statement));
2782 PRVM_UpdateBreakpoints(prog);
2785 static void PRVM_GlobalWatchpoint_f(void)
2789 if( Cmd_Argc() == 2 ) {
2790 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2793 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2794 debug->watch_global[0] = 0;
2796 PRVM_UpdateBreakpoints(prog);
2799 if( Cmd_Argc() != 3 ) {
2800 Con_Printf( "prvm_globalwatchpoint <program name> <global name>\n" );
2804 if (!(prog = PRVM_ProgFromString(Cmd_Argv(1))))
2808 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2809 strlcpy(debug->watch_global, Cmd_Argv(2), sizeof(debug->watch_global));
2811 PRVM_UpdateBreakpoints(prog);
2814 static void PRVM_EdictWatchpoint_f(void)
2818 if( Cmd_Argc() == 2 ) {
2819 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2822 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2823 debug->watch_field[0] = 0;
2825 PRVM_UpdateBreakpoints(prog);
2828 if( Cmd_Argc() != 4 ) {
2829 Con_Printf( "prvm_edictwatchpoint <program name> <edict number> <field name>\n" );
2833 if (!(prog = PRVM_ProgFromString(Cmd_Argv(1))))
2837 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2838 debug->watch_edict = atoi(Cmd_Argv(2));
2839 strlcpy(debug->watch_field, Cmd_Argv(3), sizeof(debug->watch_field));
2841 PRVM_UpdateBreakpoints(prog);
2849 void PRVM_Init (void)
2851 Cmd_AddCommand ("prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2852 Cmd_AddCommand ("prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2853 Cmd_AddCommand ("prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2854 Cmd_AddCommand ("prvm_profile", PRVM_Profile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu)");
2855 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");
2856 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)");
2857 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)");
2858 Cmd_AddCommand ("prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2859 Cmd_AddCommand ("prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2860 Cmd_AddCommand ("prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2861 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)");
2862 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");
2863 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");
2864 Cmd_AddCommand ("prvm_printfunction", PRVM_PrintFunction_f, "prints a disassembly (QuakeC instructions) of the specified function in the selected VM (server, client, menu)");
2865 Cmd_AddCommand ("cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2866 Cmd_AddCommand ("menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2867 Cmd_AddCommand ("sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2869 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");
2870 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");
2871 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");
2873 Cvar_RegisterVariable (&prvm_language);
2874 Cvar_RegisterVariable (&prvm_traceqc);
2875 Cvar_RegisterVariable (&prvm_statementprofiling);
2876 Cvar_RegisterVariable (&prvm_timeprofiling);
2877 Cvar_RegisterVariable (&prvm_backtraceforwarnings);
2878 Cvar_RegisterVariable (&prvm_leaktest);
2879 Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames);
2880 Cvar_RegisterVariable (&prvm_errordump);
2881 Cvar_RegisterVariable (&prvm_breakpointdump);
2882 Cvar_RegisterVariable (&prvm_reuseedicts_startuptime);
2883 Cvar_RegisterVariable (&prvm_reuseedicts_neverinsameframe);
2885 // COMMANDLINEOPTION: PRVM: -norunaway disables the runaway loop check (it might be impossible to exit DarkPlaces if used!)
2886 prvm_runawaycheck = !COM_CheckParm("-norunaway");
2896 void PRVM_Prog_Init(prvm_prog_t *prog)
2898 PRVM_Prog_Reset(prog);
2899 prog->leaktest_active = prvm_leaktest.integer != 0;
2902 // LordHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
2903 unsigned int PRVM_EDICT_NUM_ERROR(prvm_prog_t *prog, unsigned int n, const char *filename, int fileline)
2905 prog->error_cmd("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", prog->name, n, filename, fileline);
2909 #define PRVM_KNOWNSTRINGBASE 0x40000000
2911 const char *PRVM_GetString(prvm_prog_t *prog, int num)
2916 VM_Warning(prog, "PRVM_GetString: Invalid string offset (%i < 0)\n", num);
2919 else if (num < prog->stringssize)
2921 // constant string from progs.dat
2922 return prog->strings + num;
2924 else if (num <= prog->stringssize + prog->tempstringsbuf.maxsize)
2926 // tempstring returned by engine to QC (becomes invalid after returning to engine)
2927 num -= prog->stringssize;
2928 if (num < prog->tempstringsbuf.cursize)
2929 return (char *)prog->tempstringsbuf.data + num;
2932 VM_Warning(prog, "PRVM_GetString: Invalid temp-string offset (%i >= %i prog->tempstringsbuf.cursize)\n", num, prog->tempstringsbuf.cursize);
2936 else if (num & PRVM_KNOWNSTRINGBASE)
2939 num = num - PRVM_KNOWNSTRINGBASE;
2940 if (num >= 0 && num < prog->numknownstrings)
2942 if (!prog->knownstrings[num])
2944 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
2947 return prog->knownstrings[num];
2951 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
2957 // invalid string offset
2958 VM_Warning(prog, "PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
2963 const char *PRVM_ChangeEngineString(prvm_prog_t *prog, int i, const char *s)
2966 i = i - PRVM_KNOWNSTRINGBASE;
2967 if(i < 0 || i >= prog->numknownstrings)
2968 prog->error_cmd("PRVM_ChangeEngineString: s is not an engine string");
2969 old = prog->knownstrings[i];
2970 prog->knownstrings[i] = s;
2974 int PRVM_SetEngineString(prvm_prog_t *prog, const char *s)
2979 if (s >= prog->strings && s <= prog->strings + prog->stringssize)
2980 prog->error_cmd("PRVM_SetEngineString: s in prog->strings area");
2981 // if it's in the tempstrings area, use a reserved range
2982 // (otherwise we'd get millions of useless string offsets cluttering the database)
2983 if (s >= (char *)prog->tempstringsbuf.data && s < (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.maxsize)
2984 return prog->stringssize + (s - (char *)prog->tempstringsbuf.data);
2985 // see if it's a known string address
2986 for (i = 0;i < prog->numknownstrings;i++)
2987 if (prog->knownstrings[i] == s)
2988 return PRVM_KNOWNSTRINGBASE + i;
2989 // new unknown engine string
2990 if (developer_insane.integer)
2991 Con_DPrintf("new engine string %p = \"%s\"\n", s, s);
2992 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2993 if (!prog->knownstrings[i])
2995 if (i >= prog->numknownstrings)
2997 if (i >= prog->maxknownstrings)
2999 const char **oldstrings = prog->knownstrings;
3000 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
3001 const char **oldstrings_origin = prog->knownstrings_origin;
3002 prog->maxknownstrings += 128;
3003 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3004 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
3005 if(prog->leaktest_active)
3006 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3007 if (prog->numknownstrings)
3009 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
3010 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
3011 if(prog->leaktest_active)
3012 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
3015 prog->numknownstrings++;
3017 prog->firstfreeknownstring = i + 1;
3018 prog->knownstrings[i] = s;
3019 prog->knownstrings_freeable[i] = false;
3020 if(prog->leaktest_active)
3021 prog->knownstrings_origin[i] = NULL;
3022 return PRVM_KNOWNSTRINGBASE + i;
3025 // temp string handling
3027 // all tempstrings go into this buffer consecutively, and it is reset
3028 // whenever PRVM_ExecuteProgram returns to the engine
3029 // (technically each PRVM_ExecuteProgram call saves the cursize value and
3030 // restores it on return, so multiple recursive calls can share the same
3032 // the buffer size is automatically grown as needed
3034 int PRVM_SetTempString(prvm_prog_t *prog, const char *s)
3040 size = (int)strlen(s) + 1;
3041 if (developer_insane.integer)
3042 Con_DPrintf("PRVM_SetTempString: cursize %i, size %i\n", prog->tempstringsbuf.cursize, size);
3043 if (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
3045 sizebuf_t old = prog->tempstringsbuf;
3046 if (prog->tempstringsbuf.cursize + size >= 1<<28)
3047 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);
3048 prog->tempstringsbuf.maxsize = max(prog->tempstringsbuf.maxsize, 65536);
3049 while (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
3050 prog->tempstringsbuf.maxsize *= 2;
3051 if (prog->tempstringsbuf.maxsize != old.maxsize || prog->tempstringsbuf.data == NULL)
3053 Con_DPrintf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, prog->tempstringsbuf.maxsize/1024);
3054 prog->tempstringsbuf.data = (unsigned char *) Mem_Alloc(prog->progs_mempool, prog->tempstringsbuf.maxsize);
3056 memcpy(prog->tempstringsbuf.data, old.data, old.cursize);
3061 t = (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.cursize;
3063 prog->tempstringsbuf.cursize += size;
3064 return PRVM_SetEngineString(prog, t);
3067 int PRVM_AllocString(prvm_prog_t *prog, size_t bufferlength, char **pointer)
3072 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3073 if (!prog->knownstrings[i])
3075 if (i >= prog->numknownstrings)
3077 if (i >= prog->maxknownstrings)
3079 const char **oldstrings = prog->knownstrings;
3080 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
3081 const char **oldstrings_origin = prog->knownstrings_origin;
3082 prog->maxknownstrings += 128;
3083 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3084 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
3085 if(prog->leaktest_active)
3086 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3087 if (prog->numknownstrings)
3089 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
3090 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
3091 if(prog->leaktest_active)
3092 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
3095 Mem_Free((char **)oldstrings);
3096 if (oldstrings_freeable)
3097 Mem_Free((unsigned char *)oldstrings_freeable);
3098 if (oldstrings_origin)
3099 Mem_Free((char **)oldstrings_origin);
3101 prog->numknownstrings++;
3103 prog->firstfreeknownstring = i + 1;
3104 prog->knownstrings[i] = (char *)PRVM_Alloc(bufferlength);
3105 prog->knownstrings_freeable[i] = true;
3106 if(prog->leaktest_active)
3107 prog->knownstrings_origin[i] = PRVM_AllocationOrigin(prog);
3109 *pointer = (char *)(prog->knownstrings[i]);
3110 return PRVM_KNOWNSTRINGBASE + i;
3113 void PRVM_FreeString(prvm_prog_t *prog, int num)
3116 prog->error_cmd("PRVM_FreeString: attempt to free a NULL string");
3117 else if (num >= 0 && num < prog->stringssize)
3118 prog->error_cmd("PRVM_FreeString: attempt to free a constant string");
3119 else if (num >= PRVM_KNOWNSTRINGBASE && num < PRVM_KNOWNSTRINGBASE + prog->numknownstrings)
3121 num = num - PRVM_KNOWNSTRINGBASE;
3122 if (!prog->knownstrings[num])
3123 prog->error_cmd("PRVM_FreeString: attempt to free a non-existent or already freed string");
3124 if (!prog->knownstrings_freeable[num])
3125 prog->error_cmd("PRVM_FreeString: attempt to free a string owned by the engine");
3126 PRVM_Free((char *)prog->knownstrings[num]);
3127 if(prog->leaktest_active)
3128 if(prog->knownstrings_origin[num])
3129 PRVM_Free((char *)prog->knownstrings_origin[num]);
3130 prog->knownstrings[num] = NULL;
3131 prog->knownstrings_freeable[num] = false;
3132 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
3135 prog->error_cmd("PRVM_FreeString: invalid string offset %i", num);
3138 static qboolean PRVM_IsStringReferenced(prvm_prog_t *prog, string_t string)
3142 for (i = 0;i < prog->numglobaldefs;i++)
3144 ddef_t *d = &prog->globaldefs[i];
3145 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3147 if(string == PRVM_GLOBALFIELDSTRING(d->ofs))
3151 for(j = 0; j < prog->num_edicts; ++j)
3153 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3154 if (ed->priv.required->free)
3156 for (i=0; i<prog->numfielddefs; ++i)
3158 ddef_t *d = &prog->fielddefs[i];
3159 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3161 if(string == PRVM_EDICTFIELDSTRING(ed, d->ofs))
3169 static qboolean PRVM_IsEdictRelevant(prvm_prog_t *prog, prvm_edict_t *edict)
3173 if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
3174 return true; // world or clients
3175 if (prog == SVVM_prog)
3177 if(PRVM_serveredictfloat(edict, solid)) // can block other stuff, or is a trigger?
3179 if(PRVM_serveredictfloat(edict, modelindex)) // visible ent?
3181 if(PRVM_serveredictfloat(edict, effects)) // particle effect?
3183 if(PRVM_serveredictfunction(edict, think)) // has a think function?
3184 if(PRVM_serveredictfloat(edict, nextthink) > 0) // that actually will eventually run?
3186 if(PRVM_serveredictfloat(edict, takedamage))
3188 if(*prvm_leaktest_ignore_classnames.string)
3190 if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_serveredictstring(edict, classname)))))
3194 else if (prog == CLVM_prog)
3196 // TODO someone add more stuff here
3197 if(PRVM_clientedictfloat(edict, entnum)) // csqc networked
3199 if(PRVM_clientedictfloat(edict, modelindex)) // visible ent?
3201 if(PRVM_clientedictfloat(edict, effects)) // particle effect?
3203 if(PRVM_clientedictfunction(edict, think)) // has a think function?
3204 if(PRVM_clientedictfloat(edict, nextthink) > 0) // that actually will eventually run?
3206 if(*prvm_leaktest_ignore_classnames.string)
3208 if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_clientedictstring(edict, classname)))))
3214 // menu prog does not have classnames
3219 static qboolean PRVM_IsEdictReferenced(prvm_prog_t *prog, prvm_edict_t *edict, int mark)
3222 int edictnum = PRVM_NUM_FOR_EDICT(edict);
3223 const char *targetname = NULL;
3225 if (prog == SVVM_prog)
3226 targetname = PRVM_GetString(prog, PRVM_serveredictstring(edict, targetname));
3229 if(!*targetname) // ""
3234 for (i = 0;i < prog->numglobaldefs;i++)
3236 ddef_t *d = &prog->globaldefs[i];
3237 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3239 if(edictnum == PRVM_GLOBALFIELDEDICT(d->ofs))
3244 for(j = 0; j < prog->num_edicts; ++j)
3246 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3247 if (ed->priv.required->mark < mark)
3253 const char *target = PRVM_GetString(prog, PRVM_serveredictstring(ed, target));
3255 if(!strcmp(target, targetname))
3258 for (i=0; i<prog->numfielddefs; ++i)
3260 ddef_t *d = &prog->fielddefs[i];
3261 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3263 if(edictnum == PRVM_EDICTFIELDEDICT(ed, d->ofs))
3271 static void PRVM_MarkReferencedEdicts(prvm_prog_t *prog)
3277 for(j = 0; j < prog->num_edicts; ++j)
3279 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3280 if(ed->priv.required->free)
3282 ed->priv.required->mark = PRVM_IsEdictRelevant(prog, ed) ? 1 : 0;
3289 for(j = 0; j < prog->num_edicts; ++j)
3291 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3292 if(ed->priv.required->free)
3294 if(ed->priv.required->mark)
3296 if(PRVM_IsEdictReferenced(prog, ed, stage))
3298 ed->priv.required->mark = stage + 1;
3305 Con_DPrintf("leak check used %d stages to find all references\n", stage);
3308 void PRVM_LeakTest(prvm_prog_t *prog)
3311 qboolean leaked = false;
3313 if(!prog->leaktest_active)
3317 for (i = 0; i < prog->numknownstrings; ++i)
3319 if(prog->knownstrings[i])
3320 if(prog->knownstrings_freeable[i])
3321 if(prog->knownstrings_origin[i])
3322 if(!PRVM_IsStringReferenced(prog, PRVM_KNOWNSTRINGBASE + i))
3324 Con_Printf("Unreferenced string found!\n Value: %s\n Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
3330 PRVM_MarkReferencedEdicts(prog);
3331 for(j = 0; j < prog->num_edicts; ++j)
3333 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3334 if(ed->priv.required->free)
3336 if(!ed->priv.required->mark)
3337 if(ed->priv.required->allocation_origin)
3339 Con_Printf("Unreferenced edict found!\n Allocated at: %s\n", ed->priv.required->allocation_origin);
3340 PRVM_ED_Print(prog, ed, NULL);
3345 ed->priv.required->mark = 0; // clear marks again when done
3348 for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
3350 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
3352 if(stringbuffer->origin)
3354 Con_Printf("Open string buffer handle found!\n Allocated at: %s\n", stringbuffer->origin);
3359 for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
3361 if(prog->openfiles[i])
3362 if(prog->openfiles_origin[i])
3364 Con_Printf("Open file handle found!\n Allocated at: %s\n", prog->openfiles_origin[i]);
3369 for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
3371 if(prog->opensearches[i])
3372 if(prog->opensearches_origin[i])
3374 Con_Printf("Open search handle found!\n Allocated at: %s\n", prog->opensearches_origin[i]);
3380 Con_Printf("Congratulations. No leaks found.\n");