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;
1361 void PRVM_ED_CallPrespawnFunction(prvm_prog_t *prog, prvm_edict_t *ent)
1363 if (PRVM_serverfunction(SV_OnEntityPreSpawnFunction))
1366 PRVM_serverglobalfloat(time) = sv.time;
1367 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1368 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityPreSpawnFunction), "QC function SV_OnEntityPreSpawnFunction is missing");
1372 qboolean PRVM_ED_CallSpawnFunction(prvm_prog_t *prog, prvm_edict_t *ent, const char *data, const char *start)
1374 const char *funcname;
1376 prvm_eval_t *fulldata = NULL;
1380 // immediately call spawn function, but only if there is a self global and a classname
1382 if (!ent->priv.required->free)
1384 if (!PRVM_alledictstring(ent, classname))
1386 Con_Print("No classname for:\n");
1387 PRVM_ED_Print(prog, ent, NULL);
1388 PRVM_ED_Free (prog, ent);
1392 * This is required for FTE compatibility (FreeCS).
1393 * It copies the key/value pairs themselves into a
1394 * global for QC to parse on its own.
1396 else if (data && start)
1398 if((fulldata = PRVM_ED_FindGlobalEval(prog, "__fullspawndata")))
1402 fulldata->string = PRVM_AllocString(prog, data - start + 1, &spawndata);
1403 for(in = start; in < data; )
1407 *spawndata++ = '\t';
1415 // look for the spawn function
1416 funcname = PRVM_GetString(prog, PRVM_alledictstring(ent, classname));
1417 func = PRVM_ED_FindFunction (prog, va(vabuf, sizeof(vabuf), "spawnfunc_%s", funcname));
1419 if(!PRVM_allglobalfloat(require_spawnfunc_prefix))
1420 func = PRVM_ED_FindFunction (prog, funcname);
1424 // check for OnEntityNoSpawnFunction
1425 if (PRVM_serverfunction(SV_OnEntityNoSpawnFunction))
1428 PRVM_serverglobalfloat(time) = sv.time;
1429 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1430 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityNoSpawnFunction), "QC function SV_OnEntityNoSpawnFunction is missing");
1435 Con_DPrint("No spawn function for:\n");
1436 if (developer.integer > 0) // don't confuse non-developers with errors
1437 PRVM_ED_Print(prog, ent, NULL);
1439 PRVM_ED_Free (prog, ent);
1440 return false; // not included in "inhibited" count
1446 PRVM_serverglobalfloat(time) = sv.time;
1447 PRVM_allglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1448 prog->ExecuteProgram(prog, func - prog->functions, "");
1452 PRVM_ED_Free(prog, ent);
1456 void PRVM_ED_CallPostspawnFunction (prvm_prog_t *prog, prvm_edict_t *ent)
1458 if(!ent->priv.required->free)
1459 if (PRVM_serverfunction(SV_OnEntityPostSpawnFunction))
1462 PRVM_serverglobalfloat(time) = sv.time;
1463 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1464 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityPostSpawnFunction), "QC function SV_OnEntityPostSpawnFunction is missing");
1470 PRVM_ED_LoadFromFile
1472 The entities are directly placed in the array, rather than allocated with
1473 PRVM_ED_Alloc, because otherwise an error loading the map would have entity
1474 number references out of order.
1476 Creates a server's entity / program execution context by
1477 parsing textual entity definitions out of an ent file.
1479 Used for both fresh maps and savegame loads. A fresh map would also need
1480 to call PRVM_ED_CallSpawnFunctions () to let the objects initialize themselves.
1483 void PRVM_ED_LoadFromFile (prvm_prog_t *prog, const char *data)
1487 int parsed, inhibited, spawned, died;
1494 prvm_reuseedicts_always_allow = host.realtime;
1501 // parse the opening brace
1502 if (!COM_ParseToken_Simple(&data, false, false, true))
1504 if (com_token[0] != '{')
1505 prog->error_cmd("PRVM_ED_LoadFromFile: %s: found %s when expecting {", prog->name, com_token);
1507 // CHANGED: this is not conform to PR_LoadFromFile
1508 if(prog->loadintoworld)
1510 prog->loadintoworld = false;
1511 ent = PRVM_EDICT_NUM(0);
1514 ent = PRVM_ED_Alloc(prog);
1517 if (ent != prog->edicts) // hack
1518 memset (ent->fields.fp, 0, prog->entityfields * sizeof(prvm_vec_t));
1520 data = PRVM_ED_ParseEdict (prog, data, ent);
1523 // remove the entity ?
1524 if(!prog->load_edict(prog, ent))
1526 PRVM_ED_Free(prog, ent);
1531 PRVM_ED_CallPrespawnFunction(prog, ent);
1533 if(ent->priv.required->free)
1539 if(!PRVM_ED_CallSpawnFunction(prog, ent, data, start))
1542 PRVM_ED_CallPostspawnFunction(prog, ent);
1545 if (ent->priv.required->free)
1549 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);
1551 prvm_reuseedicts_always_allow = 0;
1554 static void PRVM_FindOffsets(prvm_prog_t *prog)
1556 // field and global searches use -1 for NULL
1557 memset(&prog->fieldoffsets, -1, sizeof(prog->fieldoffsets));
1558 memset(&prog->globaloffsets, -1, sizeof(prog->globaloffsets));
1559 // function searches use 0 for NULL
1560 memset(&prog->funcoffsets, 0, sizeof(prog->funcoffsets));
1561 #define PRVM_DECLARE_serverglobalfloat(x)
1562 #define PRVM_DECLARE_serverglobalvector(x)
1563 #define PRVM_DECLARE_serverglobalstring(x)
1564 #define PRVM_DECLARE_serverglobaledict(x)
1565 #define PRVM_DECLARE_serverglobalfunction(x)
1566 #define PRVM_DECLARE_clientglobalfloat(x)
1567 #define PRVM_DECLARE_clientglobalvector(x)
1568 #define PRVM_DECLARE_clientglobalstring(x)
1569 #define PRVM_DECLARE_clientglobaledict(x)
1570 #define PRVM_DECLARE_clientglobalfunction(x)
1571 #define PRVM_DECLARE_menuglobalfloat(x)
1572 #define PRVM_DECLARE_menuglobalvector(x)
1573 #define PRVM_DECLARE_menuglobalstring(x)
1574 #define PRVM_DECLARE_menuglobaledict(x)
1575 #define PRVM_DECLARE_menuglobalfunction(x)
1576 #define PRVM_DECLARE_serverfieldfloat(x)
1577 #define PRVM_DECLARE_serverfieldvector(x)
1578 #define PRVM_DECLARE_serverfieldstring(x)
1579 #define PRVM_DECLARE_serverfieldedict(x)
1580 #define PRVM_DECLARE_serverfieldfunction(x)
1581 #define PRVM_DECLARE_clientfieldfloat(x)
1582 #define PRVM_DECLARE_clientfieldvector(x)
1583 #define PRVM_DECLARE_clientfieldstring(x)
1584 #define PRVM_DECLARE_clientfieldedict(x)
1585 #define PRVM_DECLARE_clientfieldfunction(x)
1586 #define PRVM_DECLARE_menufieldfloat(x)
1587 #define PRVM_DECLARE_menufieldvector(x)
1588 #define PRVM_DECLARE_menufieldstring(x)
1589 #define PRVM_DECLARE_menufieldedict(x)
1590 #define PRVM_DECLARE_menufieldfunction(x)
1591 #define PRVM_DECLARE_serverfunction(x)
1592 #define PRVM_DECLARE_clientfunction(x)
1593 #define PRVM_DECLARE_menufunction(x)
1594 #define PRVM_DECLARE_field(x) prog->fieldoffsets.x = PRVM_ED_FindFieldOffset(prog, #x);
1595 #define PRVM_DECLARE_global(x) prog->globaloffsets.x = PRVM_ED_FindGlobalOffset(prog, #x);
1596 #define PRVM_DECLARE_function(x) prog->funcoffsets.x = PRVM_ED_FindFunctionOffset(prog, #x);
1597 #include "prvm_offsets.h"
1598 #undef PRVM_DECLARE_serverglobalfloat
1599 #undef PRVM_DECLARE_serverglobalvector
1600 #undef PRVM_DECLARE_serverglobalstring
1601 #undef PRVM_DECLARE_serverglobaledict
1602 #undef PRVM_DECLARE_serverglobalfunction
1603 #undef PRVM_DECLARE_clientglobalfloat
1604 #undef PRVM_DECLARE_clientglobalvector
1605 #undef PRVM_DECLARE_clientglobalstring
1606 #undef PRVM_DECLARE_clientglobaledict
1607 #undef PRVM_DECLARE_clientglobalfunction
1608 #undef PRVM_DECLARE_menuglobalfloat
1609 #undef PRVM_DECLARE_menuglobalvector
1610 #undef PRVM_DECLARE_menuglobalstring
1611 #undef PRVM_DECLARE_menuglobaledict
1612 #undef PRVM_DECLARE_menuglobalfunction
1613 #undef PRVM_DECLARE_serverfieldfloat
1614 #undef PRVM_DECLARE_serverfieldvector
1615 #undef PRVM_DECLARE_serverfieldstring
1616 #undef PRVM_DECLARE_serverfieldedict
1617 #undef PRVM_DECLARE_serverfieldfunction
1618 #undef PRVM_DECLARE_clientfieldfloat
1619 #undef PRVM_DECLARE_clientfieldvector
1620 #undef PRVM_DECLARE_clientfieldstring
1621 #undef PRVM_DECLARE_clientfieldedict
1622 #undef PRVM_DECLARE_clientfieldfunction
1623 #undef PRVM_DECLARE_menufieldfloat
1624 #undef PRVM_DECLARE_menufieldvector
1625 #undef PRVM_DECLARE_menufieldstring
1626 #undef PRVM_DECLARE_menufieldedict
1627 #undef PRVM_DECLARE_menufieldfunction
1628 #undef PRVM_DECLARE_serverfunction
1629 #undef PRVM_DECLARE_clientfunction
1630 #undef PRVM_DECLARE_menufunction
1631 #undef PRVM_DECLARE_field
1632 #undef PRVM_DECLARE_global
1633 #undef PRVM_DECLARE_function
1638 typedef struct dpfield_s
1645 #define DPFIELDS (sizeof(dpfields) / sizeof(dpfield_t))
1647 dpfield_t dpfields[] =
1658 #define PO_HASHSIZE 16384
1659 typedef struct po_string_s
1662 struct po_string_s *nextonhashchain;
1667 po_string_t *hashtable[PO_HASHSIZE];
1670 static void PRVM_PO_UnparseString(char *out, const char *in, size_t outsize)
1679 case '\a': if(outsize >= 2) { *out++ = '\\'; *out++ = 'a'; outsize -= 2; } break;
1680 case '\b': if(outsize >= 2) { *out++ = '\\'; *out++ = 'b'; outsize -= 2; } break;
1681 case '\t': if(outsize >= 2) { *out++ = '\\'; *out++ = 't'; outsize -= 2; } break;
1682 case '\r': if(outsize >= 2) { *out++ = '\\'; *out++ = 'r'; outsize -= 2; } break;
1683 case '\n': if(outsize >= 2) { *out++ = '\\'; *out++ = 'n'; outsize -= 2; } break;
1684 case '\\': if(outsize >= 2) { *out++ = '\\'; *out++ = '\\'; outsize -= 2; } break;
1685 case '"': if(outsize >= 2) { *out++ = '\\'; *out++ = '"'; outsize -= 2; } break;
1687 if(*in >= 0 && *in <= 0x1F)
1692 *out++ = '0' + ((*in & 0700) >> 6);
1693 *out++ = '0' + ((*in & 0070) >> 3);
1694 *out++ = '0' + (*in & 0007) ;
1711 static void PRVM_PO_ParseString(char *out, const char *in, size_t outsize)
1724 case 'a': if(outsize > 0) { *out++ = '\a'; --outsize; } break;
1725 case 'b': if(outsize > 0) { *out++ = '\b'; --outsize; } break;
1726 case 't': if(outsize > 0) { *out++ = '\t'; --outsize; } break;
1727 case 'r': if(outsize > 0) { *out++ = '\r'; --outsize; } break;
1728 case 'n': if(outsize > 0) { *out++ = '\n'; --outsize; } break;
1729 case '\\': if(outsize > 0) { *out++ = '\\'; --outsize; } break;
1730 case '"': if(outsize > 0) { *out++ = '"'; --outsize; } break;
1731 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
1735 if(*in >= '0' && *in <= '7')
1738 *out = (*out << 3) | (*in - '0');
1741 if(*in >= '0' && *in <= '7')
1744 *out = (*out << 3) | (*in - '0');
1755 if(outsize > 0) { *out++ = *in; --outsize; }
1770 static po_t *PRVM_PO_Load(const char *filename, const char *filename2, mempool_t *pool)
1775 char inbuf[MAX_INPUTLINE];
1776 char decodedbuf[MAX_INPUTLINE];
1779 po_string_t thisstr;
1782 for (i = 0; i < 2; ++i)
1784 const char *buf = (const char *)
1785 FS_LoadFile((i > 0 ? filename : filename2), pool, true, NULL);
1786 // first read filename2, then read filename
1787 // so that progs.dat.de.po wins over common.de.po
1788 // and within file, last item wins
1795 po = (po_t *)Mem_Alloc(pool, sizeof(*po));
1796 memset(po, 0, sizeof(*po));
1799 memset(&thisstr, 0, sizeof(thisstr)); // hush compiler warning
1807 p = strchr(p, '\n');
1813 if(*p == '\r' || *p == '\n')
1818 if(!strncmp(p, "msgid \"", 7))
1823 else if(!strncmp(p, "msgstr \"", 8))
1830 p = strchr(p, '\n');
1840 q = strchr(p, '\n');
1847 if((size_t)(q - p) >= (size_t) sizeof(inbuf))
1849 strlcpy(inbuf, p, q - p); // not - 1, because this adds a NUL
1850 PRVM_PO_ParseString(decodedbuf + decodedpos, inbuf, sizeof(decodedbuf) - decodedpos);
1851 decodedpos += strlen(decodedbuf + decodedpos);
1861 Mem_Free(thisstr.key);
1862 thisstr.key = (char *)Mem_Alloc(pool, decodedpos + 1);
1863 memcpy(thisstr.key, decodedbuf, decodedpos + 1);
1865 else if(decodedpos > 0 && thisstr.key) // skip empty translation results
1867 thisstr.value = (char *)Mem_Alloc(pool, decodedpos + 1);
1868 memcpy(thisstr.value, decodedbuf, decodedpos + 1);
1869 hashindex = CRC_Block((const unsigned char *) thisstr.key, strlen(thisstr.key)) % PO_HASHSIZE;
1870 thisstr.nextonhashchain = po->hashtable[hashindex];
1871 po->hashtable[hashindex] = (po_string_t *)Mem_Alloc(pool, sizeof(thisstr));
1872 memcpy(po->hashtable[hashindex], &thisstr, sizeof(thisstr));
1873 memset(&thisstr, 0, sizeof(thisstr));
1877 Mem_Free((char *) buf);
1882 static const char *PRVM_PO_Lookup(po_t *po, const char *str)
1884 int hashindex = CRC_Block((const unsigned char *) str, strlen(str)) % PO_HASHSIZE;
1885 po_string_t *p = po->hashtable[hashindex];
1888 if(!strcmp(str, p->key))
1890 p = p->nextonhashchain;
1894 static void PRVM_PO_Destroy(po_t *po)
1897 for(i = 0; i < PO_HASHSIZE; ++i)
1899 po_string_t *p = po->hashtable[i];
1903 p = p->nextonhashchain;
1912 void PRVM_LeakTest(prvm_prog_t *prog);
1913 void PRVM_Prog_Reset(prvm_prog_t *prog)
1917 if(prog->tempstringsbuf.cursize)
1918 Mem_Free(prog->tempstringsbuf.data);
1919 prog->tempstringsbuf.cursize = 0;
1920 PRVM_LeakTest(prog);
1921 prog->reset_cmd(prog);
1922 Mem_FreePool(&prog->progs_mempool);
1924 PRVM_PO_Destroy((po_t *) prog->po);
1926 memset(prog,0,sizeof(prvm_prog_t));
1927 prog->break_statement = -1;
1928 prog->watch_global_type = ev_void;
1929 prog->watch_field_type = ev_void;
1937 static void PRVM_LoadLNO( prvm_prog_t *prog, const char *progname ) {
1938 fs_offset_t filesize;
1940 unsigned int *header;
1943 FS_StripExtension( progname, filename, sizeof( filename ) );
1944 strlcat( filename, ".lno", sizeof( filename ) );
1946 lno = FS_LoadFile( filename, tempmempool, false, &filesize );
1952 <Spike> SafeWrite (h, &lnotype, sizeof(int));
1953 <Spike> SafeWrite (h, &version, sizeof(int));
1954 <Spike> SafeWrite (h, &numglobaldefs, sizeof(int));
1955 <Spike> SafeWrite (h, &numpr_globals, sizeof(int));
1956 <Spike> SafeWrite (h, &numfielddefs, sizeof(int));
1957 <Spike> SafeWrite (h, &numstatements, sizeof(int));
1958 <Spike> SafeWrite (h, statement_linenums, numstatements*sizeof(int));
1960 if ((unsigned int)filesize < (6 + prog->progs_numstatements) * sizeof(int))
1966 header = (unsigned int *) lno;
1967 if( header[ 0 ] == *(unsigned int *) "LNOF" &&
1968 LittleLong( header[ 1 ] ) == 1 &&
1969 (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs_numglobaldefs &&
1970 (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs_numglobals &&
1971 (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs_numfielddefs &&
1972 (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs_numstatements )
1974 prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof( int ) );
1975 memcpy( prog->statement_linenums, header + 6, prog->progs_numstatements * sizeof( int ) );
1977 /* gmqcc suports columnums */
1978 if ((unsigned int)filesize > ((6 + 2 * prog->progs_numstatements) * sizeof( int )))
1980 prog->statement_columnnums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof( int ) );
1981 memcpy( prog->statement_columnnums, header + 6 + prog->progs_numstatements, prog->progs_numstatements * sizeof( int ) );
1992 static void PRVM_UpdateBreakpoints(prvm_prog_t *prog);
1993 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)
1996 dprograms_t *dprograms;
1997 dstatement_t *instatements;
1998 ddef_t *infielddefs;
1999 ddef_t *inglobaldefs;
2001 dfunction_t *infunctions;
2003 fs_offset_t filesize;
2004 int requiredglobalspace;
2021 prog->error_cmd("PRVM_LoadProgs: there is already a %s program loaded!", prog->name );
2023 Host_LockSession(); // all progs can use the session cvar
2024 Crypto_LoadKeys(); // all progs might use the keys at init time
2028 dprograms = (dprograms_t *) data;
2032 dprograms = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
2033 if (dprograms == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
2034 prog->error_cmd("PRVM_LoadProgs: couldn't load %s for %s", filename, prog->name);
2035 // TODO bounds check header fields (e.g. numstatements), they must never go behind end of file
2037 prog->profiletime = Sys_DirtyTime();
2038 prog->starttime = host.realtime;
2040 requiredglobalspace = 0;
2041 for (i = 0;i < numrequiredglobals;i++)
2042 requiredglobalspace += required_global[i].type == ev_vector ? 3 : 1;
2044 prog->filecrc = CRC_Block((unsigned char *)dprograms, filesize);
2046 // byte swap the header
2047 prog->progs_version = LittleLong(dprograms->version);
2048 prog->progs_crc = LittleLong(dprograms->crc);
2049 if (prog->progs_version != PROG_VERSION)
2050 prog->error_cmd("%s: %s has wrong version number (%i should be %i)", prog->name, filename, prog->progs_version, PROG_VERSION);
2051 instatements = (dstatement_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_statements));
2052 prog->progs_numstatements = LittleLong(dprograms->numstatements);
2053 inglobaldefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globaldefs));
2054 prog->progs_numglobaldefs = LittleLong(dprograms->numglobaldefs);
2055 infielddefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_fielddefs));
2056 prog->progs_numfielddefs = LittleLong(dprograms->numfielddefs);
2057 infunctions = (dfunction_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_functions));
2058 prog->progs_numfunctions = LittleLong(dprograms->numfunctions);
2059 instrings = (char *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_strings));
2060 prog->progs_numstrings = LittleLong(dprograms->numstrings);
2061 inglobals = (int *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globals));
2062 prog->progs_numglobals = LittleLong(dprograms->numglobals);
2063 prog->progs_entityfields = LittleLong(dprograms->entityfields);
2065 prog->numstatements = prog->progs_numstatements;
2066 prog->numglobaldefs = prog->progs_numglobaldefs;
2067 prog->numfielddefs = prog->progs_numfielddefs;
2068 prog->numfunctions = prog->progs_numfunctions;
2069 prog->numstrings = prog->progs_numstrings;
2070 prog->numglobals = prog->progs_numglobals;
2071 prog->entityfields = prog->progs_entityfields;
2073 if (LittleLong(dprograms->ofs_strings) + prog->progs_numstrings > (int)filesize)
2074 prog->error_cmd("%s: %s strings go past end of file", prog->name, filename);
2075 prog->strings = (char *)Mem_Alloc(prog->progs_mempool, prog->progs_numstrings);
2076 memcpy(prog->strings, instrings, prog->progs_numstrings);
2077 prog->stringssize = prog->progs_numstrings;
2079 prog->numknownstrings = 0;
2080 prog->maxknownstrings = 0;
2081 prog->knownstrings = NULL;
2082 prog->knownstrings_flags = NULL;
2084 Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
2086 // we need to expand the globaldefs and fielddefs to include engine defs
2087 prog->globaldefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobaldefs + numrequiredglobals) * sizeof(ddef_t));
2088 prog->globals.fp = (prvm_vec_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobals + requiredglobalspace + 2) * sizeof(prvm_vec_t));
2089 // + 2 is because of an otherwise occurring overrun in RETURN instruction
2090 // when trying to return the last or second-last global
2091 // (RETURN always returns a vector, there is no RETURN_F instruction)
2092 prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numfielddefs + numrequiredfields) * sizeof(ddef_t));
2093 // we need to convert the statements to our memory format
2094 prog->statements = (mstatement_t *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(mstatement_t));
2095 // allocate space for profiling statement usage
2096 prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(*prog->statement_profile));
2097 prog->explicit_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(*prog->statement_profile));
2098 // functions need to be converted to the memory format
2099 prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs_numfunctions);
2101 for (i = 0;i < prog->progs_numfunctions;i++)
2103 prog->functions[i].first_statement = LittleLong(infunctions[i].first_statement);
2104 prog->functions[i].parm_start = LittleLong(infunctions[i].parm_start);
2105 prog->functions[i].s_name = LittleLong(infunctions[i].s_name);
2106 prog->functions[i].s_file = LittleLong(infunctions[i].s_file);
2107 prog->functions[i].numparms = LittleLong(infunctions[i].numparms);
2108 prog->functions[i].locals = LittleLong(infunctions[i].locals);
2109 memcpy(prog->functions[i].parm_size, infunctions[i].parm_size, sizeof(infunctions[i].parm_size));
2110 if(prog->functions[i].first_statement >= prog->numstatements)
2111 prog->error_cmd("PRVM_LoadProgs: out of bounds function statement (function %d) in %s", i, prog->name);
2112 // TODO bounds check parm_start, s_name, s_file, numparms, locals, parm_size
2115 // copy the globaldefs to the new globaldefs list
2116 for (i=0 ; i<prog->numglobaldefs ; i++)
2118 prog->globaldefs[i].type = LittleShort(inglobaldefs[i].type);
2119 prog->globaldefs[i].ofs = LittleShort(inglobaldefs[i].ofs);
2120 prog->globaldefs[i].s_name = LittleLong(inglobaldefs[i].s_name);
2121 // TODO bounds check ofs, s_name
2124 // append the required globals
2125 for (i = 0;i < numrequiredglobals;i++)
2127 prog->globaldefs[prog->numglobaldefs].type = required_global[i].type;
2128 prog->globaldefs[prog->numglobaldefs].ofs = prog->numglobals;
2129 prog->globaldefs[prog->numglobaldefs].s_name = PRVM_SetEngineString(prog, required_global[i].name);
2130 if (prog->globaldefs[prog->numglobaldefs].type == ev_vector)
2131 prog->numglobals += 3;
2134 prog->numglobaldefs++;
2137 // copy the progs fields to the new fields list
2138 for (i = 0;i < prog->numfielddefs;i++)
2140 prog->fielddefs[i].type = LittleShort(infielddefs[i].type);
2141 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
2142 prog->error_cmd("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", prog->name);
2143 prog->fielddefs[i].ofs = LittleShort(infielddefs[i].ofs);
2144 prog->fielddefs[i].s_name = LittleLong(infielddefs[i].s_name);
2145 // TODO bounds check ofs, s_name
2148 // append the required fields
2149 for (i = 0;i < numrequiredfields;i++)
2151 prog->fielddefs[prog->numfielddefs].type = required_field[i].type;
2152 prog->fielddefs[prog->numfielddefs].ofs = prog->entityfields;
2153 prog->fielddefs[prog->numfielddefs].s_name = PRVM_SetEngineString(prog, required_field[i].name);
2154 if (prog->fielddefs[prog->numfielddefs].type == ev_vector)
2155 prog->entityfields += 3;
2157 prog->entityfields++;
2158 prog->numfielddefs++;
2161 // LadyHavoc: TODO: reorder globals to match engine struct
2162 // LadyHavoc: TODO: reorder fields to match engine struct
2163 #define remapglobal(index) (index)
2164 #define remapfield(index) (index)
2167 // FIXME: LadyHavoc: this uses a crude way to identify integer constants, rather than checking for matching globaldefs and checking their type
2168 for (i = 0;i < prog->progs_numglobals;i++)
2170 u.i = LittleLong(inglobals[i]);
2171 // most globals are 0, we only need to deal with the ones that are not
2174 d = u.i & 0xFF800000;
2175 if ((d == 0xFF800000) || (d == 0))
2177 // Looks like an integer (expand to int64)
2178 prog->globals.ip[remapglobal(i)] = u.i;
2182 // Looks like a float (expand to double)
2183 prog->globals.fp[remapglobal(i)] = u.f;
2188 // LadyHavoc: TODO: support 32bit progs statement formats
2189 // copy, remap globals in statements, bounds check
2190 for (i = 0;i < prog->progs_numstatements;i++)
2192 op = (opcode_t)LittleShort(instatements[i].op);
2193 a = (unsigned short)LittleShort(instatements[i].a);
2194 b = (unsigned short)LittleShort(instatements[i].b);
2195 c = (unsigned short)LittleShort(instatements[i].c);
2201 if (a >= prog->progs_numglobals || b + i < 0 || b + i >= prog->progs_numstatements)
2202 prog->error_cmd("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", i, prog->name);
2203 prog->statements[i].op = op;
2204 prog->statements[i].operand[0] = remapglobal(a);
2205 prog->statements[i].operand[1] = -1;
2206 prog->statements[i].operand[2] = -1;
2207 prog->statements[i].jumpabsolute = i + b;
2211 if (a + i < 0 || a + i >= prog->progs_numstatements)
2212 prog->error_cmd("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, prog->name);
2213 prog->statements[i].op = op;
2214 prog->statements[i].operand[0] = -1;
2215 prog->statements[i].operand[1] = -1;
2216 prog->statements[i].operand[2] = -1;
2217 prog->statements[i].jumpabsolute = i + a;
2220 Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", (int)op, i, prog->name);
2222 // global global global
2257 if (a >= prog->progs_numglobals || b >= prog->progs_numglobals || c >= prog->progs_numglobals)
2258 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
2259 prog->statements[i].op = op;
2260 prog->statements[i].operand[0] = remapglobal(a);
2261 prog->statements[i].operand[1] = remapglobal(b);
2262 prog->statements[i].operand[2] = remapglobal(c);
2263 prog->statements[i].jumpabsolute = -1;
2265 // global none global
2271 if (a >= prog->progs_numglobals || c >= prog->progs_numglobals)
2272 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2273 prog->statements[i].op = op;
2274 prog->statements[i].operand[0] = remapglobal(a);
2275 prog->statements[i].operand[1] = -1;
2276 prog->statements[i].operand[2] = remapglobal(c);
2277 prog->statements[i].jumpabsolute = -1;
2293 if (a >= prog->progs_numglobals || b >= prog->progs_numglobals)
2294 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2295 prog->statements[i].op = op;
2296 prog->statements[i].operand[0] = remapglobal(a);
2297 prog->statements[i].operand[1] = remapglobal(b);
2298 prog->statements[i].operand[2] = -1;
2299 prog->statements[i].jumpabsolute = -1;
2303 if ( a < prog->progs_numglobals)
2304 if ( prog->globals.ip[remapglobal(a)] >= 0 )
2305 if ( prog->globals.ip[remapglobal(a)] < prog->progs_numfunctions )
2306 if ( prog->functions[prog->globals.ip[remapglobal(a)]].first_statement == -642 )
2307 ++prog->numexplicitcoveragestatements;
2318 if ( a >= prog->progs_numglobals)
2319 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2320 prog->statements[i].op = op;
2321 prog->statements[i].operand[0] = remapglobal(a);
2322 prog->statements[i].operand[1] = -1;
2323 prog->statements[i].operand[2] = -1;
2324 prog->statements[i].jumpabsolute = -1;
2328 if(prog->numstatements < 1)
2330 prog->error_cmd("PRVM_LoadProgs: empty program in %s", prog->name);
2332 else switch(prog->statements[prog->numstatements - 1].op)
2339 prog->error_cmd("PRVM_LoadProgs: program may fall off the edge (does not end with RETURN, GOTO or DONE) in %s", prog->name);
2343 // we're done with the file now
2345 Mem_Free(dprograms);
2348 // check required functions
2349 for(i=0 ; i < numrequiredfunc ; i++)
2350 if(PRVM_ED_FindFunction(prog, required_func[i]) == 0)
2351 prog->error_cmd("%s: %s not found in %s",prog->name, required_func[i], filename);
2353 PRVM_LoadLNO(prog, filename);
2355 PRVM_Init_Exec(prog);
2357 if(*prvm_language.string)
2358 // in CSQC we really shouldn't be able to change how stuff works... sorry for now
2359 // later idea: include a list of authorized .po file checksums with the csprogs
2361 qboolean deftrans = prog == CLVM_prog;
2362 const char *realfilename = (prog != CLVM_prog ? filename : csqc_progname.string);
2363 if(deftrans) // once we have dotranslate_ strings, ALWAYS use the opt-in method!
2365 for (i=0 ; i<prog->numglobaldefs ; i++)
2368 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2369 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2370 if(name && !strncmp(name, "dotranslate_", 12))
2377 if(!strcmp(prvm_language.string, "dump"))
2379 qfile_t *f = FS_OpenRealFile(va(vabuf, sizeof(vabuf), "%s.pot", realfilename), "w", false);
2380 Con_Printf("Dumping to %s.pot\n", realfilename);
2383 for (i=0 ; i<prog->numglobaldefs ; i++)
2386 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2387 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2388 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2390 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2391 const char *value = PRVM_GetString(prog, val->string);
2394 char buf[MAX_INPUTLINE];
2395 PRVM_PO_UnparseString(buf, value, sizeof(buf));
2396 FS_Printf(f, "msgid \"%s\"\nmsgstr \"\"\n\n", buf);
2405 po_t *po = PRVM_PO_Load(
2406 va(vabuf, sizeof(vabuf), "%s.%s.po", realfilename, prvm_language.string),
2407 va(vabuf2, sizeof(vabuf2), "common.%s.po", prvm_language.string),
2408 prog->progs_mempool);
2411 for (i=0 ; i<prog->numglobaldefs ; i++)
2414 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2415 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2416 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2418 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2419 const char *value = PRVM_GetString(prog, val->string);
2422 value = PRVM_PO_Lookup(po, value);
2424 val->string = PRVM_SetEngineString(prog, value);
2432 for (cvar = prog->console_cmd->cvars->vars; cvar; cvar = cvar->next)
2433 cvar->globaldefindex[prog - prvm_prog_list] = -1;
2435 for (i=0 ; i<prog->numglobaldefs ; i++)
2438 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2439 //Con_Printf("found var %s\n", name);
2441 && !strncmp(name, "autocvar_", 9)
2442 && !(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
2445 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2446 cvar = Cvar_FindVar(prog->console_cmd->cvars, name + 9, prog->console_cmd->cvars_flagsmask);
2447 //Con_Printf("PRVM_LoadProgs: autocvar global %s in %s, processing...\n", name, prog->name);
2454 Con_DPrintf("PRVM_LoadProgs: no cvar for autocvar global %s in %s, creating...\n", name, prog->name);
2455 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2458 if((float)((int)(val->_float)) == val->_float)
2459 dpsnprintf(buf, sizeof(buf), "%i", (int)(val->_float));
2464 for (int precision = 7; precision <= 9; ++precision) {
2465 dpsnprintf(buf, sizeof(buf), "%.*g", precision, f);
2466 if ((float)atof(buf) == f) {
2474 for (i = 0; i < 3; ++i)
2478 for (int precision = 7; precision <= 9; ++precision) {
2479 dpsnprintf(buf, sizeof(buf), "%.*g", precision, f);
2480 if ((float)atof(buf) == f) {
2481 prec[i] = precision;
2486 dpsnprintf(buf, sizeof(buf), "%.*g %.*g %.*g", prec[0], val->vector[0], prec[1], val->vector[1], prec[2], val->vector[2]);
2490 value = PRVM_GetString(prog, val->string);
2493 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2496 cvar = Cvar_Get(prog->console_cmd->cvars, name + 9, value, prog->console_cmd->cvars_flagsmask, NULL);
2497 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2499 val->string = PRVM_SetEngineString(prog, cvar->string);
2500 cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2503 prog->error_cmd("PRVM_LoadProgs: could not create cvar for autocvar global %s in %s", name, prog->name);
2504 cvar->globaldefindex[prog - prvm_prog_list] = i;
2506 else if((cvar->flags & CVAR_PRIVATE) == 0)
2508 // MUST BE SYNCED WITH cvar.c Cvar_Set
2511 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2514 val->_float = cvar->value;
2518 VectorClear(val->vector);
2519 for (j = 0;j < 3;j++)
2521 while (*s && ISWHITESPACE(*s))
2525 val->vector[j] = atof(s);
2526 while (!ISWHITESPACE(*s))
2533 val->string = PRVM_SetEngineString(prog, cvar->string);
2534 cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2537 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2540 cvar->globaldefindex[prog - prvm_prog_list] = i;
2543 Con_Printf("PRVM_LoadProgs: private cvar for autocvar global %s in %s\n", name, prog->name);
2549 prog->loaded = true;
2551 PRVM_UpdateBreakpoints(prog);
2553 // set flags & ddef_ts in prog
2557 PRVM_FindOffsets(prog);
2559 prog->init_cmd(prog);
2562 PRVM_MEM_Alloc(prog);
2564 Con_Printf("%s: program loaded (crc %i, size %iK)\n", prog->name, prog->filecrc, (int)(filesize/1024));
2566 // Inittime is at least the time when this function finished. However,
2567 // later events may bump it.
2568 prog->inittime = host.realtime;
2572 static void PRVM_Fields_f(cmd_state_t *cmd)
2575 int i, j, ednum, used, usedamount;
2577 char tempstring[MAX_INPUTLINE], tempstring2[260];
2587 Con_Print("no progs loaded\n");
2592 if(Cmd_Argc(cmd) != 2)
2594 Con_Print("prvm_fields <program name>\n");
2598 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2601 counts = (int *)Mem_Alloc(tempmempool, prog->numfielddefs * sizeof(int));
2602 for (ednum = 0;ednum < prog->max_edicts;ednum++)
2604 ed = PRVM_EDICT_NUM(ednum);
2605 if (ed->priv.required->free)
2607 for (i = 1;i < prog->numfielddefs;i++)
2609 d = &prog->fielddefs[i];
2610 name = PRVM_GetString(prog, d->s_name);
2611 if (name[strlen(name)-2] == '_')
2612 continue; // skip _x, _y, _z vars
2613 val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
2614 // if the value is still all 0, skip the field
2615 for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
2617 if (val->ivector[j])
2628 for (i = 0;i < prog->numfielddefs;i++)
2630 d = &prog->fielddefs[i];
2631 name = PRVM_GetString(prog, d->s_name);
2632 if (name[strlen(name)-2] == '_')
2633 continue; // skip _x, _y, _z vars
2634 switch(d->type & ~DEF_SAVEGLOBAL)
2637 strlcat(tempstring, "string ", sizeof(tempstring));
2640 strlcat(tempstring, "entity ", sizeof(tempstring));
2643 strlcat(tempstring, "function ", sizeof(tempstring));
2646 strlcat(tempstring, "field ", sizeof(tempstring));
2649 strlcat(tempstring, "void ", sizeof(tempstring));
2652 strlcat(tempstring, "float ", sizeof(tempstring));
2655 strlcat(tempstring, "vector ", sizeof(tempstring));
2658 strlcat(tempstring, "pointer ", sizeof(tempstring));
2661 dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
2662 strlcat(tempstring, tempstring2, sizeof(tempstring));
2665 if (strlen(name) > sizeof(tempstring2)-4)
2667 memcpy (tempstring2, name, sizeof(tempstring2)-4);
2668 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
2669 tempstring2[sizeof(tempstring2)-1] = 0;
2672 strlcat(tempstring, name, sizeof(tempstring));
2673 for (j = (int)strlen(name);j < 25;j++)
2674 strlcat(tempstring, " ", sizeof(tempstring));
2675 dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
2676 strlcat(tempstring, tempstring2, sizeof(tempstring));
2677 strlcat(tempstring, "\n", sizeof(tempstring));
2678 if (strlen(tempstring) >= sizeof(tempstring)/2)
2680 Con_Print(tempstring);
2686 usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
2690 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);
2693 static void PRVM_Globals_f(cmd_state_t *cmd)
2697 const char *wildcard;
2703 Con_Print("no progs loaded\n");
2706 if(Cmd_Argc (cmd) < 2 || Cmd_Argc(cmd) > 3)
2708 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
2712 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2715 if( Cmd_Argc(cmd) == 3)
2716 wildcard = Cmd_Argv(cmd, 2);
2720 Con_Printf("%s :", prog->name);
2722 for (i = 0;i < prog->numglobaldefs;i++)
2725 if( !matchpattern( PRVM_GetString(prog, prog->globaldefs[i].s_name), wildcard, 1) )
2730 Con_Printf("%s\n", PRVM_GetString(prog, prog->globaldefs[i].s_name));
2732 Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->numglobals, numculled, prog->numglobals * 4);
2740 static void PRVM_Global_f(cmd_state_t *cmd)
2744 char valuebuf[MAX_INPUTLINE];
2745 if( Cmd_Argc(cmd) != 3 ) {
2746 Con_Printf( "prvm_global <program name> <global name>\n" );
2750 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2753 global = PRVM_ED_FindGlobal( prog, Cmd_Argv(cmd, 2) );
2755 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(cmd, 2), Cmd_Argv(cmd, 1) );
2757 Con_Printf( "%s: %s\n", Cmd_Argv(cmd, 2), PRVM_ValueString( prog, (etype_t)global->type, PRVM_GLOBALFIELDVALUE(global->ofs), valuebuf, sizeof(valuebuf) ) );
2765 static void PRVM_GlobalSet_f(cmd_state_t *cmd)
2769 if( Cmd_Argc(cmd) != 4 ) {
2770 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2774 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2777 global = PRVM_ED_FindGlobal( prog, Cmd_Argv(cmd, 2) );
2779 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(cmd, 2), Cmd_Argv(cmd, 1) );
2781 PRVM_ED_ParseEpair( prog, NULL, global, Cmd_Argv(cmd, 3), true );
2785 ======================
2786 Break- and Watchpoints
2787 ======================
2791 char break_statement[256];
2792 char watch_global[256];
2794 char watch_field[256];
2797 static debug_data_t debug_data[PRVM_PROG_MAX];
2799 void PRVM_Breakpoint(prvm_prog_t *prog, int stack_index, const char *text)
2802 Con_Printf("PRVM_Breakpoint: %s\n", text);
2803 PRVM_PrintState(prog, stack_index);
2804 if (prvm_breakpointdump.integer)
2805 SV_Savegame_to(prog, va(vabuf, sizeof(vabuf), "breakpoint-%s.dmp", prog->name));
2808 void PRVM_Watchpoint(prvm_prog_t *prog, int stack_index, const char *text, etype_t type, prvm_eval_t *o, prvm_eval_t *n)
2810 size_t sz = sizeof(prvm_vec_t) * ((type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2811 if (memcmp(o, n, sz))
2814 char valuebuf_o[128];
2815 char valuebuf_n[128];
2816 PRVM_UglyValueString(prog, type, o, valuebuf_o, sizeof(valuebuf_o));
2817 PRVM_UglyValueString(prog, type, n, valuebuf_n, sizeof(valuebuf_n));
2818 dpsnprintf(buf, sizeof(buf), "%s: %s -> %s", text, valuebuf_o, valuebuf_n);
2819 PRVM_Breakpoint(prog, stack_index, buf);
2824 static void PRVM_UpdateBreakpoints(prvm_prog_t *prog)
2826 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2829 if (debug->break_statement[0])
2831 if (debug->break_statement[0] >= '0' && debug->break_statement[0] <= '9')
2833 prog->break_statement = atoi(debug->break_statement);
2834 prog->break_stack_index = 0;
2839 func = PRVM_ED_FindFunction (prog, debug->break_statement);
2842 Con_Printf("%s progs: no function or statement named %s to break on!\n", prog->name, debug->break_statement);
2843 prog->break_statement = -1;
2847 prog->break_statement = func->first_statement;
2848 prog->break_stack_index = 1;
2851 if (prog->break_statement >= -1)
2852 Con_Printf("%s progs: breakpoint is at statement %d\n", prog->name, prog->break_statement);
2855 prog->break_statement = -1;
2857 if (debug->watch_global[0])
2859 ddef_t *global = PRVM_ED_FindGlobal( prog, debug->watch_global );
2862 Con_Printf( "%s progs: no global named '%s' to watch!\n", prog->name, debug->watch_global );
2863 prog->watch_global_type = ev_void;
2867 size_t sz = sizeof(prvm_vec_t) * ((global->type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2868 prog->watch_global = global->ofs;
2869 prog->watch_global_type = (etype_t)global->type;
2870 memcpy(&prog->watch_global_value, PRVM_GLOBALFIELDVALUE(prog->watch_global), sz);
2872 if (prog->watch_global_type != ev_void)
2873 Con_Printf("%s progs: global watchpoint is at global index %d\n", prog->name, prog->watch_global);
2876 prog->watch_global_type = ev_void;
2878 if (debug->watch_field[0])
2880 ddef_t *field = PRVM_ED_FindField( prog, debug->watch_field );
2883 Con_Printf( "%s progs: no field named '%s' to watch!\n", prog->name, debug->watch_field );
2884 prog->watch_field_type = ev_void;
2888 size_t sz = sizeof(prvm_vec_t) * ((field->type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2889 prog->watch_edict = debug->watch_edict;
2890 prog->watch_field = field->ofs;
2891 prog->watch_field_type = (etype_t)field->type;
2892 if (prog->watch_edict < prog->num_edicts)
2893 memcpy(&prog->watch_edictfield_value, PRVM_EDICTFIELDVALUE(PRVM_EDICT_NUM(prog->watch_edict), prog->watch_field), sz);
2895 memset(&prog->watch_edictfield_value, 0, sz);
2897 if (prog->watch_edict != ev_void)
2898 Con_Printf("%s progs: edict field watchpoint is at edict %d field index %d\n", prog->name, prog->watch_edict, prog->watch_field);
2901 prog->watch_field_type = ev_void;
2904 static void PRVM_Breakpoint_f(cmd_state_t *cmd)
2908 if( Cmd_Argc(cmd) == 2 ) {
2909 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2912 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2913 debug->break_statement[0] = 0;
2915 PRVM_UpdateBreakpoints(prog);
2918 if( Cmd_Argc(cmd) != 3 ) {
2919 Con_Printf( "prvm_breakpoint <program name> <function name | statement>\n" );
2923 if (!(prog = PRVM_ProgFromString(Cmd_Argv(cmd, 1))))
2927 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2928 strlcpy(debug->break_statement, Cmd_Argv(cmd, 2), sizeof(debug->break_statement));
2930 PRVM_UpdateBreakpoints(prog);
2933 static void PRVM_GlobalWatchpoint_f(cmd_state_t *cmd)
2937 if( Cmd_Argc(cmd) == 2 ) {
2938 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2941 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2942 debug->watch_global[0] = 0;
2944 PRVM_UpdateBreakpoints(prog);
2947 if( Cmd_Argc(cmd) != 3 ) {
2948 Con_Printf( "prvm_globalwatchpoint <program name> <global name>\n" );
2952 if (!(prog = PRVM_ProgFromString(Cmd_Argv(cmd, 1))))
2956 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2957 strlcpy(debug->watch_global, Cmd_Argv(cmd, 2), sizeof(debug->watch_global));
2959 PRVM_UpdateBreakpoints(prog);
2962 static void PRVM_EdictWatchpoint_f(cmd_state_t *cmd)
2966 if( Cmd_Argc(cmd) == 2 ) {
2967 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2970 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2971 debug->watch_field[0] = 0;
2973 PRVM_UpdateBreakpoints(prog);
2976 if( Cmd_Argc(cmd) != 4 ) {
2977 Con_Printf( "prvm_edictwatchpoint <program name> <edict number> <field name>\n" );
2981 if (!(prog = PRVM_ProgFromString(Cmd_Argv(cmd, 1))))
2985 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2986 debug->watch_edict = atoi(Cmd_Argv(cmd, 2));
2987 strlcpy(debug->watch_field, Cmd_Argv(cmd, 3), sizeof(debug->watch_field));
2989 PRVM_UpdateBreakpoints(prog);
2997 void PRVM_Init (void)
2999 Cmd_AddCommand(CMD_SHARED, "prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
3000 Cmd_AddCommand(CMD_SHARED, "prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
3001 Cmd_AddCommand(CMD_SHARED, "prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
3002 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)");
3003 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");
3004 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)");
3005 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)");
3006 Cmd_AddCommand(CMD_SHARED, "prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
3007 Cmd_AddCommand(CMD_SHARED, "prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
3008 Cmd_AddCommand(CMD_SHARED, "prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
3009 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)");
3010 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");
3011 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");
3012 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)");
3013 Cmd_AddCommand(CMD_SHARED, "cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
3014 Cmd_AddCommand(CMD_SHARED, "menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
3015 Cmd_AddCommand(CMD_SHARED, "sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
3016 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");
3017 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");
3018 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");
3020 Cvar_RegisterVariable (&prvm_language);
3021 Cvar_RegisterVariable (&prvm_traceqc);
3022 Cvar_RegisterVariable (&prvm_statementprofiling);
3023 Cvar_RegisterVariable (&prvm_timeprofiling);
3024 Cvar_RegisterVariable (&prvm_coverage);
3025 Cvar_RegisterVariable (&prvm_backtraceforwarnings);
3026 Cvar_RegisterVariable (&prvm_leaktest);
3027 Cvar_RegisterVariable (&prvm_leaktest_follow_targetname);
3028 Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames);
3029 Cvar_RegisterVariable (&prvm_errordump);
3030 Cvar_RegisterVariable (&prvm_breakpointdump);
3031 Cvar_RegisterVariable (&prvm_reuseedicts_startuptime);
3032 Cvar_RegisterVariable (&prvm_reuseedicts_neverinsameframe);
3033 Cvar_RegisterVariable (&prvm_garbagecollection_enable);
3034 Cvar_RegisterVariable (&prvm_garbagecollection_notify);
3035 Cvar_RegisterVariable (&prvm_garbagecollection_scan_limit);
3036 Cvar_RegisterVariable (&prvm_garbagecollection_strings);
3037 Cvar_RegisterVariable (&prvm_stringdebug);
3039 // COMMANDLINEOPTION: PRVM: -norunaway disables the runaway loop check (it might be impossible to exit DarkPlaces if used!)
3040 prvm_runawaycheck = !COM_CheckParm("-norunaway");
3050 void PRVM_Prog_Init(prvm_prog_t *prog, cmd_state_t *cmd)
3052 PRVM_Prog_Reset(prog);
3053 prog->leaktest_active = prvm_leaktest.integer != 0;
3054 prog->console_cmd = cmd;
3057 // LadyHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
3058 unsigned int PRVM_EDICT_NUM_ERROR(prvm_prog_t *prog, unsigned int n, const char *filename, int fileline)
3060 prog->error_cmd("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", prog->name, n, filename, fileline);
3064 #define PRVM_KNOWNSTRINGBASE 0x40000000
3066 const char *PRVM_GetString(prvm_prog_t *prog, int num)
3071 if (prvm_stringdebug.integer)
3072 VM_Warning(prog, "PRVM_GetString: Invalid string offset (%i < 0)\n", num);
3075 else if (num < prog->stringssize)
3077 // constant string from progs.dat
3078 return prog->strings + num;
3080 else if (num <= prog->stringssize + prog->tempstringsbuf.maxsize)
3082 // tempstring returned by engine to QC (becomes invalid after returning to engine)
3083 num -= prog->stringssize;
3084 if (num < prog->tempstringsbuf.cursize)
3085 return (char *)prog->tempstringsbuf.data + num;
3088 if (prvm_stringdebug.integer)
3089 VM_Warning(prog, "PRVM_GetString: Invalid temp-string offset (%i >= %i prog->tempstringsbuf.cursize)\n", num, prog->tempstringsbuf.cursize);
3093 else if (num & PRVM_KNOWNSTRINGBASE)
3096 num = num - PRVM_KNOWNSTRINGBASE;
3097 if (num >= 0 && num < prog->numknownstrings)
3099 if (!prog->knownstrings[num])
3101 if (prvm_stringdebug.integer)
3102 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
3105 // refresh the garbage collection on the string - this guards
3106 // against a certain sort of repeated migration to earlier
3107 // points in the scan that could otherwise result in the string
3108 // being freed for being unused
3109 prog->knownstrings_flags[num] = (prog->knownstrings_flags[num] & ~KNOWNSTRINGFLAG_GCPRUNE) | KNOWNSTRINGFLAG_GCMARK;
3110 return prog->knownstrings[num];
3114 if (prvm_stringdebug.integer)
3115 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
3121 // invalid string offset
3122 if (prvm_stringdebug.integer)
3123 VM_Warning(prog, "PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
3128 const char *PRVM_ChangeEngineString(prvm_prog_t *prog, int i, const char *s)
3131 i = i - PRVM_KNOWNSTRINGBASE;
3132 if (i < 0 || i >= prog->numknownstrings)
3133 prog->error_cmd("PRVM_ChangeEngineString: string index %i is out of bounds", i);
3134 else if ((prog->knownstrings_flags[i] & KNOWNSTRINGFLAG_ENGINE) == 0)
3135 prog->error_cmd("PRVM_ChangeEngineString: string index %i is not an engine string", i);
3136 old = prog->knownstrings[i];
3137 prog->knownstrings[i] = s;
3141 static void PRVM_NewKnownString(prvm_prog_t *prog, int i, int flags, const char *s)
3143 if (i >= prog->numknownstrings)
3145 if (i >= prog->maxknownstrings)
3147 const char **oldstrings = prog->knownstrings;
3148 const unsigned char *oldstrings_flags = prog->knownstrings_flags;
3149 const char **oldstrings_origin = prog->knownstrings_origin;
3150 prog->maxknownstrings += 128;
3151 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3152 prog->knownstrings_flags = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
3153 if (prog->leaktest_active)
3154 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3155 if (prog->numknownstrings)
3157 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
3158 memcpy((char **)prog->knownstrings_flags, oldstrings_flags, prog->numknownstrings * sizeof(unsigned char));
3159 if (prog->leaktest_active)
3160 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
3163 prog->numknownstrings++;
3165 prog->firstfreeknownstring = i + 1;
3166 prog->knownstrings[i] = s;
3167 // it's in use right now, spare it until the next gc pass - that said, it is not freeable so this is probably moot
3168 prog->knownstrings_flags[i] = flags;
3169 if (prog->leaktest_active)
3170 prog->knownstrings_origin[i] = NULL;
3173 int PRVM_SetEngineString(prvm_prog_t *prog, const char *s)
3178 if (s >= prog->strings && s <= prog->strings + prog->stringssize)
3179 prog->error_cmd("PRVM_SetEngineString: s in prog->strings area");
3180 // if it's in the tempstrings area, use a reserved range
3181 // (otherwise we'd get millions of useless string offsets cluttering the database)
3182 if (s >= (char *)prog->tempstringsbuf.data && s < (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.maxsize)
3183 return prog->stringssize + (s - (char *)prog->tempstringsbuf.data);
3184 // see if it's a known string address
3185 for (i = 0;i < prog->numknownstrings;i++)
3186 if (prog->knownstrings[i] == s)
3187 return PRVM_KNOWNSTRINGBASE + i;
3188 // new unknown engine string
3189 if (developer_insane.integer)
3190 Con_DPrintf("new engine string %p = \"%s\"\n", (void *)s, s);
3191 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3192 if (!prog->knownstrings[i])
3194 PRVM_NewKnownString(prog, i, KNOWNSTRINGFLAG_GCMARK | KNOWNSTRINGFLAG_ENGINE, s);
3195 return PRVM_KNOWNSTRINGBASE + i;
3198 // temp string handling
3200 // all tempstrings go into this buffer consecutively, and it is reset
3201 // whenever PRVM_ExecuteProgram returns to the engine
3202 // (technically each PRVM_ExecuteProgram call saves the cursize value and
3203 // restores it on return, so multiple recursive calls can share the same
3205 // the buffer size is automatically grown as needed
3207 int PRVM_SetTempString(prvm_prog_t *prog, const char *s)
3213 size = (int)strlen(s) + 1;
3214 if (developer_insane.integer)
3215 Con_DPrintf("PRVM_SetTempString: cursize %i, size %i\n", prog->tempstringsbuf.cursize, size);
3216 if (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
3218 sizebuf_t old = prog->tempstringsbuf;
3219 if (prog->tempstringsbuf.cursize + size >= 1<<28)
3220 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);
3221 prog->tempstringsbuf.maxsize = max(prog->tempstringsbuf.maxsize, 65536);
3222 while (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
3223 prog->tempstringsbuf.maxsize *= 2;
3224 if (prog->tempstringsbuf.maxsize != old.maxsize || prog->tempstringsbuf.data == NULL)
3226 Con_DPrintf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, prog->tempstringsbuf.maxsize/1024);
3227 prog->tempstringsbuf.data = (unsigned char *) Mem_Alloc(prog->progs_mempool, prog->tempstringsbuf.maxsize);
3231 memcpy(prog->tempstringsbuf.data, old.data, old.cursize);
3236 t = (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.cursize;
3238 prog->tempstringsbuf.cursize += size;
3239 return PRVM_SetEngineString(prog, t);
3242 int PRVM_AllocString(prvm_prog_t *prog, size_t bufferlength, char **pointer)
3252 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3253 if (!prog->knownstrings[i])
3255 s = (char *)PRVM_Alloc(bufferlength);
3256 PRVM_NewKnownString(prog, i, KNOWNSTRINGFLAG_GCMARK, s);
3257 if(prog->leaktest_active)
3258 prog->knownstrings_origin[i] = PRVM_AllocationOrigin(prog);
3260 *pointer = (char *)(prog->knownstrings[i]);
3261 return PRVM_KNOWNSTRINGBASE + i;
3264 void PRVM_FreeString(prvm_prog_t *prog, int num)
3267 prog->error_cmd("PRVM_FreeString: attempt to free a NULL string");
3268 else if (num >= 0 && num < prog->stringssize)
3269 prog->error_cmd("PRVM_FreeString: attempt to free a constant string");
3270 else if (num >= PRVM_KNOWNSTRINGBASE && num < PRVM_KNOWNSTRINGBASE + prog->numknownstrings)
3272 num = num - PRVM_KNOWNSTRINGBASE;
3273 if (!prog->knownstrings[num])
3274 prog->error_cmd("PRVM_FreeString: attempt to free a non-existent or already freed string");
3275 if (!prog->knownstrings_flags[num])
3276 prog->error_cmd("PRVM_FreeString: attempt to free a string owned by the engine");
3277 PRVM_Free((char *)prog->knownstrings[num]);
3278 if(prog->leaktest_active)
3279 if(prog->knownstrings_origin[num])
3280 PRVM_Free((char *)prog->knownstrings_origin[num]);
3281 prog->knownstrings[num] = NULL;
3282 prog->knownstrings_flags[num] = 0;
3283 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
3286 prog->error_cmd("PRVM_FreeString: invalid string offset %i", num);
3289 static qboolean PRVM_IsStringReferenced(prvm_prog_t *prog, string_t string)
3293 for (i = 0;i < prog->numglobaldefs;i++)
3295 ddef_t *d = &prog->globaldefs[i];
3296 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3298 if(string == PRVM_GLOBALFIELDSTRING(d->ofs))
3302 for(j = 0; j < prog->num_edicts; ++j)
3304 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3305 if (ed->priv.required->free)
3307 for (i=0; i<prog->numfielddefs; ++i)
3309 ddef_t *d = &prog->fielddefs[i];
3310 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3312 if(string == PRVM_EDICTFIELDSTRING(ed, d->ofs))
3320 static qboolean PRVM_IsEdictRelevant(prvm_prog_t *prog, prvm_edict_t *edict)
3324 if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
3325 return true; // world or clients
3326 if (edict->priv.required->freetime <= prog->inittime)
3327 return true; // created during startup
3328 if (prog == SVVM_prog)
3330 if(PRVM_serveredictfloat(edict, solid)) // can block other stuff, or is a trigger?
3332 if(PRVM_serveredictfloat(edict, modelindex)) // visible ent?
3334 if(PRVM_serveredictfloat(edict, effects)) // particle effect?
3336 if(PRVM_serveredictfunction(edict, think)) // has a think function?
3337 if(PRVM_serveredictfloat(edict, nextthink) > 0) // that actually will eventually run?
3339 if(PRVM_serveredictfloat(edict, takedamage))
3341 if(*prvm_leaktest_ignore_classnames.string)
3343 if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_serveredictstring(edict, classname)))))
3347 else if (prog == CLVM_prog)
3349 // TODO someone add more stuff here
3350 if(PRVM_clientedictfloat(edict, entnum)) // csqc networked
3352 if(PRVM_clientedictfloat(edict, modelindex)) // visible ent?
3354 if(PRVM_clientedictfloat(edict, effects)) // particle effect?
3356 if(PRVM_clientedictfunction(edict, think)) // has a think function?
3357 if(PRVM_clientedictfloat(edict, nextthink) > 0) // that actually will eventually run?
3359 if(*prvm_leaktest_ignore_classnames.string)
3361 if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_clientedictstring(edict, classname)))))
3367 // menu prog does not have classnames
3372 static qboolean PRVM_IsEdictReferenced(prvm_prog_t *prog, prvm_edict_t *edict, int mark)
3375 int edictnum = PRVM_NUM_FOR_EDICT(edict);
3376 const char *targetname = NULL;
3378 if (prog == SVVM_prog && prvm_leaktest_follow_targetname.integer)
3379 targetname = PRVM_GetString(prog, PRVM_serveredictstring(edict, targetname));
3382 if(!*targetname) // ""
3385 for(j = 0; j < prog->num_edicts; ++j)
3387 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3388 if (ed->priv.required->mark < mark)
3394 const char *target = PRVM_GetString(prog, PRVM_serveredictstring(ed, target));
3396 if(!strcmp(target, targetname))
3399 for (i=0; i<prog->numfielddefs; ++i)
3401 ddef_t *d = &prog->fielddefs[i];
3402 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3404 if(edictnum == PRVM_EDICTFIELDEDICT(ed, d->ofs))
3412 static void PRVM_MarkReferencedEdicts(prvm_prog_t *prog)
3418 // Stage 1: world, all entities that are relevant, and all entities that are referenced by globals.
3420 for(j = 0; j < prog->num_edicts; ++j)
3422 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3423 if(ed->priv.required->free)
3425 ed->priv.required->mark = PRVM_IsEdictRelevant(prog, ed) ? stage : 0;
3427 for (i = 0;i < prog->numglobaldefs;i++)
3429 ddef_t *d = &prog->globaldefs[i];
3431 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3433 j = PRVM_GLOBALFIELDEDICT(d->ofs);
3434 if (i < 0 || j >= prog->max_edicts) {
3435 Con_Printf("Invalid entity reference from global %s.\n", PRVM_GetString(prog, d->s_name));
3438 ed = PRVM_EDICT_NUM(j);;
3439 ed->priv.required->mark = stage;
3442 // Future stages: all entities that are referenced by an entity of the previous stage.
3446 for(j = 0; j < prog->num_edicts; ++j)
3448 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3449 if(ed->priv.required->free)
3451 if(ed->priv.required->mark)
3453 if(PRVM_IsEdictReferenced(prog, ed, stage))
3455 ed->priv.required->mark = stage + 1;
3462 Con_DPrintf("leak check used %d stages to find all references\n", stage);
3465 void PRVM_LeakTest(prvm_prog_t *prog)
3468 qboolean leaked = false;
3470 if(!prog->leaktest_active)
3474 for (i = 0; i < prog->numknownstrings; ++i)
3476 if(prog->knownstrings[i])
3477 if(prog->knownstrings_flags[i])
3478 if(prog->knownstrings_origin[i])
3479 if(!PRVM_IsStringReferenced(prog, PRVM_KNOWNSTRINGBASE + i))
3481 Con_Printf("Unreferenced string found!\n Value: %s\n Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
3487 PRVM_MarkReferencedEdicts(prog);
3488 for(j = 0; j < prog->num_edicts; ++j)
3490 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3491 if(ed->priv.required->free)
3493 if(!ed->priv.required->mark)
3494 if(ed->priv.required->allocation_origin)
3496 Con_Printf("Unreferenced edict found!\n Allocated at: %s\n", ed->priv.required->allocation_origin);
3497 PRVM_ED_Print(prog, ed, NULL);
3502 ed->priv.required->mark = 0; // clear marks again when done
3505 for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
3507 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
3509 if(stringbuffer->origin)
3511 Con_Printf("Open string buffer handle found!\n Allocated at: %s\n", stringbuffer->origin);
3516 for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
3518 if(prog->openfiles[i])
3519 if(prog->openfiles_origin[i])
3521 Con_Printf("Open file handle found!\n Allocated at: %s\n", prog->openfiles_origin[i]);
3526 for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
3528 if(prog->opensearches[i])
3529 if(prog->opensearches_origin[i])
3531 Con_Printf("Open search handle found!\n Allocated at: %s\n", prog->opensearches_origin[i]);
3537 Con_Printf("Congratulations. No leaks found.\n");
3540 void PRVM_GarbageCollection(prvm_prog_t *prog)
3542 int limit = prvm_garbagecollection_scan_limit.integer;
3543 prvm_prog_garbagecollection_state_t *gc = &prog->gc;
3544 if (!prvm_garbagecollection_enable.integer)
3547 // we like to limit how much scanning we do so it doesn't put a significant
3548 // burden on the cpu, so each of these are not complete scans, we also like
3549 // to have consistent cpu usage so we do a bit of work on each category of
3550 // leaked object every frame
3556 case PRVM_GC_GLOBALS_MARK:
3557 for (; gc->globals_mark_progress < prog->numglobaldefs && (limit--) > 0; gc->globals_mark_progress++)
3559 ddef_t *d = &prog->globaldefs[gc->globals_mark_progress];
3564 prvm_int_t s = prog->globals.ip[d->ofs];
3565 if (s & PRVM_KNOWNSTRINGBASE)
3567 prvm_int_t num = s - PRVM_KNOWNSTRINGBASE;
3568 if (!prog->knownstrings[num])
3571 Con_DPrintf("PRVM_GarbageCollection: Found bogus strzone reference in global %i (global name: \"%s\"), erasing reference", d->ofs, PRVM_GetString(prog, d->s_name));
3572 prog->globals.ip[d->ofs] = 0;
3575 prog->knownstrings_flags[num] = (prog->knownstrings_flags[num] | KNOWNSTRINGFLAG_GCMARK) & ~KNOWNSTRINGFLAG_GCPRUNE;
3583 if (gc->globals_mark_progress >= prog->numglobaldefs)
3586 case PRVM_GC_FIELDS_MARK:
3587 for (; gc->fields_mark_progress < prog->numfielddefs && limit > 0;)
3589 ddef_t *d = &prog->fielddefs[gc->fields_mark_progress];
3593 //for (gc-> entityindex = 0; entityindex < prog->num_edicts; entityindex++)
3594 for (;gc->fields_mark_progress_entity < prog->num_edicts && (limit--) > 0;gc->fields_mark_progress_entity++)
3596 int entityindex = gc->fields_mark_progress_entity;
3597 prvm_int_t s = prog->edictsfields.ip[entityindex * prog->entityfields + d->ofs];
3598 if (s & PRVM_KNOWNSTRINGBASE)
3600 prvm_int_t num = s - PRVM_KNOWNSTRINGBASE;
3601 if (!prog->knownstrings[num])
3604 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));
3605 prog->edictsfields.ip[entityindex * prog->entityfields + d->ofs] = 0;
3608 prog->knownstrings_flags[num] = (prog->knownstrings_flags[num] | KNOWNSTRINGFLAG_GCMARK) & ~KNOWNSTRINGFLAG_GCPRUNE;
3611 if (gc->fields_mark_progress_entity >= prog->num_edicts)
3613 gc->fields_mark_progress_entity = 0;
3614 gc->fields_mark_progress++;
3618 gc->fields_mark_progress_entity = 0;
3619 gc->fields_mark_progress++;
3623 if (gc->fields_mark_progress >= prog->numfielddefs)
3626 case PRVM_GC_KNOWNSTRINGS_SWEEP:
3627 // free any strzone'd strings that are not marked
3628 if (!prvm_garbagecollection_strings.integer)
3633 for (;gc->knownstrings_sweep_progress < prog->numknownstrings && (limit--) > 0;gc->knownstrings_sweep_progress++)
3635 int num = gc->knownstrings_sweep_progress;
3636 if (prog->knownstrings[num] && (prog->knownstrings_flags[num] & (KNOWNSTRINGFLAG_GCMARK | KNOWNSTRINGFLAG_ENGINE)) == 0)
3638 if (prog->knownstrings_flags[num] & KNOWNSTRINGFLAG_GCPRUNE)
3640 // string has been marked for pruning two passes in a row
3641 if (prvm_garbagecollection_notify.integer)
3642 Con_DPrintf("prvm_garbagecollection_notify: %s: freeing unreferenced string %i: \"%s\"\n", prog->name, num, prog->knownstrings[num]);
3643 Mem_Free((char *)prog->knownstrings[num]);
3644 prog->knownstrings[num] = NULL;
3645 prog->knownstrings_flags[num] = 0;
3646 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
3650 // mark it for pruning next pass
3651 prog->knownstrings_flags[num] |= KNOWNSTRINGFLAG_GCPRUNE;
3655 if (gc->knownstrings_sweep_progress >= prog->numknownstrings)
3660 memset(gc, 0, sizeof(*gc));