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_CLIENT | CVAR_SERVER | 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 // LadyHavoc: prints every opcode as it executes - warning: this is significant spew
34 cvar_t prvm_traceqc = {CVAR_CLIENT | CVAR_SERVER, "prvm_traceqc", "0", "prints every QuakeC statement as it is executed (only for really thorough debugging!)"};
35 // LadyHavoc: counts usage of each QuakeC statement
36 cvar_t prvm_statementprofiling = {CVAR_CLIENT | CVAR_SERVER, "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 = {CVAR_CLIENT | CVAR_SERVER, "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 = {CVAR_CLIENT | CVAR_SERVER, "prvm_coverage", "0", "report and count coverage events (1: per-function, 2: coverage() builtin, 4: per-statement)"};
39 cvar_t prvm_backtraceforwarnings = {CVAR_CLIENT | CVAR_SERVER, "prvm_backtraceforwarnings", "0", "print a backtrace for warnings too"};
40 cvar_t prvm_leaktest = {CVAR_CLIENT | CVAR_SERVER, "prvm_leaktest", "0", "try to detect memory leaks in strings or entities"};
41 cvar_t prvm_leaktest_follow_targetname = {CVAR_CLIENT | CVAR_SERVER, "prvm_leaktest_follow_targetname", "0", "if set, target/targetname links are considered when leak testing; this should normally not be required, as entities created during startup - e.g. info_notnull - are never considered leaky"};
42 cvar_t prvm_leaktest_ignore_classnames = {CVAR_CLIENT | CVAR_SERVER, "prvm_leaktest_ignore_classnames", "", "classnames of entities to NOT leak check because they are found by find(world, classname, ...) but are actually spawned by QC code (NOT map entities)"};
43 cvar_t prvm_errordump = {CVAR_CLIENT | CVAR_SERVER, "prvm_errordump", "0", "write a savegame on crash to crash-server.dmp"};
44 cvar_t prvm_breakpointdump = {CVAR_CLIENT | CVAR_SERVER, "prvm_breakpointdump", "0", "write a savegame on breakpoint to breakpoint-server.dmp"};
45 cvar_t prvm_reuseedicts_startuptime = {CVAR_CLIENT | CVAR_SERVER, "prvm_reuseedicts_startuptime", "2", "allows immediate re-use of freed entity slots during start of new level (value in seconds)"};
46 cvar_t prvm_reuseedicts_neverinsameframe = {CVAR_CLIENT | CVAR_SERVER, "prvm_reuseedicts_neverinsameframe", "1", "never allows re-use of freed entity slots during same frame"};
47 cvar_t prvm_garbagecollection_enable = {CVAR_CLIENT | CVAR_SERVER, "prvm_garbagecollection_enable", "1", "automatically scan for and free resources that are not referenced by the code being executed in the VM"};
48 cvar_t prvm_garbagecollection_notify = {CVAR_CLIENT | CVAR_SERVER, "prvm_garbagecollection_notify", "0", "print out a notification for each resource freed by garbage collection"};
49 cvar_t prvm_garbagecollection_scan_limit = {CVAR_CLIENT | CVAR_SERVER, "prvm_garbagecollection_scan_limit", "10000", "scan this many fields or resources per frame to free up unreferenced resources"};
50 cvar_t prvm_garbagecollection_strings = {CVAR_CLIENT | CVAR_SERVER, "prvm_garbagecollection_strings", "1", "automatically call strunzone() on strings that are not referenced"};
51 cvar_t prvm_stringdebug = {CVAR_CLIENT | CVAR_SERVER, "prvm_stringdebug", "0", "Print debug and warning messages related to strings"};
53 static double prvm_reuseedicts_always_allow = 0;
54 qboolean prvm_runawaycheck = true;
56 //============================================================================
64 static void PRVM_MEM_Alloc(prvm_prog_t *prog)
68 // reserve space for the null entity aka world
69 // check bound of max_edicts
70 prog->max_edicts = bound(1 + prog->reserved_edicts, prog->max_edicts, prog->limit_edicts);
71 prog->num_edicts = bound(1 + prog->reserved_edicts, prog->num_edicts, prog->max_edicts);
73 // edictprivate_size has to be min as big prvm_edict_private_t
74 prog->edictprivate_size = max(prog->edictprivate_size,(int)sizeof(prvm_edict_private_t));
77 prog->edicts = (prvm_edict_t *)Mem_Alloc(prog->progs_mempool,prog->limit_edicts * sizeof(prvm_edict_t));
79 // alloc edict private space
80 prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size);
83 prog->entityfieldsarea = prog->entityfields * prog->max_edicts;
84 prog->edictsfields.fp = (prvm_vec_t *)Mem_Alloc(prog->progs_mempool, prog->entityfieldsarea * sizeof(prvm_vec_t));
87 for(i = 0; i < prog->max_edicts; i++)
89 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
90 prog->edicts[i].fields.fp = prog->edictsfields.fp + i * prog->entityfields;
96 PRVM_MEM_IncreaseEdicts
99 void PRVM_MEM_IncreaseEdicts(prvm_prog_t *prog)
103 if(prog->max_edicts >= prog->limit_edicts)
106 prog->begin_increase_edicts(prog);
109 prog->max_edicts = min(prog->max_edicts + 256, prog->limit_edicts);
111 prog->entityfieldsarea = prog->entityfields * prog->max_edicts;
112 prog->edictsfields.fp = (prvm_vec_t*)Mem_Realloc(prog->progs_mempool, (void *)prog->edictsfields.fp, prog->entityfieldsarea * sizeof(prvm_vec_t));
113 prog->edictprivate = (void *)Mem_Realloc(prog->progs_mempool, (void *)prog->edictprivate, prog->max_edicts * prog->edictprivate_size);
115 //set e and v pointers
116 for(i = 0; i < prog->max_edicts; i++)
118 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
119 prog->edicts[i].fields.fp = prog->edictsfields.fp + i * prog->entityfields;
122 prog->end_increase_edicts(prog);
125 //============================================================================
128 int PRVM_ED_FindFieldOffset(prvm_prog_t *prog, const char *field)
131 d = PRVM_ED_FindField(prog, field);
137 int PRVM_ED_FindGlobalOffset(prvm_prog_t *prog, const char *global)
140 d = PRVM_ED_FindGlobal(prog, global);
146 func_t PRVM_ED_FindFunctionOffset(prvm_prog_t *prog, const char *function)
149 f = PRVM_ED_FindFunction(prog, function);
152 return (func_t)(f - prog->functions);
160 prvm_prog_t *PRVM_ProgFromString(const char *str)
162 if (!strcmp(str, "server"))
164 if (!strcmp(str, "client"))
167 if (!strcmp(str, "menu"))
175 PRVM_FriendlyProgFromString
178 prvm_prog_t *PRVM_FriendlyProgFromString(const char *str)
180 prvm_prog_t *prog = PRVM_ProgFromString(str);
183 Con_Printf("%s: unknown program name\n", str);
188 Con_Printf("%s: program is not loaded\n", str);
198 Sets everything to NULL.
200 Nota bene: this also marks the entity as allocated if it has been previously
201 freed and sets the allocation origin.
204 void PRVM_ED_ClearEdict(prvm_prog_t *prog, prvm_edict_t *e)
206 memset(e->fields.fp, 0, prog->entityfields * sizeof(prvm_vec_t));
207 e->priv.required->free = false;
208 e->priv.required->freetime = host.realtime;
209 if(e->priv.required->allocation_origin)
210 Mem_Free((char *)e->priv.required->allocation_origin);
211 e->priv.required->allocation_origin = PRVM_AllocationOrigin(prog);
213 // AK: Let the init_edict function determine if something needs to be initialized
214 prog->init_edict(prog, e);
217 const char *PRVM_AllocationOrigin(prvm_prog_t *prog)
220 if(prog->leaktest_active)
221 if(prog->depth > 0) // actually in QC code and not just parsing the entities block of a map/savegame
223 buf = (char *)PRVM_Alloc(256);
224 PRVM_ShortStackTrace(prog, buf, 256);
233 Returns if this particular edict could get allocated by PRVM_ED_Alloc
236 qboolean PRVM_ED_CanAlloc(prvm_prog_t *prog, prvm_edict_t *e)
238 if(!e->priv.required->free)
240 if(prvm_reuseedicts_always_allow == host.realtime)
242 if(host.realtime <= e->priv.required->freetime + 0.1 && prvm_reuseedicts_neverinsameframe.integer)
243 return false; // never allow reuse in same frame (causes networking trouble)
244 if(e->priv.required->freetime < prog->starttime + prvm_reuseedicts_startuptime.value)
246 if(host.realtime > e->priv.required->freetime + 1)
248 return false; // entity slot still blocked because the entity was freed less than one second ago
255 Either finds a free edict, or allocates a new one.
256 Try to avoid reusing an entity that was recently freed, because it
257 can cause the client to think the entity morphed into something else
258 instead of being removed and recreated, which can cause interpolated
259 angles and bad trails.
262 prvm_edict_t *PRVM_ED_Alloc(prvm_prog_t *prog)
267 // the client qc dont need maxclients
268 // thus it doesnt need to use svs.maxclients
269 // AK: changed i=svs.maxclients+1
270 // AK: changed so the edict 0 wont spawn -> used as reserved/world entity
271 // although the menu/client has no world
272 for (i = prog->reserved_edicts + 1;i < prog->num_edicts;i++)
274 e = PRVM_EDICT_NUM(i);
275 if(PRVM_ED_CanAlloc(prog, e))
277 PRVM_ED_ClearEdict (prog, e);
282 if (i == prog->limit_edicts)
283 prog->error_cmd("%s: PRVM_ED_Alloc: no free edicts", prog->name);
286 if (prog->num_edicts >= prog->max_edicts)
287 PRVM_MEM_IncreaseEdicts(prog);
289 e = PRVM_EDICT_NUM(i);
291 PRVM_ED_ClearEdict(prog, e);
299 Marks the edict as free
300 FIXME: walk all entities and NULL out references to this entity
303 void PRVM_ED_Free(prvm_prog_t *prog, prvm_edict_t *ed)
305 // dont delete the null entity (world) or reserved edicts
306 if (ed - prog->edicts <= prog->reserved_edicts)
309 prog->free_edict(prog, ed);
311 ed->priv.required->free = true;
312 ed->priv.required->freetime = host.realtime;
313 if(ed->priv.required->allocation_origin)
315 Mem_Free((char *)ed->priv.required->allocation_origin);
316 ed->priv.required->allocation_origin = NULL;
320 //===========================================================================
327 static ddef_t *PRVM_ED_GlobalAtOfs (prvm_prog_t *prog, int ofs)
332 for (i = 0;i < prog->numglobaldefs;i++)
334 def = &prog->globaldefs[i];
346 ddef_t *PRVM_ED_FieldAtOfs (prvm_prog_t *prog, int ofs)
351 for (i = 0;i < prog->numfielddefs;i++)
353 def = &prog->fielddefs[i];
365 ddef_t *PRVM_ED_FindField (prvm_prog_t *prog, const char *name)
370 for (i = 0;i < prog->numfielddefs;i++)
372 def = &prog->fielddefs[i];
373 if (!strcmp(PRVM_GetString(prog, def->s_name), name))
384 ddef_t *PRVM_ED_FindGlobal (prvm_prog_t *prog, const char *name)
389 for (i = 0;i < prog->numglobaldefs;i++)
391 def = &prog->globaldefs[i];
392 if (!strcmp(PRVM_GetString(prog, def->s_name), name))
400 PRVM_ED_FindGlobalEval
403 prvm_eval_t *PRVM_ED_FindGlobalEval(prvm_prog_t *prog, const char *name)
405 ddef_t *def = PRVM_ED_FindGlobal(prog, name);
406 return def ? (prvm_eval_t *) &prog->globals.fp[def->ofs] : NULL;
414 mfunction_t *PRVM_ED_FindFunction (prvm_prog_t *prog, const char *name)
419 for (i = 0;i < prog->numfunctions;i++)
421 func = &prog->functions[i];
422 if (!strcmp(PRVM_GetString(prog, func->s_name), name))
433 Returns a string describing *data in a type specific manner
436 static char *PRVM_ValueString (prvm_prog_t *prog, etype_t type, prvm_eval_t *val, char *line, size_t linelength)
442 type = (etype_t)((int) type & ~DEF_SAVEGLOBAL);
447 strlcpy (line, PRVM_GetString (prog, val->string), linelength);
451 if (n < 0 || n >= prog->max_edicts)
452 dpsnprintf (line, linelength, "entity %i (invalid!)", n);
454 dpsnprintf (line, linelength, "entity %i", n);
457 if ((unsigned int)val->function < (unsigned int)prog->progs_numfunctions)
459 f = prog->functions + val->function;
460 dpsnprintf (line, linelength, "%s()", PRVM_GetString(prog, f->s_name));
463 dpsnprintf (line, linelength, "function %" PRVM_PRIi "() (invalid!)", val->function);
466 def = PRVM_ED_FieldAtOfs ( prog, val->_int );
468 dpsnprintf (line, linelength, ".%s", PRVM_GetString(prog, def->s_name));
470 dpsnprintf (line, linelength, "field %" PRVM_PRIi " (invalid!)", val->_int );
473 dpsnprintf (line, linelength, "void");
476 // LadyHavoc: changed from %5.1f to %10.4f
477 dpsnprintf (line, linelength, FLOAT_LOSSLESS_FORMAT, val->_float);
480 // LadyHavoc: changed from %5.1f to %10.4f
481 dpsnprintf (line, linelength, "'" VECTOR_LOSSLESS_FORMAT "'", val->vector[0], val->vector[1], val->vector[2]);
484 dpsnprintf (line, linelength, "pointer");
487 dpsnprintf (line, linelength, "bad type %i", (int) type);
498 Returns a string describing *data in a type specific manner
499 Easier to parse than PR_ValueString
502 char *PRVM_UglyValueString (prvm_prog_t *prog, etype_t type, prvm_eval_t *val, char *line, size_t linelength)
509 type = (etype_t)((int)type & ~DEF_SAVEGLOBAL);
514 // Parse the string a bit to turn special characters
515 // (like newline, specifically) into escape codes,
516 // this fixes saving games from various mods
517 s = PRVM_GetString (prog, val->string);
518 for (i = 0;i < (int)linelength - 2 && *s;)
548 dpsnprintf (line, linelength, "%i", i);
551 if ((unsigned int)val->function < (unsigned int)prog->progs_numfunctions)
553 f = prog->functions + val->function;
554 strlcpy (line, PRVM_GetString (prog, f->s_name), linelength);
557 dpsnprintf (line, linelength, "bad function %" PRVM_PRIi " (invalid!)", val->function);
560 def = PRVM_ED_FieldAtOfs ( prog, val->_int );
562 dpsnprintf (line, linelength, ".%s", PRVM_GetString(prog, def->s_name));
564 dpsnprintf (line, linelength, "field %" PRVM_PRIi "(invalid!)", val->_int );
567 dpsnprintf (line, linelength, "void");
570 dpsnprintf (line, linelength, FLOAT_LOSSLESS_FORMAT, val->_float);
573 dpsnprintf (line, linelength, VECTOR_LOSSLESS_FORMAT, val->vector[0], val->vector[1], val->vector[2]);
576 dpsnprintf (line, linelength, "bad type %i", type);
587 Returns a string with a description and the contents of a global,
588 padded to 20 field width
591 char *PRVM_GlobalString (prvm_prog_t *prog, int ofs, char *line, size_t linelength)
597 char valuebuf[MAX_INPUTLINE];
599 val = (prvm_eval_t *)&prog->globals.fp[ofs];
600 def = PRVM_ED_GlobalAtOfs(prog, ofs);
602 dpsnprintf (line, linelength, "GLOBAL%i", ofs);
605 s = PRVM_ValueString (prog, (etype_t)def->type, val, valuebuf, sizeof(valuebuf));
606 dpsnprintf (line, linelength, "%s (=%s)", PRVM_GetString(prog, def->s_name), s);
610 //for ( ; i<20 ; i++)
611 // strcat (line," ");
617 char *PRVM_GlobalStringNoContents (prvm_prog_t *prog, int ofs, char *line, size_t linelength)
622 def = PRVM_ED_GlobalAtOfs(prog, ofs);
624 dpsnprintf (line, linelength, "GLOBAL%i", ofs);
626 dpsnprintf (line, linelength, "%s", PRVM_GetString(prog, def->s_name));
629 //for ( ; i<20 ; i++)
630 // strcat (line," ");
644 // LadyHavoc: optimized this to print out much more quickly (tempstring)
645 // LadyHavoc: changed to print out every 4096 characters (incase there are a lot of fields to print)
646 void PRVM_ED_Print(prvm_prog_t *prog, prvm_edict_t *ed, const char *wildcard_fieldname)
654 char tempstring[MAX_INPUTLINE], tempstring2[260]; // temporary string buffers
655 char valuebuf[MAX_INPUTLINE];
657 if (ed->priv.required->free)
659 Con_Printf("%s: FREE\n",prog->name);
664 dpsnprintf(tempstring, sizeof(tempstring), "\n%s EDICT %i:\n", prog->name, PRVM_NUM_FOR_EDICT(ed));
665 for (i = 1;i < prog->numfielddefs;i++)
667 d = &prog->fielddefs[i];
668 name = PRVM_GetString(prog, d->s_name);
669 if(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
670 continue; // skip _x, _y, _z vars
672 // Check Field Name Wildcard
673 if(wildcard_fieldname)
674 if( !matchpattern(name, wildcard_fieldname, 1) )
675 // Didn't match; skip
678 val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
680 // if the value is still all 0, skip the field
681 type = d->type & ~DEF_SAVEGLOBAL;
683 for (j=0 ; j<prvm_type_size[type] ; j++)
686 if (j == prvm_type_size[type])
689 if (strlen(name) > sizeof(tempstring2)-4)
691 memcpy (tempstring2, name, sizeof(tempstring2)-4);
692 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
693 tempstring2[sizeof(tempstring2)-1] = 0;
696 strlcat(tempstring, name, sizeof(tempstring));
697 for (l = strlen(name);l < 14;l++)
698 strlcat(tempstring, " ", sizeof(tempstring));
699 strlcat(tempstring, " ", sizeof(tempstring));
701 name = PRVM_ValueString(prog, (etype_t)d->type, val, valuebuf, sizeof(valuebuf));
702 if (strlen(name) > sizeof(tempstring2)-4)
704 memcpy (tempstring2, name, sizeof(tempstring2)-4);
705 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
706 tempstring2[sizeof(tempstring2)-1] = 0;
709 strlcat(tempstring, name, sizeof(tempstring));
710 strlcat(tempstring, "\n", sizeof(tempstring));
711 if (strlen(tempstring) >= sizeof(tempstring)/2)
713 Con_Print(tempstring);
718 Con_Print(tempstring);
728 void PRVM_ED_Write (prvm_prog_t *prog, qfile_t *f, prvm_edict_t *ed)
736 char valuebuf[MAX_INPUTLINE];
740 if (ed->priv.required->free)
746 for (i = 1;i < prog->numfielddefs;i++)
748 d = &prog->fielddefs[i];
749 name = PRVM_GetString(prog, d->s_name);
751 if(developer_entityparsing.integer)
752 Con_Printf("PRVM_ED_Write: at entity %d field %s\n", PRVM_NUM_FOR_EDICT(ed), name);
754 //if(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
755 if(strlen(name) > 1 && name[strlen(name)-2] == '_')
756 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?)
758 val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
760 // if the value is still all 0, skip the field
761 type = d->type & ~DEF_SAVEGLOBAL;
762 for (j=0 ; j<prvm_type_size[type] ; j++)
765 if (j == prvm_type_size[type])
768 FS_Printf(f,"\"%s\" ",name);
769 prog->statestring = va(vabuf, sizeof(vabuf), "PRVM_ED_Write, ent=%d, name=%s", i, name);
770 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString(prog, (etype_t)d->type, val, valuebuf, sizeof(valuebuf)));
771 prog->statestring = NULL;
777 void PRVM_ED_PrintNum (prvm_prog_t *prog, int ent, const char *wildcard_fieldname)
779 PRVM_ED_Print(prog, PRVM_EDICT_NUM(ent), wildcard_fieldname);
784 PRVM_ED_PrintEdicts_f
786 For debugging, prints all the entities in the current server
789 void PRVM_ED_PrintEdicts_f(cmd_state_t *cmd)
793 const char *wildcard_fieldname;
795 if(Cmd_Argc(cmd) < 2 || Cmd_Argc(cmd) > 3)
797 Con_Print("prvm_edicts <program name> <optional field name wildcard>\n");
801 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
804 if( Cmd_Argc(cmd) == 3)
805 wildcard_fieldname = Cmd_Argv(cmd, 2);
807 wildcard_fieldname = NULL;
809 Con_Printf("%s: %i entities\n", prog->name, prog->num_edicts);
810 for (i=0 ; i<prog->num_edicts ; i++)
811 PRVM_ED_PrintNum (prog, i, wildcard_fieldname);
818 For debugging, prints a single edict
821 static void PRVM_ED_PrintEdict_f(cmd_state_t *cmd)
825 const char *wildcard_fieldname;
827 if(Cmd_Argc(cmd) < 3 || Cmd_Argc(cmd) > 4)
829 Con_Print("prvm_edict <program name> <edict number> <optional field name wildcard>\n");
833 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
836 i = atoi (Cmd_Argv(cmd, 2));
837 if (i >= prog->num_edicts)
839 Con_Print("Bad edict number\n");
842 if( Cmd_Argc(cmd) == 4)
843 // Optional Wildcard Provided
844 wildcard_fieldname = Cmd_Argv(cmd, 3);
847 wildcard_fieldname = NULL;
848 PRVM_ED_PrintNum (prog, i, wildcard_fieldname);
858 // 2 possibilities : 1. just displaying the active edict count
859 // 2. making a function pointer [x]
860 static void PRVM_ED_Count_f(cmd_state_t *cmd)
864 if(Cmd_Argc(cmd) != 2)
866 Con_Print("prvm_count <program name>\n");
870 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
873 prog->count_edicts(prog);
877 ==============================================================================
881 FIXME: need to tag constants, doesn't really work
882 ==============================================================================
890 void PRVM_ED_WriteGlobals (prvm_prog_t *prog, qfile_t *f)
897 char valuebuf[MAX_INPUTLINE];
900 for (i = 0;i < prog->numglobaldefs;i++)
902 def = &prog->globaldefs[i];
904 if ( !(def->type & DEF_SAVEGLOBAL) )
906 type &= ~DEF_SAVEGLOBAL;
908 if (type != ev_string && type != ev_float && type != ev_entity)
911 name = PRVM_GetString(prog, def->s_name);
913 if(developer_entityparsing.integer)
914 Con_Printf("PRVM_ED_WriteGlobals: at global %s\n", name);
916 prog->statestring = va(vabuf, sizeof(vabuf), "PRVM_ED_WriteGlobals, name=%s", name);
917 FS_Printf(f,"\"%s\" ", name);
918 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString(prog, (etype_t)type, (prvm_eval_t *)&prog->globals.fp[def->ofs], valuebuf, sizeof(valuebuf)));
919 prog->statestring = NULL;
929 void PRVM_ED_ParseGlobals (prvm_prog_t *prog, const char *data)
931 char keyname[MAX_INPUTLINE];
937 if (!COM_ParseToken_Simple(&data, false, false, true))
938 prog->error_cmd("PRVM_ED_ParseGlobals: EOF without closing brace");
939 if (com_token[0] == '}')
942 if (developer_entityparsing.integer)
943 Con_Printf("Key: \"%s\"", com_token);
945 strlcpy (keyname, com_token, sizeof(keyname));
948 if (!COM_ParseToken_Simple(&data, false, true, true))
949 prog->error_cmd("PRVM_ED_ParseGlobals: EOF without closing brace");
951 if (developer_entityparsing.integer)
952 Con_Printf(" \"%s\"\n", com_token);
954 if (com_token[0] == '}')
955 prog->error_cmd("PRVM_ED_ParseGlobals: closing brace without data");
957 key = PRVM_ED_FindGlobal (prog, keyname);
960 Con_DPrintf("'%s' is not a global on %s\n", keyname, prog->name);
964 if (!PRVM_ED_ParseEpair(prog, NULL, key, com_token, true))
965 prog->error_cmd("PRVM_ED_ParseGlobals: parse error");
969 //============================================================================
976 Can parse either fields or globals
977 returns false if error
980 qboolean PRVM_ED_ParseEpair(prvm_prog_t *prog, prvm_edict_t *ent, ddef_t *key, const char *s, qboolean parsebackslash)
989 val = (prvm_eval_t *)(ent->fields.fp + key->ofs);
991 val = (prvm_eval_t *)(prog->globals.fp + key->ofs);
992 switch (key->type & ~DEF_SAVEGLOBAL)
995 l = (int)strlen(s) + 1;
996 val->string = PRVM_AllocString(prog, l, &new_p);
997 for (i = 0;i < l;i++)
999 if (s[i] == '\\' && s[i+1] && parsebackslash)
1004 else if (s[i] == 'r')
1015 while (*s && ISWHITESPACE(*s))
1017 val->_float = atof(s);
1021 for (i = 0;i < 3;i++)
1023 while (*s && ISWHITESPACE(*s))
1027 val->vector[i] = atof(s);
1028 while (!ISWHITESPACE(*s))
1036 while (*s && ISWHITESPACE(*s))
1039 if (i >= prog->limit_edicts)
1040 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);
1041 while (i >= prog->max_edicts)
1042 PRVM_MEM_IncreaseEdicts(prog);
1043 // if IncreaseEdicts was called the base pointer needs to be updated
1045 val = (prvm_eval_t *)(ent->fields.fp + key->ofs);
1046 val->edict = PRVM_EDICT_TO_PROG(PRVM_EDICT_NUM((int)i));
1052 Con_DPrintf("PRVM_ED_ParseEpair: Bogus field name %s in %s\n", s, prog->name);
1055 def = PRVM_ED_FindField(prog, s + 1);
1058 Con_DPrintf("PRVM_ED_ParseEpair: Can't find field %s in %s\n", s, prog->name);
1061 val->_int = def->ofs;
1065 func = PRVM_ED_FindFunction(prog, s);
1068 Con_Printf("PRVM_ED_ParseEpair: Can't find function %s in %s\n", s, prog->name);
1071 val->function = func - prog->functions;
1075 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);
1085 Console command to send a string to QC function GameCommand of the
1089 sv_cmd adminmsg 3 "do not teamkill"
1090 cl_cmd someclientcommand
1091 menu_cmd somemenucommand
1093 All progs can support this extension; sg calls it in server QC, cg in client
1097 static void PRVM_GameCommand(cmd_state_t *cmd, const char *whichprogs, const char *whichcmd)
1100 if(Cmd_Argc(cmd) < 1)
1102 Con_Printf("%s text...\n", whichcmd);
1106 if (!(prog = PRVM_FriendlyProgFromString(whichprogs)))
1109 if(!PRVM_allfunction(GameCommand))
1111 Con_Printf("%s program do not support GameCommand!\n", whichprogs);
1115 int restorevm_tempstringsbuf_cursize;
1120 restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
1121 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(prog, s ? s : "");
1122 prog->ExecuteProgram(prog, PRVM_allfunction(GameCommand), "QC function GameCommand is missing");
1123 prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1126 static void PRVM_GameCommand_Server_f(cmd_state_t *cmd)
1128 PRVM_GameCommand(cmd, "server", "sv_cmd");
1130 static void PRVM_GameCommand_Client_f(cmd_state_t *cmd)
1132 PRVM_GameCommand(cmd, "client", "cl_cmd");
1134 static void PRVM_GameCommand_Menu_f(cmd_state_t *cmd)
1136 PRVM_GameCommand(cmd, "menu", "menu_cmd");
1143 Console command to load a field of a specified edict
1146 static void PRVM_ED_EdictGet_f(cmd_state_t *cmd)
1153 char valuebuf[MAX_INPUTLINE];
1155 if(Cmd_Argc(cmd) != 4 && Cmd_Argc(cmd) != 5)
1157 Con_Print("prvm_edictget <program name> <edict number> <field> [<cvar>]\n");
1161 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
1164 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(cmd, 2)));
1166 if((key = PRVM_ED_FindField(prog, Cmd_Argv(cmd, 3))) == 0)
1168 Con_Printf("Key %s not found !\n", Cmd_Argv(cmd, 3));
1172 v = (prvm_eval_t *)(ed->fields.fp + key->ofs);
1173 s = PRVM_UglyValueString(prog, (etype_t)key->type, v, valuebuf, sizeof(valuebuf));
1174 if(Cmd_Argc(cmd) == 5)
1176 cvar_t *cvar = Cvar_FindVar(cmd->cvars, Cmd_Argv(cmd, 4), cmd->cvars_flagsmask);
1178 if(Cvar_Readonly(cvar, "prvm_edictget"))
1181 Cvar_Get(cmd->cvars, Cmd_Argv(cmd, 4), s, cmd->cvars_flagsmask, NULL);
1184 Con_Printf("%s\n", s);
1190 static void PRVM_ED_GlobalGet_f(cmd_state_t *cmd)
1196 char valuebuf[MAX_INPUTLINE];
1198 if(Cmd_Argc(cmd) != 3 && Cmd_Argc(cmd) != 4)
1200 Con_Print("prvm_globalget <program name> <global> [<cvar>]\n");
1204 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
1207 key = PRVM_ED_FindGlobal(prog, Cmd_Argv(cmd, 2));
1210 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(cmd, 2), Cmd_Argv(cmd, 1) );
1214 v = (prvm_eval_t *) &prog->globals.fp[key->ofs];
1215 s = PRVM_UglyValueString(prog, (etype_t)key->type, v, valuebuf, sizeof(valuebuf));
1216 if(Cmd_Argc(cmd) == 4)
1218 cvar_t *cvar = Cvar_FindVar(cmd->cvars, Cmd_Argv(cmd, 3), cmd->cvars_flagsmask);
1220 if(Cvar_Readonly(cvar, "prvm_globalget"))
1222 Cvar_Get(cmd->cvars, Cmd_Argv(cmd, 3), s, cmd->cvars_flagsmask, NULL);
1225 Con_Printf("%s\n", s);
1235 Console command to set a field of a specified edict
1238 static void PRVM_ED_EdictSet_f(cmd_state_t *cmd)
1244 if(Cmd_Argc(cmd) != 5)
1246 Con_Print("prvm_edictset <program name> <edict number> <field> <value>\n");
1250 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
1253 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(cmd, 2)));
1255 if((key = PRVM_ED_FindField(prog, Cmd_Argv(cmd, 3))) == 0)
1256 Con_Printf("Key %s not found !\n", Cmd_Argv(cmd, 3));
1258 PRVM_ED_ParseEpair(prog, ed, key, Cmd_Argv(cmd, 4), true);
1262 ====================
1265 Parses an edict out of the given string, returning the new position
1266 ed should be a properly initialized empty edict.
1267 Used for initial level load and for savegames.
1268 ====================
1270 const char *PRVM_ED_ParseEdict (prvm_prog_t *prog, const char *data, prvm_edict_t *ent)
1280 // go through all the dictionary pairs
1284 if (!COM_ParseToken_Simple(&data, false, false, true))
1285 prog->error_cmd("PRVM_ED_ParseEdict: EOF without closing brace");
1286 if (developer_entityparsing.integer)
1287 Con_Printf("Key: \"%s\"", com_token);
1288 if (com_token[0] == '}')
1291 // anglehack is to allow QuakeEd to write single scalar angles
1292 // and allow them to be turned into vectors. (FIXME...)
1293 if (!strcmp(com_token, "angle"))
1295 strlcpy (com_token, "angles", sizeof(com_token));
1301 // FIXME: change light to _light to get rid of this hack
1302 if (!strcmp(com_token, "light"))
1303 strlcpy (com_token, "light_lev", sizeof(com_token)); // hack for single light def
1305 strlcpy (keyname, com_token, sizeof(keyname));
1307 // another hack to fix keynames with trailing spaces
1308 n = strlen(keyname);
1309 while (n && keyname[n-1] == ' ')
1316 if (!COM_ParseToken_Simple(&data, false, false, true))
1317 prog->error_cmd("PRVM_ED_ParseEdict: EOF without closing brace");
1318 if (developer_entityparsing.integer)
1319 Con_Printf(" \"%s\"\n", com_token);
1321 if (com_token[0] == '}')
1322 prog->error_cmd("PRVM_ED_ParseEdict: closing brace without data");
1326 // ignore attempts to set key "" (this problem occurs in nehahra neh1m8.bsp)
1330 // keynames with a leading underscore are used for utility comments,
1331 // and are immediately discarded by quake
1332 if (keyname[0] == '_')
1335 key = PRVM_ED_FindField (prog, keyname);
1338 Con_DPrintf("%s: '%s' is not a field\n", prog->name, keyname);
1345 strlcpy (temp, com_token, sizeof(temp));
1346 dpsnprintf (com_token, sizeof(com_token), "0 %s 0", temp);
1349 if (!PRVM_ED_ParseEpair(prog, ent, key, com_token, strcmp(keyname, "wad") != 0))
1350 prog->error_cmd("PRVM_ED_ParseEdict: parse error");
1354 ent->priv.required->free = true;
1355 ent->priv.required->freetime = host.realtime;
1364 PRVM_ED_LoadFromFile
1366 The entities are directly placed in the array, rather than allocated with
1367 PRVM_ED_Alloc, because otherwise an error loading the map would have entity
1368 number references out of order.
1370 Creates a server's entity / program execution context by
1371 parsing textual entity definitions out of an ent file.
1373 Used for both fresh maps and savegame loads. A fresh map would also need
1374 to call PRVM_ED_CallSpawnFunctions () to let the objects initialize themselves.
1377 void PRVM_ED_LoadFromFile (prvm_prog_t *prog, const char *data)
1381 int parsed, inhibited, spawned, died;
1382 ddef_t *fulldata_ddef = NULL;
1383 prvm_eval_t *fulldata = NULL;
1384 const char *funcname;
1393 prvm_reuseedicts_always_allow = host.realtime;
1400 // parse the opening brace
1401 if (!COM_ParseToken_Simple(&data, false, false, true))
1403 if (com_token[0] != '{')
1404 prog->error_cmd("PRVM_ED_LoadFromFile: %s: found %s when expecting {", prog->name, com_token);
1406 // CHANGED: this is not conform to PR_LoadFromFile
1407 if(prog->loadintoworld)
1409 prog->loadintoworld = false;
1410 ent = PRVM_EDICT_NUM(0);
1413 ent = PRVM_ED_Alloc(prog);
1416 if (ent != prog->edicts) // hack
1417 memset (ent->fields.fp, 0, prog->entityfields * sizeof(prvm_vec_t));
1419 data = PRVM_ED_ParseEdict (prog, data, ent);
1422 // remove the entity ?
1423 if(!prog->load_edict(prog, ent))
1425 PRVM_ED_Free(prog, ent);
1430 if (PRVM_serverfunction(SV_OnEntityPreSpawnFunction))
1433 PRVM_serverglobalfloat(time) = sv.time;
1434 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1435 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityPreSpawnFunction), "QC function SV_OnEntityPreSpawnFunction is missing");
1438 if(ent->priv.required->free)
1445 // immediately call spawn function, but only if there is a self global and a classname
1447 if(!ent->priv.required->free)
1449 if (!PRVM_alledictstring(ent, classname))
1451 Con_Print("No classname for:\n");
1452 PRVM_ED_Print(prog, ent, NULL);
1453 PRVM_ED_Free (prog, ent);
1457 * This is required for FTE compatibility (FreeCS).
1458 * It copies the key/value pairs themselves into a
1459 * global for QC to parse on its own.
1463 fulldata_ddef = PRVM_ED_FindGlobal(prog, "__fullspawndata");
1465 fulldata = (prvm_eval_t *) &prog->globals.fp[fulldata_ddef->ofs];
1470 fulldata->string = PRVM_AllocString(prog, data - start + 1, &spawndata);
1471 for(in = start; in < data; )
1475 *spawndata++ = '\t';
1483 // look for the spawn function
1484 funcname = PRVM_GetString(prog, PRVM_alledictstring(ent, classname));
1485 func = PRVM_ED_FindFunction (prog, va(vabuf, sizeof(vabuf), "spawnfunc_%s", funcname));
1487 if(!PRVM_allglobalfloat(require_spawnfunc_prefix))
1488 func = PRVM_ED_FindFunction (prog, funcname);
1492 // check for OnEntityNoSpawnFunction
1493 if (PRVM_serverfunction(SV_OnEntityNoSpawnFunction))
1496 PRVM_serverglobalfloat(time) = sv.time;
1497 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1498 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityNoSpawnFunction), "QC function SV_OnEntityNoSpawnFunction is missing");
1502 if (developer.integer > 0) // don't confuse non-developers with errors
1504 Con_Print("No spawn function for:\n");
1505 PRVM_ED_Print(prog, ent, NULL);
1507 PRVM_ED_Free (prog, ent);
1508 continue; // not included in "inhibited" count
1514 PRVM_serverglobalfloat(time) = sv.time;
1515 PRVM_allglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1516 prog->ExecuteProgram(prog, func - prog->functions, "");
1520 if(!ent->priv.required->free)
1521 if (PRVM_serverfunction(SV_OnEntityPostSpawnFunction))
1524 PRVM_serverglobalfloat(time) = sv.time;
1525 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1526 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityPostSpawnFunction), "QC function SV_OnEntityPostSpawnFunction is missing");
1530 if (ent->priv.required->free)
1534 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);
1536 prvm_reuseedicts_always_allow = 0;
1539 static void PRVM_FindOffsets(prvm_prog_t *prog)
1541 // field and global searches use -1 for NULL
1542 memset(&prog->fieldoffsets, -1, sizeof(prog->fieldoffsets));
1543 memset(&prog->globaloffsets, -1, sizeof(prog->globaloffsets));
1544 // function searches use 0 for NULL
1545 memset(&prog->funcoffsets, 0, sizeof(prog->funcoffsets));
1546 #define PRVM_DECLARE_serverglobalfloat(x)
1547 #define PRVM_DECLARE_serverglobalvector(x)
1548 #define PRVM_DECLARE_serverglobalstring(x)
1549 #define PRVM_DECLARE_serverglobaledict(x)
1550 #define PRVM_DECLARE_serverglobalfunction(x)
1551 #define PRVM_DECLARE_clientglobalfloat(x)
1552 #define PRVM_DECLARE_clientglobalvector(x)
1553 #define PRVM_DECLARE_clientglobalstring(x)
1554 #define PRVM_DECLARE_clientglobaledict(x)
1555 #define PRVM_DECLARE_clientglobalfunction(x)
1556 #define PRVM_DECLARE_menuglobalfloat(x)
1557 #define PRVM_DECLARE_menuglobalvector(x)
1558 #define PRVM_DECLARE_menuglobalstring(x)
1559 #define PRVM_DECLARE_menuglobaledict(x)
1560 #define PRVM_DECLARE_menuglobalfunction(x)
1561 #define PRVM_DECLARE_serverfieldfloat(x)
1562 #define PRVM_DECLARE_serverfieldvector(x)
1563 #define PRVM_DECLARE_serverfieldstring(x)
1564 #define PRVM_DECLARE_serverfieldedict(x)
1565 #define PRVM_DECLARE_serverfieldfunction(x)
1566 #define PRVM_DECLARE_clientfieldfloat(x)
1567 #define PRVM_DECLARE_clientfieldvector(x)
1568 #define PRVM_DECLARE_clientfieldstring(x)
1569 #define PRVM_DECLARE_clientfieldedict(x)
1570 #define PRVM_DECLARE_clientfieldfunction(x)
1571 #define PRVM_DECLARE_menufieldfloat(x)
1572 #define PRVM_DECLARE_menufieldvector(x)
1573 #define PRVM_DECLARE_menufieldstring(x)
1574 #define PRVM_DECLARE_menufieldedict(x)
1575 #define PRVM_DECLARE_menufieldfunction(x)
1576 #define PRVM_DECLARE_serverfunction(x)
1577 #define PRVM_DECLARE_clientfunction(x)
1578 #define PRVM_DECLARE_menufunction(x)
1579 #define PRVM_DECLARE_field(x) prog->fieldoffsets.x = PRVM_ED_FindFieldOffset(prog, #x);
1580 #define PRVM_DECLARE_global(x) prog->globaloffsets.x = PRVM_ED_FindGlobalOffset(prog, #x);
1581 #define PRVM_DECLARE_function(x) prog->funcoffsets.x = PRVM_ED_FindFunctionOffset(prog, #x);
1582 #include "prvm_offsets.h"
1583 #undef PRVM_DECLARE_serverglobalfloat
1584 #undef PRVM_DECLARE_serverglobalvector
1585 #undef PRVM_DECLARE_serverglobalstring
1586 #undef PRVM_DECLARE_serverglobaledict
1587 #undef PRVM_DECLARE_serverglobalfunction
1588 #undef PRVM_DECLARE_clientglobalfloat
1589 #undef PRVM_DECLARE_clientglobalvector
1590 #undef PRVM_DECLARE_clientglobalstring
1591 #undef PRVM_DECLARE_clientglobaledict
1592 #undef PRVM_DECLARE_clientglobalfunction
1593 #undef PRVM_DECLARE_menuglobalfloat
1594 #undef PRVM_DECLARE_menuglobalvector
1595 #undef PRVM_DECLARE_menuglobalstring
1596 #undef PRVM_DECLARE_menuglobaledict
1597 #undef PRVM_DECLARE_menuglobalfunction
1598 #undef PRVM_DECLARE_serverfieldfloat
1599 #undef PRVM_DECLARE_serverfieldvector
1600 #undef PRVM_DECLARE_serverfieldstring
1601 #undef PRVM_DECLARE_serverfieldedict
1602 #undef PRVM_DECLARE_serverfieldfunction
1603 #undef PRVM_DECLARE_clientfieldfloat
1604 #undef PRVM_DECLARE_clientfieldvector
1605 #undef PRVM_DECLARE_clientfieldstring
1606 #undef PRVM_DECLARE_clientfieldedict
1607 #undef PRVM_DECLARE_clientfieldfunction
1608 #undef PRVM_DECLARE_menufieldfloat
1609 #undef PRVM_DECLARE_menufieldvector
1610 #undef PRVM_DECLARE_menufieldstring
1611 #undef PRVM_DECLARE_menufieldedict
1612 #undef PRVM_DECLARE_menufieldfunction
1613 #undef PRVM_DECLARE_serverfunction
1614 #undef PRVM_DECLARE_clientfunction
1615 #undef PRVM_DECLARE_menufunction
1616 #undef PRVM_DECLARE_field
1617 #undef PRVM_DECLARE_global
1618 #undef PRVM_DECLARE_function
1623 typedef struct dpfield_s
1630 #define DPFIELDS (sizeof(dpfields) / sizeof(dpfield_t))
1632 dpfield_t dpfields[] =
1643 #define PO_HASHSIZE 16384
1644 typedef struct po_string_s
1647 struct po_string_s *nextonhashchain;
1652 po_string_t *hashtable[PO_HASHSIZE];
1655 static void PRVM_PO_UnparseString(char *out, const char *in, size_t outsize)
1664 case '\a': if(outsize >= 2) { *out++ = '\\'; *out++ = 'a'; outsize -= 2; } break;
1665 case '\b': if(outsize >= 2) { *out++ = '\\'; *out++ = 'b'; outsize -= 2; } break;
1666 case '\t': if(outsize >= 2) { *out++ = '\\'; *out++ = 't'; outsize -= 2; } break;
1667 case '\r': if(outsize >= 2) { *out++ = '\\'; *out++ = 'r'; outsize -= 2; } break;
1668 case '\n': if(outsize >= 2) { *out++ = '\\'; *out++ = 'n'; outsize -= 2; } break;
1669 case '\\': if(outsize >= 2) { *out++ = '\\'; *out++ = '\\'; outsize -= 2; } break;
1670 case '"': if(outsize >= 2) { *out++ = '\\'; *out++ = '"'; outsize -= 2; } break;
1672 if(*in >= 0 && *in <= 0x1F)
1677 *out++ = '0' + ((*in & 0700) >> 6);
1678 *out++ = '0' + ((*in & 0070) >> 3);
1679 *out++ = '0' + (*in & 0007) ;
1696 static void PRVM_PO_ParseString(char *out, const char *in, size_t outsize)
1709 case 'a': if(outsize > 0) { *out++ = '\a'; --outsize; } break;
1710 case 'b': if(outsize > 0) { *out++ = '\b'; --outsize; } break;
1711 case 't': if(outsize > 0) { *out++ = '\t'; --outsize; } break;
1712 case 'r': if(outsize > 0) { *out++ = '\r'; --outsize; } break;
1713 case 'n': if(outsize > 0) { *out++ = '\n'; --outsize; } break;
1714 case '\\': if(outsize > 0) { *out++ = '\\'; --outsize; } break;
1715 case '"': if(outsize > 0) { *out++ = '"'; --outsize; } break;
1716 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
1720 if(*in >= '0' && *in <= '7')
1723 *out = (*out << 3) | (*in - '0');
1726 if(*in >= '0' && *in <= '7')
1729 *out = (*out << 3) | (*in - '0');
1740 if(outsize > 0) { *out++ = *in; --outsize; }
1755 static po_t *PRVM_PO_Load(const char *filename, const char *filename2, mempool_t *pool)
1760 char inbuf[MAX_INPUTLINE];
1761 char decodedbuf[MAX_INPUTLINE];
1764 po_string_t thisstr;
1767 for (i = 0; i < 2; ++i)
1769 const char *buf = (const char *)
1770 FS_LoadFile((i > 0 ? filename : filename2), pool, true, NULL);
1771 // first read filename2, then read filename
1772 // so that progs.dat.de.po wins over common.de.po
1773 // and within file, last item wins
1780 po = (po_t *)Mem_Alloc(pool, sizeof(*po));
1781 memset(po, 0, sizeof(*po));
1784 memset(&thisstr, 0, sizeof(thisstr)); // hush compiler warning
1792 p = strchr(p, '\n');
1798 if(*p == '\r' || *p == '\n')
1803 if(!strncmp(p, "msgid \"", 7))
1808 else if(!strncmp(p, "msgstr \"", 8))
1815 p = strchr(p, '\n');
1825 q = strchr(p, '\n');
1832 if((size_t)(q - p) >= (size_t) sizeof(inbuf))
1834 strlcpy(inbuf, p, q - p); // not - 1, because this adds a NUL
1835 PRVM_PO_ParseString(decodedbuf + decodedpos, inbuf, sizeof(decodedbuf) - decodedpos);
1836 decodedpos += strlen(decodedbuf + decodedpos);
1846 Mem_Free(thisstr.key);
1847 thisstr.key = (char *)Mem_Alloc(pool, decodedpos + 1);
1848 memcpy(thisstr.key, decodedbuf, decodedpos + 1);
1850 else if(decodedpos > 0 && thisstr.key) // skip empty translation results
1852 thisstr.value = (char *)Mem_Alloc(pool, decodedpos + 1);
1853 memcpy(thisstr.value, decodedbuf, decodedpos + 1);
1854 hashindex = CRC_Block((const unsigned char *) thisstr.key, strlen(thisstr.key)) % PO_HASHSIZE;
1855 thisstr.nextonhashchain = po->hashtable[hashindex];
1856 po->hashtable[hashindex] = (po_string_t *)Mem_Alloc(pool, sizeof(thisstr));
1857 memcpy(po->hashtable[hashindex], &thisstr, sizeof(thisstr));
1858 memset(&thisstr, 0, sizeof(thisstr));
1862 Mem_Free((char *) buf);
1867 static const char *PRVM_PO_Lookup(po_t *po, const char *str)
1869 int hashindex = CRC_Block((const unsigned char *) str, strlen(str)) % PO_HASHSIZE;
1870 po_string_t *p = po->hashtable[hashindex];
1873 if(!strcmp(str, p->key))
1875 p = p->nextonhashchain;
1879 static void PRVM_PO_Destroy(po_t *po)
1882 for(i = 0; i < PO_HASHSIZE; ++i)
1884 po_string_t *p = po->hashtable[i];
1888 p = p->nextonhashchain;
1897 void PRVM_LeakTest(prvm_prog_t *prog);
1898 void PRVM_Prog_Reset(prvm_prog_t *prog)
1902 if(prog->tempstringsbuf.cursize)
1903 Mem_Free(prog->tempstringsbuf.data);
1904 prog->tempstringsbuf.cursize = 0;
1905 PRVM_LeakTest(prog);
1906 prog->reset_cmd(prog);
1907 Mem_FreePool(&prog->progs_mempool);
1909 PRVM_PO_Destroy((po_t *) prog->po);
1911 memset(prog,0,sizeof(prvm_prog_t));
1912 prog->break_statement = -1;
1913 prog->watch_global_type = ev_void;
1914 prog->watch_field_type = ev_void;
1922 static void PRVM_LoadLNO( prvm_prog_t *prog, const char *progname ) {
1923 fs_offset_t filesize;
1925 unsigned int *header;
1928 FS_StripExtension( progname, filename, sizeof( filename ) );
1929 strlcat( filename, ".lno", sizeof( filename ) );
1931 lno = FS_LoadFile( filename, tempmempool, false, &filesize );
1937 <Spike> SafeWrite (h, &lnotype, sizeof(int));
1938 <Spike> SafeWrite (h, &version, sizeof(int));
1939 <Spike> SafeWrite (h, &numglobaldefs, sizeof(int));
1940 <Spike> SafeWrite (h, &numpr_globals, sizeof(int));
1941 <Spike> SafeWrite (h, &numfielddefs, sizeof(int));
1942 <Spike> SafeWrite (h, &numstatements, sizeof(int));
1943 <Spike> SafeWrite (h, statement_linenums, numstatements*sizeof(int));
1945 if ((unsigned int)filesize < (6 + prog->progs_numstatements) * sizeof(int))
1951 header = (unsigned int *) lno;
1952 if( header[ 0 ] == *(unsigned int *) "LNOF" &&
1953 LittleLong( header[ 1 ] ) == 1 &&
1954 (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs_numglobaldefs &&
1955 (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs_numglobals &&
1956 (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs_numfielddefs &&
1957 (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs_numstatements )
1959 prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof( int ) );
1960 memcpy( prog->statement_linenums, header + 6, prog->progs_numstatements * sizeof( int ) );
1962 /* gmqcc suports columnums */
1963 if ((unsigned int)filesize > ((6 + 2 * prog->progs_numstatements) * sizeof( int )))
1965 prog->statement_columnnums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof( int ) );
1966 memcpy( prog->statement_columnnums, header + 6 + prog->progs_numstatements, prog->progs_numstatements * sizeof( int ) );
1977 static void PRVM_UpdateBreakpoints(prvm_prog_t *prog);
1978 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)
1981 dprograms_t *dprograms;
1982 dstatement_t *instatements;
1983 ddef_t *infielddefs;
1984 ddef_t *inglobaldefs;
1986 dfunction_t *infunctions;
1988 fs_offset_t filesize;
1989 int requiredglobalspace;
2006 prog->error_cmd("PRVM_LoadProgs: there is already a %s program loaded!", prog->name );
2008 Host_LockSession(); // all progs can use the session cvar
2009 Crypto_LoadKeys(); // all progs might use the keys at init time
2013 dprograms = (dprograms_t *) data;
2017 dprograms = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
2018 if (dprograms == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
2019 prog->error_cmd("PRVM_LoadProgs: couldn't load %s for %s", filename, prog->name);
2020 // TODO bounds check header fields (e.g. numstatements), they must never go behind end of file
2022 prog->profiletime = Sys_DirtyTime();
2023 prog->starttime = host.realtime;
2025 requiredglobalspace = 0;
2026 for (i = 0;i < numrequiredglobals;i++)
2027 requiredglobalspace += required_global[i].type == ev_vector ? 3 : 1;
2029 prog->filecrc = CRC_Block((unsigned char *)dprograms, filesize);
2031 // byte swap the header
2032 prog->progs_version = LittleLong(dprograms->version);
2033 prog->progs_crc = LittleLong(dprograms->crc);
2034 if (prog->progs_version != PROG_VERSION)
2035 prog->error_cmd("%s: %s has wrong version number (%i should be %i)", prog->name, filename, prog->progs_version, PROG_VERSION);
2036 instatements = (dstatement_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_statements));
2037 prog->progs_numstatements = LittleLong(dprograms->numstatements);
2038 inglobaldefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globaldefs));
2039 prog->progs_numglobaldefs = LittleLong(dprograms->numglobaldefs);
2040 infielddefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_fielddefs));
2041 prog->progs_numfielddefs = LittleLong(dprograms->numfielddefs);
2042 infunctions = (dfunction_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_functions));
2043 prog->progs_numfunctions = LittleLong(dprograms->numfunctions);
2044 instrings = (char *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_strings));
2045 prog->progs_numstrings = LittleLong(dprograms->numstrings);
2046 inglobals = (int *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globals));
2047 prog->progs_numglobals = LittleLong(dprograms->numglobals);
2048 prog->progs_entityfields = LittleLong(dprograms->entityfields);
2050 prog->numstatements = prog->progs_numstatements;
2051 prog->numglobaldefs = prog->progs_numglobaldefs;
2052 prog->numfielddefs = prog->progs_numfielddefs;
2053 prog->numfunctions = prog->progs_numfunctions;
2054 prog->numstrings = prog->progs_numstrings;
2055 prog->numglobals = prog->progs_numglobals;
2056 prog->entityfields = prog->progs_entityfields;
2058 if (LittleLong(dprograms->ofs_strings) + prog->progs_numstrings > (int)filesize)
2059 prog->error_cmd("%s: %s strings go past end of file", prog->name, filename);
2060 prog->strings = (char *)Mem_Alloc(prog->progs_mempool, prog->progs_numstrings);
2061 memcpy(prog->strings, instrings, prog->progs_numstrings);
2062 prog->stringssize = prog->progs_numstrings;
2064 prog->numknownstrings = 0;
2065 prog->maxknownstrings = 0;
2066 prog->knownstrings = NULL;
2067 prog->knownstrings_flags = NULL;
2069 Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
2071 // we need to expand the globaldefs and fielddefs to include engine defs
2072 prog->globaldefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobaldefs + numrequiredglobals) * sizeof(ddef_t));
2073 prog->globals.fp = (prvm_vec_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobals + requiredglobalspace + 2) * sizeof(prvm_vec_t));
2074 // + 2 is because of an otherwise occurring overrun in RETURN instruction
2075 // when trying to return the last or second-last global
2076 // (RETURN always returns a vector, there is no RETURN_F instruction)
2077 prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numfielddefs + numrequiredfields) * sizeof(ddef_t));
2078 // we need to convert the statements to our memory format
2079 prog->statements = (mstatement_t *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(mstatement_t));
2080 // allocate space for profiling statement usage
2081 prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(*prog->statement_profile));
2082 prog->explicit_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(*prog->statement_profile));
2083 // functions need to be converted to the memory format
2084 prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs_numfunctions);
2086 for (i = 0;i < prog->progs_numfunctions;i++)
2088 prog->functions[i].first_statement = LittleLong(infunctions[i].first_statement);
2089 prog->functions[i].parm_start = LittleLong(infunctions[i].parm_start);
2090 prog->functions[i].s_name = LittleLong(infunctions[i].s_name);
2091 prog->functions[i].s_file = LittleLong(infunctions[i].s_file);
2092 prog->functions[i].numparms = LittleLong(infunctions[i].numparms);
2093 prog->functions[i].locals = LittleLong(infunctions[i].locals);
2094 memcpy(prog->functions[i].parm_size, infunctions[i].parm_size, sizeof(infunctions[i].parm_size));
2095 if(prog->functions[i].first_statement >= prog->numstatements)
2096 prog->error_cmd("PRVM_LoadProgs: out of bounds function statement (function %d) in %s", i, prog->name);
2097 // TODO bounds check parm_start, s_name, s_file, numparms, locals, parm_size
2100 // copy the globaldefs to the new globaldefs list
2101 for (i=0 ; i<prog->numglobaldefs ; i++)
2103 prog->globaldefs[i].type = LittleShort(inglobaldefs[i].type);
2104 prog->globaldefs[i].ofs = LittleShort(inglobaldefs[i].ofs);
2105 prog->globaldefs[i].s_name = LittleLong(inglobaldefs[i].s_name);
2106 // TODO bounds check ofs, s_name
2109 // append the required globals
2110 for (i = 0;i < numrequiredglobals;i++)
2112 prog->globaldefs[prog->numglobaldefs].type = required_global[i].type;
2113 prog->globaldefs[prog->numglobaldefs].ofs = prog->numglobals;
2114 prog->globaldefs[prog->numglobaldefs].s_name = PRVM_SetEngineString(prog, required_global[i].name);
2115 if (prog->globaldefs[prog->numglobaldefs].type == ev_vector)
2116 prog->numglobals += 3;
2119 prog->numglobaldefs++;
2122 // copy the progs fields to the new fields list
2123 for (i = 0;i < prog->numfielddefs;i++)
2125 prog->fielddefs[i].type = LittleShort(infielddefs[i].type);
2126 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
2127 prog->error_cmd("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", prog->name);
2128 prog->fielddefs[i].ofs = LittleShort(infielddefs[i].ofs);
2129 prog->fielddefs[i].s_name = LittleLong(infielddefs[i].s_name);
2130 // TODO bounds check ofs, s_name
2133 // append the required fields
2134 for (i = 0;i < numrequiredfields;i++)
2136 prog->fielddefs[prog->numfielddefs].type = required_field[i].type;
2137 prog->fielddefs[prog->numfielddefs].ofs = prog->entityfields;
2138 prog->fielddefs[prog->numfielddefs].s_name = PRVM_SetEngineString(prog, required_field[i].name);
2139 if (prog->fielddefs[prog->numfielddefs].type == ev_vector)
2140 prog->entityfields += 3;
2142 prog->entityfields++;
2143 prog->numfielddefs++;
2146 // LadyHavoc: TODO: reorder globals to match engine struct
2147 // LadyHavoc: TODO: reorder fields to match engine struct
2148 #define remapglobal(index) (index)
2149 #define remapfield(index) (index)
2152 // FIXME: LadyHavoc: this uses a crude way to identify integer constants, rather than checking for matching globaldefs and checking their type
2153 for (i = 0;i < prog->progs_numglobals;i++)
2155 u.i = LittleLong(inglobals[i]);
2156 // most globals are 0, we only need to deal with the ones that are not
2159 d = u.i & 0xFF800000;
2160 if ((d == 0xFF800000) || (d == 0))
2162 // Looks like an integer (expand to int64)
2163 prog->globals.ip[remapglobal(i)] = u.i;
2167 // Looks like a float (expand to double)
2168 prog->globals.fp[remapglobal(i)] = u.f;
2173 // LadyHavoc: TODO: support 32bit progs statement formats
2174 // copy, remap globals in statements, bounds check
2175 for (i = 0;i < prog->progs_numstatements;i++)
2177 op = (opcode_t)LittleShort(instatements[i].op);
2178 a = (unsigned short)LittleShort(instatements[i].a);
2179 b = (unsigned short)LittleShort(instatements[i].b);
2180 c = (unsigned short)LittleShort(instatements[i].c);
2186 if (a >= prog->progs_numglobals || b + i < 0 || b + i >= prog->progs_numstatements)
2187 prog->error_cmd("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", i, prog->name);
2188 prog->statements[i].op = op;
2189 prog->statements[i].operand[0] = remapglobal(a);
2190 prog->statements[i].operand[1] = -1;
2191 prog->statements[i].operand[2] = -1;
2192 prog->statements[i].jumpabsolute = i + b;
2196 if (a + i < 0 || a + i >= prog->progs_numstatements)
2197 prog->error_cmd("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, prog->name);
2198 prog->statements[i].op = op;
2199 prog->statements[i].operand[0] = -1;
2200 prog->statements[i].operand[1] = -1;
2201 prog->statements[i].operand[2] = -1;
2202 prog->statements[i].jumpabsolute = i + a;
2205 Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", (int)op, i, prog->name);
2207 // global global global
2242 if (a >= prog->progs_numglobals || b >= prog->progs_numglobals || c >= prog->progs_numglobals)
2243 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
2244 prog->statements[i].op = op;
2245 prog->statements[i].operand[0] = remapglobal(a);
2246 prog->statements[i].operand[1] = remapglobal(b);
2247 prog->statements[i].operand[2] = remapglobal(c);
2248 prog->statements[i].jumpabsolute = -1;
2250 // global none global
2256 if (a >= prog->progs_numglobals || c >= prog->progs_numglobals)
2257 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2258 prog->statements[i].op = op;
2259 prog->statements[i].operand[0] = remapglobal(a);
2260 prog->statements[i].operand[1] = -1;
2261 prog->statements[i].operand[2] = remapglobal(c);
2262 prog->statements[i].jumpabsolute = -1;
2278 if (a >= prog->progs_numglobals || b >= prog->progs_numglobals)
2279 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2280 prog->statements[i].op = op;
2281 prog->statements[i].operand[0] = remapglobal(a);
2282 prog->statements[i].operand[1] = remapglobal(b);
2283 prog->statements[i].operand[2] = -1;
2284 prog->statements[i].jumpabsolute = -1;
2288 if ( a < prog->progs_numglobals)
2289 if ( prog->globals.ip[remapglobal(a)] >= 0 )
2290 if ( prog->globals.ip[remapglobal(a)] < prog->progs_numfunctions )
2291 if ( prog->functions[prog->globals.ip[remapglobal(a)]].first_statement == -642 )
2292 ++prog->numexplicitcoveragestatements;
2303 if ( a >= prog->progs_numglobals)
2304 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2305 prog->statements[i].op = op;
2306 prog->statements[i].operand[0] = remapglobal(a);
2307 prog->statements[i].operand[1] = -1;
2308 prog->statements[i].operand[2] = -1;
2309 prog->statements[i].jumpabsolute = -1;
2313 if(prog->numstatements < 1)
2315 prog->error_cmd("PRVM_LoadProgs: empty program in %s", prog->name);
2317 else switch(prog->statements[prog->numstatements - 1].op)
2324 prog->error_cmd("PRVM_LoadProgs: program may fall off the edge (does not end with RETURN, GOTO or DONE) in %s", prog->name);
2328 // we're done with the file now
2330 Mem_Free(dprograms);
2333 // check required functions
2334 for(i=0 ; i < numrequiredfunc ; i++)
2335 if(PRVM_ED_FindFunction(prog, required_func[i]) == 0)
2336 prog->error_cmd("%s: %s not found in %s",prog->name, required_func[i], filename);
2338 PRVM_LoadLNO(prog, filename);
2340 PRVM_Init_Exec(prog);
2342 if(*prvm_language.string)
2343 // in CSQC we really shouldn't be able to change how stuff works... sorry for now
2344 // later idea: include a list of authorized .po file checksums with the csprogs
2346 qboolean deftrans = prog == CLVM_prog;
2347 const char *realfilename = (prog != CLVM_prog ? filename : csqc_progname.string);
2348 if(deftrans) // once we have dotranslate_ strings, ALWAYS use the opt-in method!
2350 for (i=0 ; i<prog->numglobaldefs ; i++)
2353 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2354 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2355 if(name && !strncmp(name, "dotranslate_", 12))
2362 if(!strcmp(prvm_language.string, "dump"))
2364 qfile_t *f = FS_OpenRealFile(va(vabuf, sizeof(vabuf), "%s.pot", realfilename), "w", false);
2365 Con_Printf("Dumping to %s.pot\n", realfilename);
2368 for (i=0 ; i<prog->numglobaldefs ; i++)
2371 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2372 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2373 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2375 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2376 const char *value = PRVM_GetString(prog, val->string);
2379 char buf[MAX_INPUTLINE];
2380 PRVM_PO_UnparseString(buf, value, sizeof(buf));
2381 FS_Printf(f, "msgid \"%s\"\nmsgstr \"\"\n\n", buf);
2390 po_t *po = PRVM_PO_Load(
2391 va(vabuf, sizeof(vabuf), "%s.%s.po", realfilename, prvm_language.string),
2392 va(vabuf2, sizeof(vabuf2), "common.%s.po", prvm_language.string),
2393 prog->progs_mempool);
2396 for (i=0 ; i<prog->numglobaldefs ; i++)
2399 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2400 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2401 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2403 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2404 const char *value = PRVM_GetString(prog, val->string);
2407 value = PRVM_PO_Lookup(po, value);
2409 val->string = PRVM_SetEngineString(prog, value);
2417 for (cvar = prog->console_cmd->cvars->vars; cvar; cvar = cvar->next)
2418 cvar->globaldefindex[prog - prvm_prog_list] = -1;
2420 for (i=0 ; i<prog->numglobaldefs ; i++)
2423 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2424 //Con_Printf("found var %s\n", name);
2426 && !strncmp(name, "autocvar_", 9)
2427 && !(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
2430 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2431 cvar = Cvar_FindVar(prog->console_cmd->cvars, name + 9, prog->console_cmd->cvars_flagsmask);
2432 //Con_Printf("PRVM_LoadProgs: autocvar global %s in %s, processing...\n", name, prog->name);
2439 Con_DPrintf("PRVM_LoadProgs: no cvar for autocvar global %s in %s, creating...\n", name, prog->name);
2440 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2443 if((float)((int)(val->_float)) == val->_float)
2444 dpsnprintf(buf, sizeof(buf), "%i", (int)(val->_float));
2449 for (int precision = 7; precision <= 9; ++precision) {
2450 dpsnprintf(buf, sizeof(buf), "%.*g", precision, f);
2451 if ((float)atof(buf) == f) {
2459 for (i = 0; i < 3; ++i)
2463 for (int precision = 7; precision <= 9; ++precision) {
2464 dpsnprintf(buf, sizeof(buf), "%.*g", precision, f);
2465 if ((float)atof(buf) == f) {
2466 prec[i] = precision;
2471 dpsnprintf(buf, sizeof(buf), "%.*g %.*g %.*g", prec[0], val->vector[0], prec[1], val->vector[1], prec[2], val->vector[2]);
2475 value = PRVM_GetString(prog, val->string);
2478 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2481 cvar = Cvar_Get(prog->console_cmd->cvars, name + 9, value, prog->console_cmd->cvars_flagsmask, NULL);
2482 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2484 val->string = PRVM_SetEngineString(prog, cvar->string);
2485 cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2488 prog->error_cmd("PRVM_LoadProgs: could not create cvar for autocvar global %s in %s", name, prog->name);
2489 cvar->globaldefindex[prog - prvm_prog_list] = i;
2491 else if((cvar->flags & CVAR_PRIVATE) == 0)
2493 // MUST BE SYNCED WITH cvar.c Cvar_Set
2496 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2499 val->_float = cvar->value;
2503 VectorClear(val->vector);
2504 for (j = 0;j < 3;j++)
2506 while (*s && ISWHITESPACE(*s))
2510 val->vector[j] = atof(s);
2511 while (!ISWHITESPACE(*s))
2518 val->string = PRVM_SetEngineString(prog, cvar->string);
2519 cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2522 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2525 cvar->globaldefindex[prog - prvm_prog_list] = i;
2528 Con_Printf("PRVM_LoadProgs: private cvar for autocvar global %s in %s\n", name, prog->name);
2534 prog->loaded = true;
2536 PRVM_UpdateBreakpoints(prog);
2538 // set flags & ddef_ts in prog
2542 PRVM_FindOffsets(prog);
2544 prog->init_cmd(prog);
2547 PRVM_MEM_Alloc(prog);
2549 Con_Printf("%s: program loaded (crc %i, size %iK)\n", prog->name, prog->filecrc, (int)(filesize/1024));
2551 // Inittime is at least the time when this function finished. However,
2552 // later events may bump it.
2553 prog->inittime = host.realtime;
2557 static void PRVM_Fields_f(cmd_state_t *cmd)
2560 int i, j, ednum, used, usedamount;
2562 char tempstring[MAX_INPUTLINE], tempstring2[260];
2572 Con_Print("no progs loaded\n");
2577 if(Cmd_Argc(cmd) != 2)
2579 Con_Print("prvm_fields <program name>\n");
2583 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2586 counts = (int *)Mem_Alloc(tempmempool, prog->numfielddefs * sizeof(int));
2587 for (ednum = 0;ednum < prog->max_edicts;ednum++)
2589 ed = PRVM_EDICT_NUM(ednum);
2590 if (ed->priv.required->free)
2592 for (i = 1;i < prog->numfielddefs;i++)
2594 d = &prog->fielddefs[i];
2595 name = PRVM_GetString(prog, d->s_name);
2596 if (name[strlen(name)-2] == '_')
2597 continue; // skip _x, _y, _z vars
2598 val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
2599 // if the value is still all 0, skip the field
2600 for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
2602 if (val->ivector[j])
2613 for (i = 0;i < prog->numfielddefs;i++)
2615 d = &prog->fielddefs[i];
2616 name = PRVM_GetString(prog, d->s_name);
2617 if (name[strlen(name)-2] == '_')
2618 continue; // skip _x, _y, _z vars
2619 switch(d->type & ~DEF_SAVEGLOBAL)
2622 strlcat(tempstring, "string ", sizeof(tempstring));
2625 strlcat(tempstring, "entity ", sizeof(tempstring));
2628 strlcat(tempstring, "function ", sizeof(tempstring));
2631 strlcat(tempstring, "field ", sizeof(tempstring));
2634 strlcat(tempstring, "void ", sizeof(tempstring));
2637 strlcat(tempstring, "float ", sizeof(tempstring));
2640 strlcat(tempstring, "vector ", sizeof(tempstring));
2643 strlcat(tempstring, "pointer ", sizeof(tempstring));
2646 dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
2647 strlcat(tempstring, tempstring2, sizeof(tempstring));
2650 if (strlen(name) > sizeof(tempstring2)-4)
2652 memcpy (tempstring2, name, sizeof(tempstring2)-4);
2653 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
2654 tempstring2[sizeof(tempstring2)-1] = 0;
2657 strlcat(tempstring, name, sizeof(tempstring));
2658 for (j = (int)strlen(name);j < 25;j++)
2659 strlcat(tempstring, " ", sizeof(tempstring));
2660 dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
2661 strlcat(tempstring, tempstring2, sizeof(tempstring));
2662 strlcat(tempstring, "\n", sizeof(tempstring));
2663 if (strlen(tempstring) >= sizeof(tempstring)/2)
2665 Con_Print(tempstring);
2671 usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
2675 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);
2678 static void PRVM_Globals_f(cmd_state_t *cmd)
2682 const char *wildcard;
2688 Con_Print("no progs loaded\n");
2691 if(Cmd_Argc (cmd) < 2 || Cmd_Argc(cmd) > 3)
2693 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
2697 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2700 if( Cmd_Argc(cmd) == 3)
2701 wildcard = Cmd_Argv(cmd, 2);
2705 Con_Printf("%s :", prog->name);
2707 for (i = 0;i < prog->numglobaldefs;i++)
2710 if( !matchpattern( PRVM_GetString(prog, prog->globaldefs[i].s_name), wildcard, 1) )
2715 Con_Printf("%s\n", PRVM_GetString(prog, prog->globaldefs[i].s_name));
2717 Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->numglobals, numculled, prog->numglobals * 4);
2725 static void PRVM_Global_f(cmd_state_t *cmd)
2729 char valuebuf[MAX_INPUTLINE];
2730 if( Cmd_Argc(cmd) != 3 ) {
2731 Con_Printf( "prvm_global <program name> <global name>\n" );
2735 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2738 global = PRVM_ED_FindGlobal( prog, Cmd_Argv(cmd, 2) );
2740 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(cmd, 2), Cmd_Argv(cmd, 1) );
2742 Con_Printf( "%s: %s\n", Cmd_Argv(cmd, 2), PRVM_ValueString( prog, (etype_t)global->type, PRVM_GLOBALFIELDVALUE(global->ofs), valuebuf, sizeof(valuebuf) ) );
2750 static void PRVM_GlobalSet_f(cmd_state_t *cmd)
2754 if( Cmd_Argc(cmd) != 4 ) {
2755 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2759 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2762 global = PRVM_ED_FindGlobal( prog, Cmd_Argv(cmd, 2) );
2764 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(cmd, 2), Cmd_Argv(cmd, 1) );
2766 PRVM_ED_ParseEpair( prog, NULL, global, Cmd_Argv(cmd, 3), true );
2770 ======================
2771 Break- and Watchpoints
2772 ======================
2776 char break_statement[256];
2777 char watch_global[256];
2779 char watch_field[256];
2782 static debug_data_t debug_data[PRVM_PROG_MAX];
2784 void PRVM_Breakpoint(prvm_prog_t *prog, int stack_index, const char *text)
2787 Con_Printf("PRVM_Breakpoint: %s\n", text);
2788 PRVM_PrintState(prog, stack_index);
2789 if (prvm_breakpointdump.integer)
2790 SV_Savegame_to(prog, va(vabuf, sizeof(vabuf), "breakpoint-%s.dmp", prog->name));
2793 void PRVM_Watchpoint(prvm_prog_t *prog, int stack_index, const char *text, etype_t type, prvm_eval_t *o, prvm_eval_t *n)
2795 size_t sz = sizeof(prvm_vec_t) * ((type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2796 if (memcmp(o, n, sz))
2799 char valuebuf_o[128];
2800 char valuebuf_n[128];
2801 PRVM_UglyValueString(prog, type, o, valuebuf_o, sizeof(valuebuf_o));
2802 PRVM_UglyValueString(prog, type, n, valuebuf_n, sizeof(valuebuf_n));
2803 dpsnprintf(buf, sizeof(buf), "%s: %s -> %s", text, valuebuf_o, valuebuf_n);
2804 PRVM_Breakpoint(prog, stack_index, buf);
2809 static void PRVM_UpdateBreakpoints(prvm_prog_t *prog)
2811 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2814 if (debug->break_statement[0])
2816 if (debug->break_statement[0] >= '0' && debug->break_statement[0] <= '9')
2818 prog->break_statement = atoi(debug->break_statement);
2819 prog->break_stack_index = 0;
2824 func = PRVM_ED_FindFunction (prog, debug->break_statement);
2827 Con_Printf("%s progs: no function or statement named %s to break on!\n", prog->name, debug->break_statement);
2828 prog->break_statement = -1;
2832 prog->break_statement = func->first_statement;
2833 prog->break_stack_index = 1;
2836 if (prog->break_statement >= -1)
2837 Con_Printf("%s progs: breakpoint is at statement %d\n", prog->name, prog->break_statement);
2840 prog->break_statement = -1;
2842 if (debug->watch_global[0])
2844 ddef_t *global = PRVM_ED_FindGlobal( prog, debug->watch_global );
2847 Con_Printf( "%s progs: no global named '%s' to watch!\n", prog->name, debug->watch_global );
2848 prog->watch_global_type = ev_void;
2852 size_t sz = sizeof(prvm_vec_t) * ((global->type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2853 prog->watch_global = global->ofs;
2854 prog->watch_global_type = (etype_t)global->type;
2855 memcpy(&prog->watch_global_value, PRVM_GLOBALFIELDVALUE(prog->watch_global), sz);
2857 if (prog->watch_global_type != ev_void)
2858 Con_Printf("%s progs: global watchpoint is at global index %d\n", prog->name, prog->watch_global);
2861 prog->watch_global_type = ev_void;
2863 if (debug->watch_field[0])
2865 ddef_t *field = PRVM_ED_FindField( prog, debug->watch_field );
2868 Con_Printf( "%s progs: no field named '%s' to watch!\n", prog->name, debug->watch_field );
2869 prog->watch_field_type = ev_void;
2873 size_t sz = sizeof(prvm_vec_t) * ((field->type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2874 prog->watch_edict = debug->watch_edict;
2875 prog->watch_field = field->ofs;
2876 prog->watch_field_type = (etype_t)field->type;
2877 if (prog->watch_edict < prog->num_edicts)
2878 memcpy(&prog->watch_edictfield_value, PRVM_EDICTFIELDVALUE(PRVM_EDICT_NUM(prog->watch_edict), prog->watch_field), sz);
2880 memset(&prog->watch_edictfield_value, 0, sz);
2882 if (prog->watch_edict != ev_void)
2883 Con_Printf("%s progs: edict field watchpoint is at edict %d field index %d\n", prog->name, prog->watch_edict, prog->watch_field);
2886 prog->watch_field_type = ev_void;
2889 static void PRVM_Breakpoint_f(cmd_state_t *cmd)
2893 if( Cmd_Argc(cmd) == 2 ) {
2894 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2897 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2898 debug->break_statement[0] = 0;
2900 PRVM_UpdateBreakpoints(prog);
2903 if( Cmd_Argc(cmd) != 3 ) {
2904 Con_Printf( "prvm_breakpoint <program name> <function name | statement>\n" );
2908 if (!(prog = PRVM_ProgFromString(Cmd_Argv(cmd, 1))))
2912 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2913 strlcpy(debug->break_statement, Cmd_Argv(cmd, 2), sizeof(debug->break_statement));
2915 PRVM_UpdateBreakpoints(prog);
2918 static void PRVM_GlobalWatchpoint_f(cmd_state_t *cmd)
2922 if( Cmd_Argc(cmd) == 2 ) {
2923 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2926 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2927 debug->watch_global[0] = 0;
2929 PRVM_UpdateBreakpoints(prog);
2932 if( Cmd_Argc(cmd) != 3 ) {
2933 Con_Printf( "prvm_globalwatchpoint <program name> <global name>\n" );
2937 if (!(prog = PRVM_ProgFromString(Cmd_Argv(cmd, 1))))
2941 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2942 strlcpy(debug->watch_global, Cmd_Argv(cmd, 2), sizeof(debug->watch_global));
2944 PRVM_UpdateBreakpoints(prog);
2947 static void PRVM_EdictWatchpoint_f(cmd_state_t *cmd)
2951 if( Cmd_Argc(cmd) == 2 ) {
2952 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2955 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2956 debug->watch_field[0] = 0;
2958 PRVM_UpdateBreakpoints(prog);
2961 if( Cmd_Argc(cmd) != 4 ) {
2962 Con_Printf( "prvm_edictwatchpoint <program name> <edict number> <field name>\n" );
2966 if (!(prog = PRVM_ProgFromString(Cmd_Argv(cmd, 1))))
2970 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2971 debug->watch_edict = atoi(Cmd_Argv(cmd, 2));
2972 strlcpy(debug->watch_field, Cmd_Argv(cmd, 3), sizeof(debug->watch_field));
2974 PRVM_UpdateBreakpoints(prog);
2982 void PRVM_Init (void)
2984 Cmd_AddCommand(CMD_SHARED, "prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2985 Cmd_AddCommand(CMD_SHARED, "prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2986 Cmd_AddCommand(CMD_SHARED, "prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2987 Cmd_AddCommand(CMD_SHARED, "prvm_profile", PRVM_Profile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu)");
2988 Cmd_AddCommand(CMD_SHARED, "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");
2989 Cmd_AddCommand(CMD_SHARED, "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)");
2990 Cmd_AddCommand(CMD_SHARED, "prvm_fields", PRVM_Fields_f, "prints usage statistics on properties (how many entities have non-zero values) in the selected VM (server, client, menu)");
2991 Cmd_AddCommand(CMD_SHARED, "prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2992 Cmd_AddCommand(CMD_SHARED, "prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2993 Cmd_AddCommand(CMD_SHARED, "prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2994 Cmd_AddCommand(CMD_SHARED, "prvm_edictset", PRVM_ED_EdictSet_f, "changes value of a specified property of a specified entity in the selected VM (server, client, menu)");
2995 Cmd_AddCommand(CMD_SHARED, "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");
2996 Cmd_AddCommand(CMD_SHARED, "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");
2997 Cmd_AddCommand(CMD_SHARED, "prvm_printfunction", PRVM_PrintFunction_f, "prints a disassembly (QuakeC instructions) of the specified function in the selected VM (server, client, menu)");
2998 Cmd_AddCommand(CMD_SHARED, "cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2999 Cmd_AddCommand(CMD_SHARED, "menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
3000 Cmd_AddCommand(CMD_SHARED, "sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
3001 Cmd_AddCommand(CMD_SHARED, "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");
3002 Cmd_AddCommand(CMD_SHARED, "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");
3003 Cmd_AddCommand(CMD_SHARED, "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");
3005 Cvar_RegisterVariable (&prvm_language);
3006 Cvar_RegisterVariable (&prvm_traceqc);
3007 Cvar_RegisterVariable (&prvm_statementprofiling);
3008 Cvar_RegisterVariable (&prvm_timeprofiling);
3009 Cvar_RegisterVariable (&prvm_coverage);
3010 Cvar_RegisterVariable (&prvm_backtraceforwarnings);
3011 Cvar_RegisterVariable (&prvm_leaktest);
3012 Cvar_RegisterVariable (&prvm_leaktest_follow_targetname);
3013 Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames);
3014 Cvar_RegisterVariable (&prvm_errordump);
3015 Cvar_RegisterVariable (&prvm_breakpointdump);
3016 Cvar_RegisterVariable (&prvm_reuseedicts_startuptime);
3017 Cvar_RegisterVariable (&prvm_reuseedicts_neverinsameframe);
3018 Cvar_RegisterVariable (&prvm_garbagecollection_enable);
3019 Cvar_RegisterVariable (&prvm_garbagecollection_notify);
3020 Cvar_RegisterVariable (&prvm_garbagecollection_scan_limit);
3021 Cvar_RegisterVariable (&prvm_garbagecollection_strings);
3022 Cvar_RegisterVariable (&prvm_stringdebug);
3024 // COMMANDLINEOPTION: PRVM: -norunaway disables the runaway loop check (it might be impossible to exit DarkPlaces if used!)
3025 prvm_runawaycheck = !COM_CheckParm("-norunaway");
3035 void PRVM_Prog_Init(prvm_prog_t *prog, cmd_state_t *cmd)
3037 PRVM_Prog_Reset(prog);
3038 prog->leaktest_active = prvm_leaktest.integer != 0;
3039 prog->console_cmd = cmd;
3042 // LadyHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
3043 unsigned int PRVM_EDICT_NUM_ERROR(prvm_prog_t *prog, unsigned int n, const char *filename, int fileline)
3045 prog->error_cmd("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", prog->name, n, filename, fileline);
3049 #define PRVM_KNOWNSTRINGBASE 0x40000000
3051 const char *PRVM_GetString(prvm_prog_t *prog, int num)
3056 if (prvm_stringdebug.integer)
3057 VM_Warning(prog, "PRVM_GetString: Invalid string offset (%i < 0)\n", num);
3060 else if (num < prog->stringssize)
3062 // constant string from progs.dat
3063 return prog->strings + num;
3065 else if (num <= prog->stringssize + prog->tempstringsbuf.maxsize)
3067 // tempstring returned by engine to QC (becomes invalid after returning to engine)
3068 num -= prog->stringssize;
3069 if (num < prog->tempstringsbuf.cursize)
3070 return (char *)prog->tempstringsbuf.data + num;
3073 if (prvm_stringdebug.integer)
3074 VM_Warning(prog, "PRVM_GetString: Invalid temp-string offset (%i >= %i prog->tempstringsbuf.cursize)\n", num, prog->tempstringsbuf.cursize);
3078 else if (num & PRVM_KNOWNSTRINGBASE)
3081 num = num - PRVM_KNOWNSTRINGBASE;
3082 if (num >= 0 && num < prog->numknownstrings)
3084 if (!prog->knownstrings[num])
3086 if (prvm_stringdebug.integer)
3087 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
3090 // refresh the garbage collection on the string - this guards
3091 // against a certain sort of repeated migration to earlier
3092 // points in the scan that could otherwise result in the string
3093 // being freed for being unused
3094 prog->knownstrings_flags[num] = (prog->knownstrings_flags[num] & ~KNOWNSTRINGFLAG_GCPRUNE) | KNOWNSTRINGFLAG_GCMARK;
3095 return prog->knownstrings[num];
3099 if (prvm_stringdebug.integer)
3100 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
3106 // invalid string offset
3107 if (prvm_stringdebug.integer)
3108 VM_Warning(prog, "PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
3113 const char *PRVM_ChangeEngineString(prvm_prog_t *prog, int i, const char *s)
3116 i = i - PRVM_KNOWNSTRINGBASE;
3117 if (i < 0 || i >= prog->numknownstrings)
3118 prog->error_cmd("PRVM_ChangeEngineString: string index %i is out of bounds", i);
3119 else if ((prog->knownstrings_flags[i] & KNOWNSTRINGFLAG_ENGINE) == 0)
3120 prog->error_cmd("PRVM_ChangeEngineString: string index %i is not an engine string", i);
3121 old = prog->knownstrings[i];
3122 prog->knownstrings[i] = s;
3126 static void PRVM_NewKnownString(prvm_prog_t *prog, int i, int flags, const char *s)
3128 if (i >= prog->numknownstrings)
3130 if (i >= prog->maxknownstrings)
3132 const char **oldstrings = prog->knownstrings;
3133 const unsigned char *oldstrings_flags = prog->knownstrings_flags;
3134 const char **oldstrings_origin = prog->knownstrings_origin;
3135 prog->maxknownstrings += 128;
3136 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3137 prog->knownstrings_flags = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
3138 if (prog->leaktest_active)
3139 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3140 if (prog->numknownstrings)
3142 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
3143 memcpy((char **)prog->knownstrings_flags, oldstrings_flags, prog->numknownstrings * sizeof(unsigned char));
3144 if (prog->leaktest_active)
3145 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
3148 prog->numknownstrings++;
3150 prog->firstfreeknownstring = i + 1;
3151 prog->knownstrings[i] = s;
3152 // it's in use right now, spare it until the next gc pass - that said, it is not freeable so this is probably moot
3153 prog->knownstrings_flags[i] = flags;
3154 if (prog->leaktest_active)
3155 prog->knownstrings_origin[i] = NULL;
3158 int PRVM_SetEngineString(prvm_prog_t *prog, const char *s)
3163 if (s >= prog->strings && s <= prog->strings + prog->stringssize)
3164 prog->error_cmd("PRVM_SetEngineString: s in prog->strings area");
3165 // if it's in the tempstrings area, use a reserved range
3166 // (otherwise we'd get millions of useless string offsets cluttering the database)
3167 if (s >= (char *)prog->tempstringsbuf.data && s < (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.maxsize)
3168 return prog->stringssize + (s - (char *)prog->tempstringsbuf.data);
3169 // see if it's a known string address
3170 for (i = 0;i < prog->numknownstrings;i++)
3171 if (prog->knownstrings[i] == s)
3172 return PRVM_KNOWNSTRINGBASE + i;
3173 // new unknown engine string
3174 if (developer_insane.integer)
3175 Con_DPrintf("new engine string %p = \"%s\"\n", (void *)s, s);
3176 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3177 if (!prog->knownstrings[i])
3179 PRVM_NewKnownString(prog, i, KNOWNSTRINGFLAG_GCMARK | KNOWNSTRINGFLAG_ENGINE, s);
3180 return PRVM_KNOWNSTRINGBASE + i;
3183 // temp string handling
3185 // all tempstrings go into this buffer consecutively, and it is reset
3186 // whenever PRVM_ExecuteProgram returns to the engine
3187 // (technically each PRVM_ExecuteProgram call saves the cursize value and
3188 // restores it on return, so multiple recursive calls can share the same
3190 // the buffer size is automatically grown as needed
3192 int PRVM_SetTempString(prvm_prog_t *prog, const char *s)
3198 size = (int)strlen(s) + 1;
3199 if (developer_insane.integer)
3200 Con_DPrintf("PRVM_SetTempString: cursize %i, size %i\n", prog->tempstringsbuf.cursize, size);
3201 if (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
3203 sizebuf_t old = prog->tempstringsbuf;
3204 if (prog->tempstringsbuf.cursize + size >= 1<<28)
3205 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);
3206 prog->tempstringsbuf.maxsize = max(prog->tempstringsbuf.maxsize, 65536);
3207 while (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
3208 prog->tempstringsbuf.maxsize *= 2;
3209 if (prog->tempstringsbuf.maxsize != old.maxsize || prog->tempstringsbuf.data == NULL)
3211 Con_DPrintf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, prog->tempstringsbuf.maxsize/1024);
3212 prog->tempstringsbuf.data = (unsigned char *) Mem_Alloc(prog->progs_mempool, prog->tempstringsbuf.maxsize);
3216 memcpy(prog->tempstringsbuf.data, old.data, old.cursize);
3221 t = (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.cursize;
3223 prog->tempstringsbuf.cursize += size;
3224 return PRVM_SetEngineString(prog, t);
3227 int PRVM_AllocString(prvm_prog_t *prog, size_t bufferlength, char **pointer)
3237 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3238 if (!prog->knownstrings[i])
3240 s = (char *)PRVM_Alloc(bufferlength);
3241 PRVM_NewKnownString(prog, i, KNOWNSTRINGFLAG_GCMARK, s);
3242 if(prog->leaktest_active)
3243 prog->knownstrings_origin[i] = PRVM_AllocationOrigin(prog);
3245 *pointer = (char *)(prog->knownstrings[i]);
3246 return PRVM_KNOWNSTRINGBASE + i;
3249 void PRVM_FreeString(prvm_prog_t *prog, int num)
3252 prog->error_cmd("PRVM_FreeString: attempt to free a NULL string");
3253 else if (num >= 0 && num < prog->stringssize)
3254 prog->error_cmd("PRVM_FreeString: attempt to free a constant string");
3255 else if (num >= PRVM_KNOWNSTRINGBASE && num < PRVM_KNOWNSTRINGBASE + prog->numknownstrings)
3257 num = num - PRVM_KNOWNSTRINGBASE;
3258 if (!prog->knownstrings[num])
3259 prog->error_cmd("PRVM_FreeString: attempt to free a non-existent or already freed string");
3260 if (!prog->knownstrings_flags[num])
3261 prog->error_cmd("PRVM_FreeString: attempt to free a string owned by the engine");
3262 PRVM_Free((char *)prog->knownstrings[num]);
3263 if(prog->leaktest_active)
3264 if(prog->knownstrings_origin[num])
3265 PRVM_Free((char *)prog->knownstrings_origin[num]);
3266 prog->knownstrings[num] = NULL;
3267 prog->knownstrings_flags[num] = 0;
3268 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
3271 prog->error_cmd("PRVM_FreeString: invalid string offset %i", num);
3274 static qboolean PRVM_IsStringReferenced(prvm_prog_t *prog, string_t string)
3278 for (i = 0;i < prog->numglobaldefs;i++)
3280 ddef_t *d = &prog->globaldefs[i];
3281 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3283 if(string == PRVM_GLOBALFIELDSTRING(d->ofs))
3287 for(j = 0; j < prog->num_edicts; ++j)
3289 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3290 if (ed->priv.required->free)
3292 for (i=0; i<prog->numfielddefs; ++i)
3294 ddef_t *d = &prog->fielddefs[i];
3295 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3297 if(string == PRVM_EDICTFIELDSTRING(ed, d->ofs))
3305 static qboolean PRVM_IsEdictRelevant(prvm_prog_t *prog, prvm_edict_t *edict)
3309 if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
3310 return true; // world or clients
3311 if (edict->priv.required->freetime <= prog->inittime)
3312 return true; // created during startup
3313 if (prog == SVVM_prog)
3315 if(PRVM_serveredictfloat(edict, solid)) // can block other stuff, or is a trigger?
3317 if(PRVM_serveredictfloat(edict, modelindex)) // visible ent?
3319 if(PRVM_serveredictfloat(edict, effects)) // particle effect?
3321 if(PRVM_serveredictfunction(edict, think)) // has a think function?
3322 if(PRVM_serveredictfloat(edict, nextthink) > 0) // that actually will eventually run?
3324 if(PRVM_serveredictfloat(edict, takedamage))
3326 if(*prvm_leaktest_ignore_classnames.string)
3328 if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_serveredictstring(edict, classname)))))
3332 else if (prog == CLVM_prog)
3334 // TODO someone add more stuff here
3335 if(PRVM_clientedictfloat(edict, entnum)) // csqc networked
3337 if(PRVM_clientedictfloat(edict, modelindex)) // visible ent?
3339 if(PRVM_clientedictfloat(edict, effects)) // particle effect?
3341 if(PRVM_clientedictfunction(edict, think)) // has a think function?
3342 if(PRVM_clientedictfloat(edict, nextthink) > 0) // that actually will eventually run?
3344 if(*prvm_leaktest_ignore_classnames.string)
3346 if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_clientedictstring(edict, classname)))))
3352 // menu prog does not have classnames
3357 static qboolean PRVM_IsEdictReferenced(prvm_prog_t *prog, prvm_edict_t *edict, int mark)
3360 int edictnum = PRVM_NUM_FOR_EDICT(edict);
3361 const char *targetname = NULL;
3363 if (prog == SVVM_prog && prvm_leaktest_follow_targetname.integer)
3364 targetname = PRVM_GetString(prog, PRVM_serveredictstring(edict, targetname));
3367 if(!*targetname) // ""
3370 for(j = 0; j < prog->num_edicts; ++j)
3372 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3373 if (ed->priv.required->mark < mark)
3379 const char *target = PRVM_GetString(prog, PRVM_serveredictstring(ed, target));
3381 if(!strcmp(target, targetname))
3384 for (i=0; i<prog->numfielddefs; ++i)
3386 ddef_t *d = &prog->fielddefs[i];
3387 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3389 if(edictnum == PRVM_EDICTFIELDEDICT(ed, d->ofs))
3397 static void PRVM_MarkReferencedEdicts(prvm_prog_t *prog)
3403 // Stage 1: world, all entities that are relevant, and all entities that are referenced by globals.
3405 for(j = 0; j < prog->num_edicts; ++j)
3407 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3408 if(ed->priv.required->free)
3410 ed->priv.required->mark = PRVM_IsEdictRelevant(prog, ed) ? stage : 0;
3412 for (i = 0;i < prog->numglobaldefs;i++)
3414 ddef_t *d = &prog->globaldefs[i];
3416 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3418 j = PRVM_GLOBALFIELDEDICT(d->ofs);
3419 if (i < 0 || j >= prog->max_edicts) {
3420 Con_Printf("Invalid entity reference from global %s.\n", PRVM_GetString(prog, d->s_name));
3423 ed = PRVM_EDICT_NUM(j);;
3424 ed->priv.required->mark = stage;
3427 // Future stages: all entities that are referenced by an entity of the previous stage.
3431 for(j = 0; j < prog->num_edicts; ++j)
3433 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3434 if(ed->priv.required->free)
3436 if(ed->priv.required->mark)
3438 if(PRVM_IsEdictReferenced(prog, ed, stage))
3440 ed->priv.required->mark = stage + 1;
3447 Con_DPrintf("leak check used %d stages to find all references\n", stage);
3450 void PRVM_LeakTest(prvm_prog_t *prog)
3453 qboolean leaked = false;
3455 if(!prog->leaktest_active)
3459 for (i = 0; i < prog->numknownstrings; ++i)
3461 if(prog->knownstrings[i])
3462 if(prog->knownstrings_flags[i])
3463 if(prog->knownstrings_origin[i])
3464 if(!PRVM_IsStringReferenced(prog, PRVM_KNOWNSTRINGBASE + i))
3466 Con_Printf("Unreferenced string found!\n Value: %s\n Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
3472 PRVM_MarkReferencedEdicts(prog);
3473 for(j = 0; j < prog->num_edicts; ++j)
3475 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3476 if(ed->priv.required->free)
3478 if(!ed->priv.required->mark)
3479 if(ed->priv.required->allocation_origin)
3481 Con_Printf("Unreferenced edict found!\n Allocated at: %s\n", ed->priv.required->allocation_origin);
3482 PRVM_ED_Print(prog, ed, NULL);
3487 ed->priv.required->mark = 0; // clear marks again when done
3490 for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
3492 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
3494 if(stringbuffer->origin)
3496 Con_Printf("Open string buffer handle found!\n Allocated at: %s\n", stringbuffer->origin);
3501 for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
3503 if(prog->openfiles[i])
3504 if(prog->openfiles_origin[i])
3506 Con_Printf("Open file handle found!\n Allocated at: %s\n", prog->openfiles_origin[i]);
3511 for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
3513 if(prog->opensearches[i])
3514 if(prog->opensearches_origin[i])
3516 Con_Printf("Open search handle found!\n Allocated at: %s\n", prog->opensearches_origin[i]);
3522 Con_Printf("Congratulations. No leaks found.\n");
3525 void PRVM_GarbageCollection(prvm_prog_t *prog)
3527 int limit = prvm_garbagecollection_scan_limit.integer;
3528 prvm_prog_garbagecollection_state_t *gc = &prog->gc;
3529 if (!prvm_garbagecollection_enable.integer)
3532 // we like to limit how much scanning we do so it doesn't put a significant
3533 // burden on the cpu, so each of these are not complete scans, we also like
3534 // to have consistent cpu usage so we do a bit of work on each category of
3535 // leaked object every frame
3541 case PRVM_GC_GLOBALS_MARK:
3542 for (; gc->globals_mark_progress < prog->numglobaldefs && (limit--) > 0; gc->globals_mark_progress++)
3544 ddef_t *d = &prog->globaldefs[gc->globals_mark_progress];
3549 prvm_int_t s = prog->globals.ip[d->ofs];
3550 if (s & PRVM_KNOWNSTRINGBASE)
3552 prvm_int_t num = s - PRVM_KNOWNSTRINGBASE;
3553 if (!prog->knownstrings[num])
3556 Con_DPrintf("PRVM_GarbageCollection: Found bogus strzone reference in global %i (global name: \"%s\"), erasing reference", d->ofs, PRVM_GetString(prog, d->s_name));
3557 prog->globals.ip[d->ofs] = 0;
3560 prog->knownstrings_flags[num] = (prog->knownstrings_flags[num] | KNOWNSTRINGFLAG_GCMARK) & ~KNOWNSTRINGFLAG_GCPRUNE;
3568 if (gc->globals_mark_progress >= prog->numglobaldefs)
3571 case PRVM_GC_FIELDS_MARK:
3572 for (; gc->fields_mark_progress < prog->numfielddefs && limit > 0;)
3574 ddef_t *d = &prog->fielddefs[gc->fields_mark_progress];
3578 //for (gc-> entityindex = 0; entityindex < prog->num_edicts; entityindex++)
3579 for (;gc->fields_mark_progress_entity < prog->num_edicts && (limit--) > 0;gc->fields_mark_progress_entity++)
3581 int entityindex = gc->fields_mark_progress_entity;
3582 prvm_int_t s = prog->edictsfields.ip[entityindex * prog->entityfields + d->ofs];
3583 if (s & PRVM_KNOWNSTRINGBASE)
3585 prvm_int_t num = s - PRVM_KNOWNSTRINGBASE;
3586 if (!prog->knownstrings[num])
3589 Con_DPrintf("PRVM_GarbageCollection: Found bogus strzone reference in edict %i field %i (field name: \"%s\"), erasing reference", entityindex, d->ofs, PRVM_GetString(prog, d->s_name));
3590 prog->edictsfields.ip[entityindex * prog->entityfields + d->ofs] = 0;
3593 prog->knownstrings_flags[num] = (prog->knownstrings_flags[num] | KNOWNSTRINGFLAG_GCMARK) & ~KNOWNSTRINGFLAG_GCPRUNE;
3596 if (gc->fields_mark_progress_entity >= prog->num_edicts)
3598 gc->fields_mark_progress_entity = 0;
3599 gc->fields_mark_progress++;
3603 gc->fields_mark_progress_entity = 0;
3604 gc->fields_mark_progress++;
3608 if (gc->fields_mark_progress >= prog->numfielddefs)
3611 case PRVM_GC_KNOWNSTRINGS_SWEEP:
3612 // free any strzone'd strings that are not marked
3613 if (!prvm_garbagecollection_strings.integer)
3618 for (;gc->knownstrings_sweep_progress < prog->numknownstrings && (limit--) > 0;gc->knownstrings_sweep_progress++)
3620 int num = gc->knownstrings_sweep_progress;
3621 if (prog->knownstrings[num] && (prog->knownstrings_flags[num] & (KNOWNSTRINGFLAG_GCMARK | KNOWNSTRINGFLAG_ENGINE)) == 0)
3623 if (prog->knownstrings_flags[num] & KNOWNSTRINGFLAG_GCPRUNE)
3625 // string has been marked for pruning two passes in a row
3626 if (prvm_garbagecollection_notify.integer)
3627 Con_DPrintf("prvm_garbagecollection_notify: %s: freeing unreferenced string %i: \"%s\"\n", prog->name, num, prog->knownstrings[num]);
3628 Mem_Free((char *)prog->knownstrings[num]);
3629 prog->knownstrings[num] = NULL;
3630 prog->knownstrings_flags[num] = 0;
3631 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
3635 // mark it for pruning next pass
3636 prog->knownstrings_flags[num] |= KNOWNSTRINGFLAG_GCPRUNE;
3640 if (gc->knownstrings_sweep_progress >= prog->numknownstrings)
3645 memset(gc, 0, sizeof(*gc));