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, "");
1455 void PRVM_ED_CallPostspawnFunction (prvm_prog_t *prog, prvm_edict_t *ent)
1457 if(!ent->priv.required->free)
1458 if (PRVM_serverfunction(SV_OnEntityPostSpawnFunction))
1461 PRVM_serverglobalfloat(time) = sv.time;
1462 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1463 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityPostSpawnFunction), "QC function SV_OnEntityPostSpawnFunction is missing");
1469 PRVM_ED_LoadFromFile
1471 The entities are directly placed in the array, rather than allocated with
1472 PRVM_ED_Alloc, because otherwise an error loading the map would have entity
1473 number references out of order.
1475 Creates a server's entity / program execution context by
1476 parsing textual entity definitions out of an ent file.
1478 Used for both fresh maps and savegame loads. A fresh map would also need
1479 to call PRVM_ED_CallSpawnFunctions () to let the objects initialize themselves.
1482 void PRVM_ED_LoadFromFile (prvm_prog_t *prog, const char *data)
1486 int parsed, inhibited, spawned, died;
1493 prvm_reuseedicts_always_allow = host.realtime;
1500 // parse the opening brace
1501 if (!COM_ParseToken_Simple(&data, false, false, true))
1503 if (com_token[0] != '{')
1504 prog->error_cmd("PRVM_ED_LoadFromFile: %s: found %s when expecting {", prog->name, com_token);
1506 // CHANGED: this is not conform to PR_LoadFromFile
1507 if(prog->loadintoworld)
1509 prog->loadintoworld = false;
1510 ent = PRVM_EDICT_NUM(0);
1513 ent = PRVM_ED_Alloc(prog);
1516 if (ent != prog->edicts) // hack
1517 memset (ent->fields.fp, 0, prog->entityfields * sizeof(prvm_vec_t));
1519 data = PRVM_ED_ParseEdict (prog, data, ent);
1522 // remove the entity ?
1523 if(!prog->load_edict(prog, ent))
1525 PRVM_ED_Free(prog, ent);
1530 PRVM_ED_CallPrespawnFunction(prog, ent);
1532 if(ent->priv.required->free)
1538 if(!PRVM_ED_CallSpawnFunction(prog, ent, data, start))
1541 PRVM_ED_CallPostspawnFunction(prog, ent);
1544 if (ent->priv.required->free)
1548 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);
1550 prvm_reuseedicts_always_allow = 0;
1553 static void PRVM_FindOffsets(prvm_prog_t *prog)
1555 // field and global searches use -1 for NULL
1556 memset(&prog->fieldoffsets, -1, sizeof(prog->fieldoffsets));
1557 memset(&prog->globaloffsets, -1, sizeof(prog->globaloffsets));
1558 // function searches use 0 for NULL
1559 memset(&prog->funcoffsets, 0, sizeof(prog->funcoffsets));
1560 #define PRVM_DECLARE_serverglobalfloat(x)
1561 #define PRVM_DECLARE_serverglobalvector(x)
1562 #define PRVM_DECLARE_serverglobalstring(x)
1563 #define PRVM_DECLARE_serverglobaledict(x)
1564 #define PRVM_DECLARE_serverglobalfunction(x)
1565 #define PRVM_DECLARE_clientglobalfloat(x)
1566 #define PRVM_DECLARE_clientglobalvector(x)
1567 #define PRVM_DECLARE_clientglobalstring(x)
1568 #define PRVM_DECLARE_clientglobaledict(x)
1569 #define PRVM_DECLARE_clientglobalfunction(x)
1570 #define PRVM_DECLARE_menuglobalfloat(x)
1571 #define PRVM_DECLARE_menuglobalvector(x)
1572 #define PRVM_DECLARE_menuglobalstring(x)
1573 #define PRVM_DECLARE_menuglobaledict(x)
1574 #define PRVM_DECLARE_menuglobalfunction(x)
1575 #define PRVM_DECLARE_serverfieldfloat(x)
1576 #define PRVM_DECLARE_serverfieldvector(x)
1577 #define PRVM_DECLARE_serverfieldstring(x)
1578 #define PRVM_DECLARE_serverfieldedict(x)
1579 #define PRVM_DECLARE_serverfieldfunction(x)
1580 #define PRVM_DECLARE_clientfieldfloat(x)
1581 #define PRVM_DECLARE_clientfieldvector(x)
1582 #define PRVM_DECLARE_clientfieldstring(x)
1583 #define PRVM_DECLARE_clientfieldedict(x)
1584 #define PRVM_DECLARE_clientfieldfunction(x)
1585 #define PRVM_DECLARE_menufieldfloat(x)
1586 #define PRVM_DECLARE_menufieldvector(x)
1587 #define PRVM_DECLARE_menufieldstring(x)
1588 #define PRVM_DECLARE_menufieldedict(x)
1589 #define PRVM_DECLARE_menufieldfunction(x)
1590 #define PRVM_DECLARE_serverfunction(x)
1591 #define PRVM_DECLARE_clientfunction(x)
1592 #define PRVM_DECLARE_menufunction(x)
1593 #define PRVM_DECLARE_field(x) prog->fieldoffsets.x = PRVM_ED_FindFieldOffset(prog, #x);
1594 #define PRVM_DECLARE_global(x) prog->globaloffsets.x = PRVM_ED_FindGlobalOffset(prog, #x);
1595 #define PRVM_DECLARE_function(x) prog->funcoffsets.x = PRVM_ED_FindFunctionOffset(prog, #x);
1596 #include "prvm_offsets.h"
1597 #undef PRVM_DECLARE_serverglobalfloat
1598 #undef PRVM_DECLARE_serverglobalvector
1599 #undef PRVM_DECLARE_serverglobalstring
1600 #undef PRVM_DECLARE_serverglobaledict
1601 #undef PRVM_DECLARE_serverglobalfunction
1602 #undef PRVM_DECLARE_clientglobalfloat
1603 #undef PRVM_DECLARE_clientglobalvector
1604 #undef PRVM_DECLARE_clientglobalstring
1605 #undef PRVM_DECLARE_clientglobaledict
1606 #undef PRVM_DECLARE_clientglobalfunction
1607 #undef PRVM_DECLARE_menuglobalfloat
1608 #undef PRVM_DECLARE_menuglobalvector
1609 #undef PRVM_DECLARE_menuglobalstring
1610 #undef PRVM_DECLARE_menuglobaledict
1611 #undef PRVM_DECLARE_menuglobalfunction
1612 #undef PRVM_DECLARE_serverfieldfloat
1613 #undef PRVM_DECLARE_serverfieldvector
1614 #undef PRVM_DECLARE_serverfieldstring
1615 #undef PRVM_DECLARE_serverfieldedict
1616 #undef PRVM_DECLARE_serverfieldfunction
1617 #undef PRVM_DECLARE_clientfieldfloat
1618 #undef PRVM_DECLARE_clientfieldvector
1619 #undef PRVM_DECLARE_clientfieldstring
1620 #undef PRVM_DECLARE_clientfieldedict
1621 #undef PRVM_DECLARE_clientfieldfunction
1622 #undef PRVM_DECLARE_menufieldfloat
1623 #undef PRVM_DECLARE_menufieldvector
1624 #undef PRVM_DECLARE_menufieldstring
1625 #undef PRVM_DECLARE_menufieldedict
1626 #undef PRVM_DECLARE_menufieldfunction
1627 #undef PRVM_DECLARE_serverfunction
1628 #undef PRVM_DECLARE_clientfunction
1629 #undef PRVM_DECLARE_menufunction
1630 #undef PRVM_DECLARE_field
1631 #undef PRVM_DECLARE_global
1632 #undef PRVM_DECLARE_function
1637 typedef struct dpfield_s
1644 #define DPFIELDS (sizeof(dpfields) / sizeof(dpfield_t))
1646 dpfield_t dpfields[] =
1657 #define PO_HASHSIZE 16384
1658 typedef struct po_string_s
1661 struct po_string_s *nextonhashchain;
1666 po_string_t *hashtable[PO_HASHSIZE];
1669 static void PRVM_PO_UnparseString(char *out, const char *in, size_t outsize)
1678 case '\a': if(outsize >= 2) { *out++ = '\\'; *out++ = 'a'; outsize -= 2; } break;
1679 case '\b': if(outsize >= 2) { *out++ = '\\'; *out++ = 'b'; outsize -= 2; } break;
1680 case '\t': if(outsize >= 2) { *out++ = '\\'; *out++ = 't'; outsize -= 2; } break;
1681 case '\r': if(outsize >= 2) { *out++ = '\\'; *out++ = 'r'; outsize -= 2; } break;
1682 case '\n': if(outsize >= 2) { *out++ = '\\'; *out++ = 'n'; outsize -= 2; } break;
1683 case '\\': if(outsize >= 2) { *out++ = '\\'; *out++ = '\\'; outsize -= 2; } break;
1684 case '"': if(outsize >= 2) { *out++ = '\\'; *out++ = '"'; outsize -= 2; } break;
1686 if(*in >= 0 && *in <= 0x1F)
1691 *out++ = '0' + ((*in & 0700) >> 6);
1692 *out++ = '0' + ((*in & 0070) >> 3);
1693 *out++ = '0' + (*in & 0007) ;
1710 static void PRVM_PO_ParseString(char *out, const char *in, size_t outsize)
1723 case 'a': if(outsize > 0) { *out++ = '\a'; --outsize; } break;
1724 case 'b': if(outsize > 0) { *out++ = '\b'; --outsize; } break;
1725 case 't': if(outsize > 0) { *out++ = '\t'; --outsize; } break;
1726 case 'r': if(outsize > 0) { *out++ = '\r'; --outsize; } break;
1727 case 'n': if(outsize > 0) { *out++ = '\n'; --outsize; } break;
1728 case '\\': if(outsize > 0) { *out++ = '\\'; --outsize; } break;
1729 case '"': if(outsize > 0) { *out++ = '"'; --outsize; } break;
1730 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
1734 if(*in >= '0' && *in <= '7')
1737 *out = (*out << 3) | (*in - '0');
1740 if(*in >= '0' && *in <= '7')
1743 *out = (*out << 3) | (*in - '0');
1754 if(outsize > 0) { *out++ = *in; --outsize; }
1769 static po_t *PRVM_PO_Load(const char *filename, const char *filename2, mempool_t *pool)
1774 char inbuf[MAX_INPUTLINE];
1775 char decodedbuf[MAX_INPUTLINE];
1778 po_string_t thisstr;
1781 for (i = 0; i < 2; ++i)
1783 const char *buf = (const char *)
1784 FS_LoadFile((i > 0 ? filename : filename2), pool, true, NULL);
1785 // first read filename2, then read filename
1786 // so that progs.dat.de.po wins over common.de.po
1787 // and within file, last item wins
1794 po = (po_t *)Mem_Alloc(pool, sizeof(*po));
1795 memset(po, 0, sizeof(*po));
1798 memset(&thisstr, 0, sizeof(thisstr)); // hush compiler warning
1806 p = strchr(p, '\n');
1812 if(*p == '\r' || *p == '\n')
1817 if(!strncmp(p, "msgid \"", 7))
1822 else if(!strncmp(p, "msgstr \"", 8))
1829 p = strchr(p, '\n');
1839 q = strchr(p, '\n');
1846 if((size_t)(q - p) >= (size_t) sizeof(inbuf))
1848 strlcpy(inbuf, p, q - p); // not - 1, because this adds a NUL
1849 PRVM_PO_ParseString(decodedbuf + decodedpos, inbuf, sizeof(decodedbuf) - decodedpos);
1850 decodedpos += strlen(decodedbuf + decodedpos);
1860 Mem_Free(thisstr.key);
1861 thisstr.key = (char *)Mem_Alloc(pool, decodedpos + 1);
1862 memcpy(thisstr.key, decodedbuf, decodedpos + 1);
1864 else if(decodedpos > 0 && thisstr.key) // skip empty translation results
1866 thisstr.value = (char *)Mem_Alloc(pool, decodedpos + 1);
1867 memcpy(thisstr.value, decodedbuf, decodedpos + 1);
1868 hashindex = CRC_Block((const unsigned char *) thisstr.key, strlen(thisstr.key)) % PO_HASHSIZE;
1869 thisstr.nextonhashchain = po->hashtable[hashindex];
1870 po->hashtable[hashindex] = (po_string_t *)Mem_Alloc(pool, sizeof(thisstr));
1871 memcpy(po->hashtable[hashindex], &thisstr, sizeof(thisstr));
1872 memset(&thisstr, 0, sizeof(thisstr));
1876 Mem_Free((char *) buf);
1881 static const char *PRVM_PO_Lookup(po_t *po, const char *str)
1883 int hashindex = CRC_Block((const unsigned char *) str, strlen(str)) % PO_HASHSIZE;
1884 po_string_t *p = po->hashtable[hashindex];
1887 if(!strcmp(str, p->key))
1889 p = p->nextonhashchain;
1893 static void PRVM_PO_Destroy(po_t *po)
1896 for(i = 0; i < PO_HASHSIZE; ++i)
1898 po_string_t *p = po->hashtable[i];
1902 p = p->nextonhashchain;
1911 void PRVM_LeakTest(prvm_prog_t *prog);
1912 void PRVM_Prog_Reset(prvm_prog_t *prog)
1916 if(prog->tempstringsbuf.cursize)
1917 Mem_Free(prog->tempstringsbuf.data);
1918 prog->tempstringsbuf.cursize = 0;
1919 PRVM_LeakTest(prog);
1920 prog->reset_cmd(prog);
1921 Mem_FreePool(&prog->progs_mempool);
1923 PRVM_PO_Destroy((po_t *) prog->po);
1925 memset(prog,0,sizeof(prvm_prog_t));
1926 prog->break_statement = -1;
1927 prog->watch_global_type = ev_void;
1928 prog->watch_field_type = ev_void;
1936 static void PRVM_LoadLNO( prvm_prog_t *prog, const char *progname ) {
1937 fs_offset_t filesize;
1939 unsigned int *header;
1942 FS_StripExtension( progname, filename, sizeof( filename ) );
1943 strlcat( filename, ".lno", sizeof( filename ) );
1945 lno = FS_LoadFile( filename, tempmempool, false, &filesize );
1951 <Spike> SafeWrite (h, &lnotype, sizeof(int));
1952 <Spike> SafeWrite (h, &version, sizeof(int));
1953 <Spike> SafeWrite (h, &numglobaldefs, sizeof(int));
1954 <Spike> SafeWrite (h, &numpr_globals, sizeof(int));
1955 <Spike> SafeWrite (h, &numfielddefs, sizeof(int));
1956 <Spike> SafeWrite (h, &numstatements, sizeof(int));
1957 <Spike> SafeWrite (h, statement_linenums, numstatements*sizeof(int));
1959 if ((unsigned int)filesize < (6 + prog->progs_numstatements) * sizeof(int))
1965 header = (unsigned int *) lno;
1966 if( header[ 0 ] == *(unsigned int *) "LNOF" &&
1967 LittleLong( header[ 1 ] ) == 1 &&
1968 (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs_numglobaldefs &&
1969 (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs_numglobals &&
1970 (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs_numfielddefs &&
1971 (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs_numstatements )
1973 prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof( int ) );
1974 memcpy( prog->statement_linenums, header + 6, prog->progs_numstatements * sizeof( int ) );
1976 /* gmqcc suports columnums */
1977 if ((unsigned int)filesize > ((6 + 2 * prog->progs_numstatements) * sizeof( int )))
1979 prog->statement_columnnums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof( int ) );
1980 memcpy( prog->statement_columnnums, header + 6 + prog->progs_numstatements, prog->progs_numstatements * sizeof( int ) );
1991 static void PRVM_UpdateBreakpoints(prvm_prog_t *prog);
1992 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)
1995 dprograms_t *dprograms;
1996 dstatement_t *instatements;
1997 ddef_t *infielddefs;
1998 ddef_t *inglobaldefs;
2000 dfunction_t *infunctions;
2002 fs_offset_t filesize;
2003 int requiredglobalspace;
2020 prog->error_cmd("PRVM_LoadProgs: there is already a %s program loaded!", prog->name );
2022 Host_LockSession(); // all progs can use the session cvar
2023 Crypto_LoadKeys(); // all progs might use the keys at init time
2027 dprograms = (dprograms_t *) data;
2031 dprograms = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
2032 if (dprograms == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
2033 prog->error_cmd("PRVM_LoadProgs: couldn't load %s for %s", filename, prog->name);
2034 // TODO bounds check header fields (e.g. numstatements), they must never go behind end of file
2036 prog->profiletime = Sys_DirtyTime();
2037 prog->starttime = host.realtime;
2039 requiredglobalspace = 0;
2040 for (i = 0;i < numrequiredglobals;i++)
2041 requiredglobalspace += required_global[i].type == ev_vector ? 3 : 1;
2043 prog->filecrc = CRC_Block((unsigned char *)dprograms, filesize);
2045 // byte swap the header
2046 prog->progs_version = LittleLong(dprograms->version);
2047 prog->progs_crc = LittleLong(dprograms->crc);
2048 if (prog->progs_version != PROG_VERSION)
2049 prog->error_cmd("%s: %s has wrong version number (%i should be %i)", prog->name, filename, prog->progs_version, PROG_VERSION);
2050 instatements = (dstatement_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_statements));
2051 prog->progs_numstatements = LittleLong(dprograms->numstatements);
2052 inglobaldefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globaldefs));
2053 prog->progs_numglobaldefs = LittleLong(dprograms->numglobaldefs);
2054 infielddefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_fielddefs));
2055 prog->progs_numfielddefs = LittleLong(dprograms->numfielddefs);
2056 infunctions = (dfunction_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_functions));
2057 prog->progs_numfunctions = LittleLong(dprograms->numfunctions);
2058 instrings = (char *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_strings));
2059 prog->progs_numstrings = LittleLong(dprograms->numstrings);
2060 inglobals = (int *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globals));
2061 prog->progs_numglobals = LittleLong(dprograms->numglobals);
2062 prog->progs_entityfields = LittleLong(dprograms->entityfields);
2064 prog->numstatements = prog->progs_numstatements;
2065 prog->numglobaldefs = prog->progs_numglobaldefs;
2066 prog->numfielddefs = prog->progs_numfielddefs;
2067 prog->numfunctions = prog->progs_numfunctions;
2068 prog->numstrings = prog->progs_numstrings;
2069 prog->numglobals = prog->progs_numglobals;
2070 prog->entityfields = prog->progs_entityfields;
2072 if (LittleLong(dprograms->ofs_strings) + prog->progs_numstrings > (int)filesize)
2073 prog->error_cmd("%s: %s strings go past end of file", prog->name, filename);
2074 prog->strings = (char *)Mem_Alloc(prog->progs_mempool, prog->progs_numstrings);
2075 memcpy(prog->strings, instrings, prog->progs_numstrings);
2076 prog->stringssize = prog->progs_numstrings;
2078 prog->numknownstrings = 0;
2079 prog->maxknownstrings = 0;
2080 prog->knownstrings = NULL;
2081 prog->knownstrings_flags = NULL;
2083 Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
2085 // we need to expand the globaldefs and fielddefs to include engine defs
2086 prog->globaldefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobaldefs + numrequiredglobals) * sizeof(ddef_t));
2087 prog->globals.fp = (prvm_vec_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobals + requiredglobalspace + 2) * sizeof(prvm_vec_t));
2088 // + 2 is because of an otherwise occurring overrun in RETURN instruction
2089 // when trying to return the last or second-last global
2090 // (RETURN always returns a vector, there is no RETURN_F instruction)
2091 prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numfielddefs + numrequiredfields) * sizeof(ddef_t));
2092 // we need to convert the statements to our memory format
2093 prog->statements = (mstatement_t *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(mstatement_t));
2094 // allocate space for profiling statement usage
2095 prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(*prog->statement_profile));
2096 prog->explicit_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(*prog->statement_profile));
2097 // functions need to be converted to the memory format
2098 prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs_numfunctions);
2100 for (i = 0;i < prog->progs_numfunctions;i++)
2102 prog->functions[i].first_statement = LittleLong(infunctions[i].first_statement);
2103 prog->functions[i].parm_start = LittleLong(infunctions[i].parm_start);
2104 prog->functions[i].s_name = LittleLong(infunctions[i].s_name);
2105 prog->functions[i].s_file = LittleLong(infunctions[i].s_file);
2106 prog->functions[i].numparms = LittleLong(infunctions[i].numparms);
2107 prog->functions[i].locals = LittleLong(infunctions[i].locals);
2108 memcpy(prog->functions[i].parm_size, infunctions[i].parm_size, sizeof(infunctions[i].parm_size));
2109 if(prog->functions[i].first_statement >= prog->numstatements)
2110 prog->error_cmd("PRVM_LoadProgs: out of bounds function statement (function %d) in %s", i, prog->name);
2111 // TODO bounds check parm_start, s_name, s_file, numparms, locals, parm_size
2114 // copy the globaldefs to the new globaldefs list
2115 for (i=0 ; i<prog->numglobaldefs ; i++)
2117 prog->globaldefs[i].type = LittleShort(inglobaldefs[i].type);
2118 prog->globaldefs[i].ofs = LittleShort(inglobaldefs[i].ofs);
2119 prog->globaldefs[i].s_name = LittleLong(inglobaldefs[i].s_name);
2120 // TODO bounds check ofs, s_name
2123 // append the required globals
2124 for (i = 0;i < numrequiredglobals;i++)
2126 prog->globaldefs[prog->numglobaldefs].type = required_global[i].type;
2127 prog->globaldefs[prog->numglobaldefs].ofs = prog->numglobals;
2128 prog->globaldefs[prog->numglobaldefs].s_name = PRVM_SetEngineString(prog, required_global[i].name);
2129 if (prog->globaldefs[prog->numglobaldefs].type == ev_vector)
2130 prog->numglobals += 3;
2133 prog->numglobaldefs++;
2136 // copy the progs fields to the new fields list
2137 for (i = 0;i < prog->numfielddefs;i++)
2139 prog->fielddefs[i].type = LittleShort(infielddefs[i].type);
2140 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
2141 prog->error_cmd("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", prog->name);
2142 prog->fielddefs[i].ofs = LittleShort(infielddefs[i].ofs);
2143 prog->fielddefs[i].s_name = LittleLong(infielddefs[i].s_name);
2144 // TODO bounds check ofs, s_name
2147 // append the required fields
2148 for (i = 0;i < numrequiredfields;i++)
2150 prog->fielddefs[prog->numfielddefs].type = required_field[i].type;
2151 prog->fielddefs[prog->numfielddefs].ofs = prog->entityfields;
2152 prog->fielddefs[prog->numfielddefs].s_name = PRVM_SetEngineString(prog, required_field[i].name);
2153 if (prog->fielddefs[prog->numfielddefs].type == ev_vector)
2154 prog->entityfields += 3;
2156 prog->entityfields++;
2157 prog->numfielddefs++;
2160 // LadyHavoc: TODO: reorder globals to match engine struct
2161 // LadyHavoc: TODO: reorder fields to match engine struct
2162 #define remapglobal(index) (index)
2163 #define remapfield(index) (index)
2166 // FIXME: LadyHavoc: this uses a crude way to identify integer constants, rather than checking for matching globaldefs and checking their type
2167 for (i = 0;i < prog->progs_numglobals;i++)
2169 u.i = LittleLong(inglobals[i]);
2170 // most globals are 0, we only need to deal with the ones that are not
2173 d = u.i & 0xFF800000;
2174 if ((d == 0xFF800000) || (d == 0))
2176 // Looks like an integer (expand to int64)
2177 prog->globals.ip[remapglobal(i)] = u.i;
2181 // Looks like a float (expand to double)
2182 prog->globals.fp[remapglobal(i)] = u.f;
2187 // LadyHavoc: TODO: support 32bit progs statement formats
2188 // copy, remap globals in statements, bounds check
2189 for (i = 0;i < prog->progs_numstatements;i++)
2191 op = (opcode_t)LittleShort(instatements[i].op);
2192 a = (unsigned short)LittleShort(instatements[i].a);
2193 b = (unsigned short)LittleShort(instatements[i].b);
2194 c = (unsigned short)LittleShort(instatements[i].c);
2200 if (a >= prog->progs_numglobals || b + i < 0 || b + i >= prog->progs_numstatements)
2201 prog->error_cmd("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", i, prog->name);
2202 prog->statements[i].op = op;
2203 prog->statements[i].operand[0] = remapglobal(a);
2204 prog->statements[i].operand[1] = -1;
2205 prog->statements[i].operand[2] = -1;
2206 prog->statements[i].jumpabsolute = i + b;
2210 if (a + i < 0 || a + i >= prog->progs_numstatements)
2211 prog->error_cmd("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, prog->name);
2212 prog->statements[i].op = op;
2213 prog->statements[i].operand[0] = -1;
2214 prog->statements[i].operand[1] = -1;
2215 prog->statements[i].operand[2] = -1;
2216 prog->statements[i].jumpabsolute = i + a;
2219 Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", (int)op, i, prog->name);
2221 // global global global
2256 if (a >= prog->progs_numglobals || b >= prog->progs_numglobals || c >= prog->progs_numglobals)
2257 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
2258 prog->statements[i].op = op;
2259 prog->statements[i].operand[0] = remapglobal(a);
2260 prog->statements[i].operand[1] = remapglobal(b);
2261 prog->statements[i].operand[2] = remapglobal(c);
2262 prog->statements[i].jumpabsolute = -1;
2264 // global none global
2270 if (a >= prog->progs_numglobals || c >= prog->progs_numglobals)
2271 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2272 prog->statements[i].op = op;
2273 prog->statements[i].operand[0] = remapglobal(a);
2274 prog->statements[i].operand[1] = -1;
2275 prog->statements[i].operand[2] = remapglobal(c);
2276 prog->statements[i].jumpabsolute = -1;
2292 if (a >= prog->progs_numglobals || b >= prog->progs_numglobals)
2293 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2294 prog->statements[i].op = op;
2295 prog->statements[i].operand[0] = remapglobal(a);
2296 prog->statements[i].operand[1] = remapglobal(b);
2297 prog->statements[i].operand[2] = -1;
2298 prog->statements[i].jumpabsolute = -1;
2302 if ( a < prog->progs_numglobals)
2303 if ( prog->globals.ip[remapglobal(a)] >= 0 )
2304 if ( prog->globals.ip[remapglobal(a)] < prog->progs_numfunctions )
2305 if ( prog->functions[prog->globals.ip[remapglobal(a)]].first_statement == -642 )
2306 ++prog->numexplicitcoveragestatements;
2317 if ( a >= prog->progs_numglobals)
2318 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2319 prog->statements[i].op = op;
2320 prog->statements[i].operand[0] = remapglobal(a);
2321 prog->statements[i].operand[1] = -1;
2322 prog->statements[i].operand[2] = -1;
2323 prog->statements[i].jumpabsolute = -1;
2327 if(prog->numstatements < 1)
2329 prog->error_cmd("PRVM_LoadProgs: empty program in %s", prog->name);
2331 else switch(prog->statements[prog->numstatements - 1].op)
2338 prog->error_cmd("PRVM_LoadProgs: program may fall off the edge (does not end with RETURN, GOTO or DONE) in %s", prog->name);
2342 // we're done with the file now
2344 Mem_Free(dprograms);
2347 // check required functions
2348 for(i=0 ; i < numrequiredfunc ; i++)
2349 if(PRVM_ED_FindFunction(prog, required_func[i]) == 0)
2350 prog->error_cmd("%s: %s not found in %s",prog->name, required_func[i], filename);
2352 PRVM_LoadLNO(prog, filename);
2354 PRVM_Init_Exec(prog);
2356 if(*prvm_language.string)
2357 // in CSQC we really shouldn't be able to change how stuff works... sorry for now
2358 // later idea: include a list of authorized .po file checksums with the csprogs
2360 qboolean deftrans = prog == CLVM_prog;
2361 const char *realfilename = (prog != CLVM_prog ? filename : csqc_progname.string);
2362 if(deftrans) // once we have dotranslate_ strings, ALWAYS use the opt-in method!
2364 for (i=0 ; i<prog->numglobaldefs ; i++)
2367 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2368 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2369 if(name && !strncmp(name, "dotranslate_", 12))
2376 if(!strcmp(prvm_language.string, "dump"))
2378 qfile_t *f = FS_OpenRealFile(va(vabuf, sizeof(vabuf), "%s.pot", realfilename), "w", false);
2379 Con_Printf("Dumping to %s.pot\n", realfilename);
2382 for (i=0 ; i<prog->numglobaldefs ; i++)
2385 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2386 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2387 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2389 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2390 const char *value = PRVM_GetString(prog, val->string);
2393 char buf[MAX_INPUTLINE];
2394 PRVM_PO_UnparseString(buf, value, sizeof(buf));
2395 FS_Printf(f, "msgid \"%s\"\nmsgstr \"\"\n\n", buf);
2404 po_t *po = PRVM_PO_Load(
2405 va(vabuf, sizeof(vabuf), "%s.%s.po", realfilename, prvm_language.string),
2406 va(vabuf2, sizeof(vabuf2), "common.%s.po", prvm_language.string),
2407 prog->progs_mempool);
2410 for (i=0 ; i<prog->numglobaldefs ; i++)
2413 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2414 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2415 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2417 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2418 const char *value = PRVM_GetString(prog, val->string);
2421 value = PRVM_PO_Lookup(po, value);
2423 val->string = PRVM_SetEngineString(prog, value);
2431 for (cvar = prog->console_cmd->cvars->vars; cvar; cvar = cvar->next)
2432 cvar->globaldefindex[prog - prvm_prog_list] = -1;
2434 for (i=0 ; i<prog->numglobaldefs ; i++)
2437 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2438 //Con_Printf("found var %s\n", name);
2440 && !strncmp(name, "autocvar_", 9)
2441 && !(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
2444 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2445 cvar = Cvar_FindVar(prog->console_cmd->cvars, name + 9, prog->console_cmd->cvars_flagsmask);
2446 //Con_Printf("PRVM_LoadProgs: autocvar global %s in %s, processing...\n", name, prog->name);
2453 Con_DPrintf("PRVM_LoadProgs: no cvar for autocvar global %s in %s, creating...\n", name, prog->name);
2454 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2457 if((float)((int)(val->_float)) == val->_float)
2458 dpsnprintf(buf, sizeof(buf), "%i", (int)(val->_float));
2463 for (int precision = 7; precision <= 9; ++precision) {
2464 dpsnprintf(buf, sizeof(buf), "%.*g", precision, f);
2465 if ((float)atof(buf) == f) {
2473 for (i = 0; i < 3; ++i)
2477 for (int precision = 7; precision <= 9; ++precision) {
2478 dpsnprintf(buf, sizeof(buf), "%.*g", precision, f);
2479 if ((float)atof(buf) == f) {
2480 prec[i] = precision;
2485 dpsnprintf(buf, sizeof(buf), "%.*g %.*g %.*g", prec[0], val->vector[0], prec[1], val->vector[1], prec[2], val->vector[2]);
2489 value = PRVM_GetString(prog, val->string);
2492 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2495 cvar = Cvar_Get(prog->console_cmd->cvars, name + 9, value, prog->console_cmd->cvars_flagsmask, NULL);
2496 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2498 val->string = PRVM_SetEngineString(prog, cvar->string);
2499 cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2502 prog->error_cmd("PRVM_LoadProgs: could not create cvar for autocvar global %s in %s", name, prog->name);
2503 cvar->globaldefindex[prog - prvm_prog_list] = i;
2505 else if((cvar->flags & CVAR_PRIVATE) == 0)
2507 // MUST BE SYNCED WITH cvar.c Cvar_Set
2510 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2513 val->_float = cvar->value;
2517 VectorClear(val->vector);
2518 for (j = 0;j < 3;j++)
2520 while (*s && ISWHITESPACE(*s))
2524 val->vector[j] = atof(s);
2525 while (!ISWHITESPACE(*s))
2532 val->string = PRVM_SetEngineString(prog, cvar->string);
2533 cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2536 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2539 cvar->globaldefindex[prog - prvm_prog_list] = i;
2542 Con_Printf("PRVM_LoadProgs: private cvar for autocvar global %s in %s\n", name, prog->name);
2548 prog->loaded = true;
2550 PRVM_UpdateBreakpoints(prog);
2552 // set flags & ddef_ts in prog
2556 PRVM_FindOffsets(prog);
2558 prog->init_cmd(prog);
2561 PRVM_MEM_Alloc(prog);
2563 Con_Printf("%s: program loaded (crc %i, size %iK)\n", prog->name, prog->filecrc, (int)(filesize/1024));
2565 // Inittime is at least the time when this function finished. However,
2566 // later events may bump it.
2567 prog->inittime = host.realtime;
2571 static void PRVM_Fields_f(cmd_state_t *cmd)
2574 int i, j, ednum, used, usedamount;
2576 char tempstring[MAX_INPUTLINE], tempstring2[260];
2586 Con_Print("no progs loaded\n");
2591 if(Cmd_Argc(cmd) != 2)
2593 Con_Print("prvm_fields <program name>\n");
2597 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2600 counts = (int *)Mem_Alloc(tempmempool, prog->numfielddefs * sizeof(int));
2601 for (ednum = 0;ednum < prog->max_edicts;ednum++)
2603 ed = PRVM_EDICT_NUM(ednum);
2604 if (ed->priv.required->free)
2606 for (i = 1;i < prog->numfielddefs;i++)
2608 d = &prog->fielddefs[i];
2609 name = PRVM_GetString(prog, d->s_name);
2610 if (name[strlen(name)-2] == '_')
2611 continue; // skip _x, _y, _z vars
2612 val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
2613 // if the value is still all 0, skip the field
2614 for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
2616 if (val->ivector[j])
2627 for (i = 0;i < prog->numfielddefs;i++)
2629 d = &prog->fielddefs[i];
2630 name = PRVM_GetString(prog, d->s_name);
2631 if (name[strlen(name)-2] == '_')
2632 continue; // skip _x, _y, _z vars
2633 switch(d->type & ~DEF_SAVEGLOBAL)
2636 strlcat(tempstring, "string ", sizeof(tempstring));
2639 strlcat(tempstring, "entity ", sizeof(tempstring));
2642 strlcat(tempstring, "function ", sizeof(tempstring));
2645 strlcat(tempstring, "field ", sizeof(tempstring));
2648 strlcat(tempstring, "void ", sizeof(tempstring));
2651 strlcat(tempstring, "float ", sizeof(tempstring));
2654 strlcat(tempstring, "vector ", sizeof(tempstring));
2657 strlcat(tempstring, "pointer ", sizeof(tempstring));
2660 dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
2661 strlcat(tempstring, tempstring2, sizeof(tempstring));
2664 if (strlen(name) > sizeof(tempstring2)-4)
2666 memcpy (tempstring2, name, sizeof(tempstring2)-4);
2667 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
2668 tempstring2[sizeof(tempstring2)-1] = 0;
2671 strlcat(tempstring, name, sizeof(tempstring));
2672 for (j = (int)strlen(name);j < 25;j++)
2673 strlcat(tempstring, " ", sizeof(tempstring));
2674 dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
2675 strlcat(tempstring, tempstring2, sizeof(tempstring));
2676 strlcat(tempstring, "\n", sizeof(tempstring));
2677 if (strlen(tempstring) >= sizeof(tempstring)/2)
2679 Con_Print(tempstring);
2685 usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
2689 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);
2692 static void PRVM_Globals_f(cmd_state_t *cmd)
2696 const char *wildcard;
2702 Con_Print("no progs loaded\n");
2705 if(Cmd_Argc (cmd) < 2 || Cmd_Argc(cmd) > 3)
2707 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
2711 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2714 if( Cmd_Argc(cmd) == 3)
2715 wildcard = Cmd_Argv(cmd, 2);
2719 Con_Printf("%s :", prog->name);
2721 for (i = 0;i < prog->numglobaldefs;i++)
2724 if( !matchpattern( PRVM_GetString(prog, prog->globaldefs[i].s_name), wildcard, 1) )
2729 Con_Printf("%s\n", PRVM_GetString(prog, prog->globaldefs[i].s_name));
2731 Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->numglobals, numculled, prog->numglobals * 4);
2739 static void PRVM_Global_f(cmd_state_t *cmd)
2743 char valuebuf[MAX_INPUTLINE];
2744 if( Cmd_Argc(cmd) != 3 ) {
2745 Con_Printf( "prvm_global <program name> <global name>\n" );
2749 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2752 global = PRVM_ED_FindGlobal( prog, Cmd_Argv(cmd, 2) );
2754 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(cmd, 2), Cmd_Argv(cmd, 1) );
2756 Con_Printf( "%s: %s\n", Cmd_Argv(cmd, 2), PRVM_ValueString( prog, (etype_t)global->type, PRVM_GLOBALFIELDVALUE(global->ofs), valuebuf, sizeof(valuebuf) ) );
2764 static void PRVM_GlobalSet_f(cmd_state_t *cmd)
2768 if( Cmd_Argc(cmd) != 4 ) {
2769 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2773 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2776 global = PRVM_ED_FindGlobal( prog, Cmd_Argv(cmd, 2) );
2778 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(cmd, 2), Cmd_Argv(cmd, 1) );
2780 PRVM_ED_ParseEpair( prog, NULL, global, Cmd_Argv(cmd, 3), true );
2784 ======================
2785 Break- and Watchpoints
2786 ======================
2790 char break_statement[256];
2791 char watch_global[256];
2793 char watch_field[256];
2796 static debug_data_t debug_data[PRVM_PROG_MAX];
2798 void PRVM_Breakpoint(prvm_prog_t *prog, int stack_index, const char *text)
2801 Con_Printf("PRVM_Breakpoint: %s\n", text);
2802 PRVM_PrintState(prog, stack_index);
2803 if (prvm_breakpointdump.integer)
2804 SV_Savegame_to(prog, va(vabuf, sizeof(vabuf), "breakpoint-%s.dmp", prog->name));
2807 void PRVM_Watchpoint(prvm_prog_t *prog, int stack_index, const char *text, etype_t type, prvm_eval_t *o, prvm_eval_t *n)
2809 size_t sz = sizeof(prvm_vec_t) * ((type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2810 if (memcmp(o, n, sz))
2813 char valuebuf_o[128];
2814 char valuebuf_n[128];
2815 PRVM_UglyValueString(prog, type, o, valuebuf_o, sizeof(valuebuf_o));
2816 PRVM_UglyValueString(prog, type, n, valuebuf_n, sizeof(valuebuf_n));
2817 dpsnprintf(buf, sizeof(buf), "%s: %s -> %s", text, valuebuf_o, valuebuf_n);
2818 PRVM_Breakpoint(prog, stack_index, buf);
2823 static void PRVM_UpdateBreakpoints(prvm_prog_t *prog)
2825 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2828 if (debug->break_statement[0])
2830 if (debug->break_statement[0] >= '0' && debug->break_statement[0] <= '9')
2832 prog->break_statement = atoi(debug->break_statement);
2833 prog->break_stack_index = 0;
2838 func = PRVM_ED_FindFunction (prog, debug->break_statement);
2841 Con_Printf("%s progs: no function or statement named %s to break on!\n", prog->name, debug->break_statement);
2842 prog->break_statement = -1;
2846 prog->break_statement = func->first_statement;
2847 prog->break_stack_index = 1;
2850 if (prog->break_statement >= -1)
2851 Con_Printf("%s progs: breakpoint is at statement %d\n", prog->name, prog->break_statement);
2854 prog->break_statement = -1;
2856 if (debug->watch_global[0])
2858 ddef_t *global = PRVM_ED_FindGlobal( prog, debug->watch_global );
2861 Con_Printf( "%s progs: no global named '%s' to watch!\n", prog->name, debug->watch_global );
2862 prog->watch_global_type = ev_void;
2866 size_t sz = sizeof(prvm_vec_t) * ((global->type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2867 prog->watch_global = global->ofs;
2868 prog->watch_global_type = (etype_t)global->type;
2869 memcpy(&prog->watch_global_value, PRVM_GLOBALFIELDVALUE(prog->watch_global), sz);
2871 if (prog->watch_global_type != ev_void)
2872 Con_Printf("%s progs: global watchpoint is at global index %d\n", prog->name, prog->watch_global);
2875 prog->watch_global_type = ev_void;
2877 if (debug->watch_field[0])
2879 ddef_t *field = PRVM_ED_FindField( prog, debug->watch_field );
2882 Con_Printf( "%s progs: no field named '%s' to watch!\n", prog->name, debug->watch_field );
2883 prog->watch_field_type = ev_void;
2887 size_t sz = sizeof(prvm_vec_t) * ((field->type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2888 prog->watch_edict = debug->watch_edict;
2889 prog->watch_field = field->ofs;
2890 prog->watch_field_type = (etype_t)field->type;
2891 if (prog->watch_edict < prog->num_edicts)
2892 memcpy(&prog->watch_edictfield_value, PRVM_EDICTFIELDVALUE(PRVM_EDICT_NUM(prog->watch_edict), prog->watch_field), sz);
2894 memset(&prog->watch_edictfield_value, 0, sz);
2896 if (prog->watch_edict != ev_void)
2897 Con_Printf("%s progs: edict field watchpoint is at edict %d field index %d\n", prog->name, prog->watch_edict, prog->watch_field);
2900 prog->watch_field_type = ev_void;
2903 static void PRVM_Breakpoint_f(cmd_state_t *cmd)
2907 if( Cmd_Argc(cmd) == 2 ) {
2908 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2911 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2912 debug->break_statement[0] = 0;
2914 PRVM_UpdateBreakpoints(prog);
2917 if( Cmd_Argc(cmd) != 3 ) {
2918 Con_Printf( "prvm_breakpoint <program name> <function name | statement>\n" );
2922 if (!(prog = PRVM_ProgFromString(Cmd_Argv(cmd, 1))))
2926 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2927 strlcpy(debug->break_statement, Cmd_Argv(cmd, 2), sizeof(debug->break_statement));
2929 PRVM_UpdateBreakpoints(prog);
2932 static void PRVM_GlobalWatchpoint_f(cmd_state_t *cmd)
2936 if( Cmd_Argc(cmd) == 2 ) {
2937 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2940 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2941 debug->watch_global[0] = 0;
2943 PRVM_UpdateBreakpoints(prog);
2946 if( Cmd_Argc(cmd) != 3 ) {
2947 Con_Printf( "prvm_globalwatchpoint <program name> <global name>\n" );
2951 if (!(prog = PRVM_ProgFromString(Cmd_Argv(cmd, 1))))
2955 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2956 strlcpy(debug->watch_global, Cmd_Argv(cmd, 2), sizeof(debug->watch_global));
2958 PRVM_UpdateBreakpoints(prog);
2961 static void PRVM_EdictWatchpoint_f(cmd_state_t *cmd)
2965 if( Cmd_Argc(cmd) == 2 ) {
2966 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2969 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2970 debug->watch_field[0] = 0;
2972 PRVM_UpdateBreakpoints(prog);
2975 if( Cmd_Argc(cmd) != 4 ) {
2976 Con_Printf( "prvm_edictwatchpoint <program name> <edict number> <field name>\n" );
2980 if (!(prog = PRVM_ProgFromString(Cmd_Argv(cmd, 1))))
2984 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2985 debug->watch_edict = atoi(Cmd_Argv(cmd, 2));
2986 strlcpy(debug->watch_field, Cmd_Argv(cmd, 3), sizeof(debug->watch_field));
2988 PRVM_UpdateBreakpoints(prog);
2996 void PRVM_Init (void)
2998 Cmd_AddCommand(CMD_SHARED, "prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2999 Cmd_AddCommand(CMD_SHARED, "prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
3000 Cmd_AddCommand(CMD_SHARED, "prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
3001 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)");
3002 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");
3003 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)");
3004 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)");
3005 Cmd_AddCommand(CMD_SHARED, "prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
3006 Cmd_AddCommand(CMD_SHARED, "prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
3007 Cmd_AddCommand(CMD_SHARED, "prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
3008 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)");
3009 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");
3010 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");
3011 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)");
3012 Cmd_AddCommand(CMD_SHARED, "cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
3013 Cmd_AddCommand(CMD_SHARED, "menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
3014 Cmd_AddCommand(CMD_SHARED, "sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
3015 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");
3016 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");
3017 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");
3019 Cvar_RegisterVariable (&prvm_language);
3020 Cvar_RegisterVariable (&prvm_traceqc);
3021 Cvar_RegisterVariable (&prvm_statementprofiling);
3022 Cvar_RegisterVariable (&prvm_timeprofiling);
3023 Cvar_RegisterVariable (&prvm_coverage);
3024 Cvar_RegisterVariable (&prvm_backtraceforwarnings);
3025 Cvar_RegisterVariable (&prvm_leaktest);
3026 Cvar_RegisterVariable (&prvm_leaktest_follow_targetname);
3027 Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames);
3028 Cvar_RegisterVariable (&prvm_errordump);
3029 Cvar_RegisterVariable (&prvm_breakpointdump);
3030 Cvar_RegisterVariable (&prvm_reuseedicts_startuptime);
3031 Cvar_RegisterVariable (&prvm_reuseedicts_neverinsameframe);
3032 Cvar_RegisterVariable (&prvm_garbagecollection_enable);
3033 Cvar_RegisterVariable (&prvm_garbagecollection_notify);
3034 Cvar_RegisterVariable (&prvm_garbagecollection_scan_limit);
3035 Cvar_RegisterVariable (&prvm_garbagecollection_strings);
3036 Cvar_RegisterVariable (&prvm_stringdebug);
3038 // COMMANDLINEOPTION: PRVM: -norunaway disables the runaway loop check (it might be impossible to exit DarkPlaces if used!)
3039 prvm_runawaycheck = !COM_CheckParm("-norunaway");
3049 void PRVM_Prog_Init(prvm_prog_t *prog, cmd_state_t *cmd)
3051 PRVM_Prog_Reset(prog);
3052 prog->leaktest_active = prvm_leaktest.integer != 0;
3053 prog->console_cmd = cmd;
3056 // LadyHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
3057 unsigned int PRVM_EDICT_NUM_ERROR(prvm_prog_t *prog, unsigned int n, const char *filename, int fileline)
3059 prog->error_cmd("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", prog->name, n, filename, fileline);
3063 #define PRVM_KNOWNSTRINGBASE 0x40000000
3065 const char *PRVM_GetString(prvm_prog_t *prog, int num)
3070 if (prvm_stringdebug.integer)
3071 VM_Warning(prog, "PRVM_GetString: Invalid string offset (%i < 0)\n", num);
3074 else if (num < prog->stringssize)
3076 // constant string from progs.dat
3077 return prog->strings + num;
3079 else if (num <= prog->stringssize + prog->tempstringsbuf.maxsize)
3081 // tempstring returned by engine to QC (becomes invalid after returning to engine)
3082 num -= prog->stringssize;
3083 if (num < prog->tempstringsbuf.cursize)
3084 return (char *)prog->tempstringsbuf.data + num;
3087 if (prvm_stringdebug.integer)
3088 VM_Warning(prog, "PRVM_GetString: Invalid temp-string offset (%i >= %i prog->tempstringsbuf.cursize)\n", num, prog->tempstringsbuf.cursize);
3092 else if (num & PRVM_KNOWNSTRINGBASE)
3095 num = num - PRVM_KNOWNSTRINGBASE;
3096 if (num >= 0 && num < prog->numknownstrings)
3098 if (!prog->knownstrings[num])
3100 if (prvm_stringdebug.integer)
3101 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
3104 // refresh the garbage collection on the string - this guards
3105 // against a certain sort of repeated migration to earlier
3106 // points in the scan that could otherwise result in the string
3107 // being freed for being unused
3108 prog->knownstrings_flags[num] = (prog->knownstrings_flags[num] & ~KNOWNSTRINGFLAG_GCPRUNE) | KNOWNSTRINGFLAG_GCMARK;
3109 return prog->knownstrings[num];
3113 if (prvm_stringdebug.integer)
3114 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
3120 // invalid string offset
3121 if (prvm_stringdebug.integer)
3122 VM_Warning(prog, "PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
3127 const char *PRVM_ChangeEngineString(prvm_prog_t *prog, int i, const char *s)
3130 i = i - PRVM_KNOWNSTRINGBASE;
3131 if (i < 0 || i >= prog->numknownstrings)
3132 prog->error_cmd("PRVM_ChangeEngineString: string index %i is out of bounds", i);
3133 else if ((prog->knownstrings_flags[i] & KNOWNSTRINGFLAG_ENGINE) == 0)
3134 prog->error_cmd("PRVM_ChangeEngineString: string index %i is not an engine string", i);
3135 old = prog->knownstrings[i];
3136 prog->knownstrings[i] = s;
3140 static void PRVM_NewKnownString(prvm_prog_t *prog, int i, int flags, const char *s)
3142 if (i >= prog->numknownstrings)
3144 if (i >= prog->maxknownstrings)
3146 const char **oldstrings = prog->knownstrings;
3147 const unsigned char *oldstrings_flags = prog->knownstrings_flags;
3148 const char **oldstrings_origin = prog->knownstrings_origin;
3149 prog->maxknownstrings += 128;
3150 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3151 prog->knownstrings_flags = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
3152 if (prog->leaktest_active)
3153 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3154 if (prog->numknownstrings)
3156 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
3157 memcpy((char **)prog->knownstrings_flags, oldstrings_flags, prog->numknownstrings * sizeof(unsigned char));
3158 if (prog->leaktest_active)
3159 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
3162 prog->numknownstrings++;
3164 prog->firstfreeknownstring = i + 1;
3165 prog->knownstrings[i] = s;
3166 // it's in use right now, spare it until the next gc pass - that said, it is not freeable so this is probably moot
3167 prog->knownstrings_flags[i] = flags;
3168 if (prog->leaktest_active)
3169 prog->knownstrings_origin[i] = NULL;
3172 int PRVM_SetEngineString(prvm_prog_t *prog, const char *s)
3177 if (s >= prog->strings && s <= prog->strings + prog->stringssize)
3178 prog->error_cmd("PRVM_SetEngineString: s in prog->strings area");
3179 // if it's in the tempstrings area, use a reserved range
3180 // (otherwise we'd get millions of useless string offsets cluttering the database)
3181 if (s >= (char *)prog->tempstringsbuf.data && s < (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.maxsize)
3182 return prog->stringssize + (s - (char *)prog->tempstringsbuf.data);
3183 // see if it's a known string address
3184 for (i = 0;i < prog->numknownstrings;i++)
3185 if (prog->knownstrings[i] == s)
3186 return PRVM_KNOWNSTRINGBASE + i;
3187 // new unknown engine string
3188 if (developer_insane.integer)
3189 Con_DPrintf("new engine string %p = \"%s\"\n", (void *)s, s);
3190 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3191 if (!prog->knownstrings[i])
3193 PRVM_NewKnownString(prog, i, KNOWNSTRINGFLAG_GCMARK | KNOWNSTRINGFLAG_ENGINE, s);
3194 return PRVM_KNOWNSTRINGBASE + i;
3197 // temp string handling
3199 // all tempstrings go into this buffer consecutively, and it is reset
3200 // whenever PRVM_ExecuteProgram returns to the engine
3201 // (technically each PRVM_ExecuteProgram call saves the cursize value and
3202 // restores it on return, so multiple recursive calls can share the same
3204 // the buffer size is automatically grown as needed
3206 int PRVM_SetTempString(prvm_prog_t *prog, const char *s)
3212 size = (int)strlen(s) + 1;
3213 if (developer_insane.integer)
3214 Con_DPrintf("PRVM_SetTempString: cursize %i, size %i\n", prog->tempstringsbuf.cursize, size);
3215 if (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
3217 sizebuf_t old = prog->tempstringsbuf;
3218 if (prog->tempstringsbuf.cursize + size >= 1<<28)
3219 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);
3220 prog->tempstringsbuf.maxsize = max(prog->tempstringsbuf.maxsize, 65536);
3221 while (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
3222 prog->tempstringsbuf.maxsize *= 2;
3223 if (prog->tempstringsbuf.maxsize != old.maxsize || prog->tempstringsbuf.data == NULL)
3225 Con_DPrintf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, prog->tempstringsbuf.maxsize/1024);
3226 prog->tempstringsbuf.data = (unsigned char *) Mem_Alloc(prog->progs_mempool, prog->tempstringsbuf.maxsize);
3230 memcpy(prog->tempstringsbuf.data, old.data, old.cursize);
3235 t = (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.cursize;
3237 prog->tempstringsbuf.cursize += size;
3238 return PRVM_SetEngineString(prog, t);
3241 int PRVM_AllocString(prvm_prog_t *prog, size_t bufferlength, char **pointer)
3251 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3252 if (!prog->knownstrings[i])
3254 s = (char *)PRVM_Alloc(bufferlength);
3255 PRVM_NewKnownString(prog, i, KNOWNSTRINGFLAG_GCMARK, s);
3256 if(prog->leaktest_active)
3257 prog->knownstrings_origin[i] = PRVM_AllocationOrigin(prog);
3259 *pointer = (char *)(prog->knownstrings[i]);
3260 return PRVM_KNOWNSTRINGBASE + i;
3263 void PRVM_FreeString(prvm_prog_t *prog, int num)
3266 prog->error_cmd("PRVM_FreeString: attempt to free a NULL string");
3267 else if (num >= 0 && num < prog->stringssize)
3268 prog->error_cmd("PRVM_FreeString: attempt to free a constant string");
3269 else if (num >= PRVM_KNOWNSTRINGBASE && num < PRVM_KNOWNSTRINGBASE + prog->numknownstrings)
3271 num = num - PRVM_KNOWNSTRINGBASE;
3272 if (!prog->knownstrings[num])
3273 prog->error_cmd("PRVM_FreeString: attempt to free a non-existent or already freed string");
3274 if (!prog->knownstrings_flags[num])
3275 prog->error_cmd("PRVM_FreeString: attempt to free a string owned by the engine");
3276 PRVM_Free((char *)prog->knownstrings[num]);
3277 if(prog->leaktest_active)
3278 if(prog->knownstrings_origin[num])
3279 PRVM_Free((char *)prog->knownstrings_origin[num]);
3280 prog->knownstrings[num] = NULL;
3281 prog->knownstrings_flags[num] = 0;
3282 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
3285 prog->error_cmd("PRVM_FreeString: invalid string offset %i", num);
3288 static qboolean PRVM_IsStringReferenced(prvm_prog_t *prog, string_t string)
3292 for (i = 0;i < prog->numglobaldefs;i++)
3294 ddef_t *d = &prog->globaldefs[i];
3295 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3297 if(string == PRVM_GLOBALFIELDSTRING(d->ofs))
3301 for(j = 0; j < prog->num_edicts; ++j)
3303 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3304 if (ed->priv.required->free)
3306 for (i=0; i<prog->numfielddefs; ++i)
3308 ddef_t *d = &prog->fielddefs[i];
3309 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3311 if(string == PRVM_EDICTFIELDSTRING(ed, d->ofs))
3319 static qboolean PRVM_IsEdictRelevant(prvm_prog_t *prog, prvm_edict_t *edict)
3323 if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
3324 return true; // world or clients
3325 if (edict->priv.required->freetime <= prog->inittime)
3326 return true; // created during startup
3327 if (prog == SVVM_prog)
3329 if(PRVM_serveredictfloat(edict, solid)) // can block other stuff, or is a trigger?
3331 if(PRVM_serveredictfloat(edict, modelindex)) // visible ent?
3333 if(PRVM_serveredictfloat(edict, effects)) // particle effect?
3335 if(PRVM_serveredictfunction(edict, think)) // has a think function?
3336 if(PRVM_serveredictfloat(edict, nextthink) > 0) // that actually will eventually run?
3338 if(PRVM_serveredictfloat(edict, takedamage))
3340 if(*prvm_leaktest_ignore_classnames.string)
3342 if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_serveredictstring(edict, classname)))))
3346 else if (prog == CLVM_prog)
3348 // TODO someone add more stuff here
3349 if(PRVM_clientedictfloat(edict, entnum)) // csqc networked
3351 if(PRVM_clientedictfloat(edict, modelindex)) // visible ent?
3353 if(PRVM_clientedictfloat(edict, effects)) // particle effect?
3355 if(PRVM_clientedictfunction(edict, think)) // has a think function?
3356 if(PRVM_clientedictfloat(edict, nextthink) > 0) // that actually will eventually run?
3358 if(*prvm_leaktest_ignore_classnames.string)
3360 if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_clientedictstring(edict, classname)))))
3366 // menu prog does not have classnames
3371 static qboolean PRVM_IsEdictReferenced(prvm_prog_t *prog, prvm_edict_t *edict, int mark)
3374 int edictnum = PRVM_NUM_FOR_EDICT(edict);
3375 const char *targetname = NULL;
3377 if (prog == SVVM_prog && prvm_leaktest_follow_targetname.integer)
3378 targetname = PRVM_GetString(prog, PRVM_serveredictstring(edict, targetname));
3381 if(!*targetname) // ""
3384 for(j = 0; j < prog->num_edicts; ++j)
3386 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3387 if (ed->priv.required->mark < mark)
3393 const char *target = PRVM_GetString(prog, PRVM_serveredictstring(ed, target));
3395 if(!strcmp(target, targetname))
3398 for (i=0; i<prog->numfielddefs; ++i)
3400 ddef_t *d = &prog->fielddefs[i];
3401 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3403 if(edictnum == PRVM_EDICTFIELDEDICT(ed, d->ofs))
3411 static void PRVM_MarkReferencedEdicts(prvm_prog_t *prog)
3417 // Stage 1: world, all entities that are relevant, and all entities that are referenced by globals.
3419 for(j = 0; j < prog->num_edicts; ++j)
3421 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3422 if(ed->priv.required->free)
3424 ed->priv.required->mark = PRVM_IsEdictRelevant(prog, ed) ? stage : 0;
3426 for (i = 0;i < prog->numglobaldefs;i++)
3428 ddef_t *d = &prog->globaldefs[i];
3430 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3432 j = PRVM_GLOBALFIELDEDICT(d->ofs);
3433 if (i < 0 || j >= prog->max_edicts) {
3434 Con_Printf("Invalid entity reference from global %s.\n", PRVM_GetString(prog, d->s_name));
3437 ed = PRVM_EDICT_NUM(j);;
3438 ed->priv.required->mark = stage;
3441 // Future stages: all entities that are referenced by an entity of the previous stage.
3445 for(j = 0; j < prog->num_edicts; ++j)
3447 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3448 if(ed->priv.required->free)
3450 if(ed->priv.required->mark)
3452 if(PRVM_IsEdictReferenced(prog, ed, stage))
3454 ed->priv.required->mark = stage + 1;
3461 Con_DPrintf("leak check used %d stages to find all references\n", stage);
3464 void PRVM_LeakTest(prvm_prog_t *prog)
3467 qboolean leaked = false;
3469 if(!prog->leaktest_active)
3473 for (i = 0; i < prog->numknownstrings; ++i)
3475 if(prog->knownstrings[i])
3476 if(prog->knownstrings_flags[i])
3477 if(prog->knownstrings_origin[i])
3478 if(!PRVM_IsStringReferenced(prog, PRVM_KNOWNSTRINGBASE + i))
3480 Con_Printf("Unreferenced string found!\n Value: %s\n Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
3486 PRVM_MarkReferencedEdicts(prog);
3487 for(j = 0; j < prog->num_edicts; ++j)
3489 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3490 if(ed->priv.required->free)
3492 if(!ed->priv.required->mark)
3493 if(ed->priv.required->allocation_origin)
3495 Con_Printf("Unreferenced edict found!\n Allocated at: %s\n", ed->priv.required->allocation_origin);
3496 PRVM_ED_Print(prog, ed, NULL);
3501 ed->priv.required->mark = 0; // clear marks again when done
3504 for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
3506 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
3508 if(stringbuffer->origin)
3510 Con_Printf("Open string buffer handle found!\n Allocated at: %s\n", stringbuffer->origin);
3515 for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
3517 if(prog->openfiles[i])
3518 if(prog->openfiles_origin[i])
3520 Con_Printf("Open file handle found!\n Allocated at: %s\n", prog->openfiles_origin[i]);
3525 for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
3527 if(prog->opensearches[i])
3528 if(prog->opensearches_origin[i])
3530 Con_Printf("Open search handle found!\n Allocated at: %s\n", prog->opensearches_origin[i]);
3536 Con_Printf("Congratulations. No leaks found.\n");
3539 void PRVM_GarbageCollection(prvm_prog_t *prog)
3541 int limit = prvm_garbagecollection_scan_limit.integer;
3542 prvm_prog_garbagecollection_state_t *gc = &prog->gc;
3543 if (!prvm_garbagecollection_enable.integer)
3546 // we like to limit how much scanning we do so it doesn't put a significant
3547 // burden on the cpu, so each of these are not complete scans, we also like
3548 // to have consistent cpu usage so we do a bit of work on each category of
3549 // leaked object every frame
3555 case PRVM_GC_GLOBALS_MARK:
3556 for (; gc->globals_mark_progress < prog->numglobaldefs && (limit--) > 0; gc->globals_mark_progress++)
3558 ddef_t *d = &prog->globaldefs[gc->globals_mark_progress];
3563 prvm_int_t s = prog->globals.ip[d->ofs];
3564 if (s & PRVM_KNOWNSTRINGBASE)
3566 prvm_int_t num = s - PRVM_KNOWNSTRINGBASE;
3567 if (!prog->knownstrings[num])
3570 Con_DPrintf("PRVM_GarbageCollection: Found bogus strzone reference in global %i (global name: \"%s\"), erasing reference", d->ofs, PRVM_GetString(prog, d->s_name));
3571 prog->globals.ip[d->ofs] = 0;
3574 prog->knownstrings_flags[num] = (prog->knownstrings_flags[num] | KNOWNSTRINGFLAG_GCMARK) & ~KNOWNSTRINGFLAG_GCPRUNE;
3582 if (gc->globals_mark_progress >= prog->numglobaldefs)
3585 case PRVM_GC_FIELDS_MARK:
3586 for (; gc->fields_mark_progress < prog->numfielddefs && limit > 0;)
3588 ddef_t *d = &prog->fielddefs[gc->fields_mark_progress];
3592 //for (gc-> entityindex = 0; entityindex < prog->num_edicts; entityindex++)
3593 for (;gc->fields_mark_progress_entity < prog->num_edicts && (limit--) > 0;gc->fields_mark_progress_entity++)
3595 int entityindex = gc->fields_mark_progress_entity;
3596 prvm_int_t s = prog->edictsfields.ip[entityindex * prog->entityfields + d->ofs];
3597 if (s & PRVM_KNOWNSTRINGBASE)
3599 prvm_int_t num = s - PRVM_KNOWNSTRINGBASE;
3600 if (!prog->knownstrings[num])
3603 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));
3604 prog->edictsfields.ip[entityindex * prog->entityfields + d->ofs] = 0;
3607 prog->knownstrings_flags[num] = (prog->knownstrings_flags[num] | KNOWNSTRINGFLAG_GCMARK) & ~KNOWNSTRINGFLAG_GCPRUNE;
3610 if (gc->fields_mark_progress_entity >= prog->num_edicts)
3612 gc->fields_mark_progress_entity = 0;
3613 gc->fields_mark_progress++;
3617 gc->fields_mark_progress_entity = 0;
3618 gc->fields_mark_progress++;
3622 if (gc->fields_mark_progress >= prog->numfielddefs)
3625 case PRVM_GC_KNOWNSTRINGS_SWEEP:
3626 // free any strzone'd strings that are not marked
3627 if (!prvm_garbagecollection_strings.integer)
3632 for (;gc->knownstrings_sweep_progress < prog->numknownstrings && (limit--) > 0;gc->knownstrings_sweep_progress++)
3634 int num = gc->knownstrings_sweep_progress;
3635 if (prog->knownstrings[num] && (prog->knownstrings_flags[num] & (KNOWNSTRINGFLAG_GCMARK | KNOWNSTRINGFLAG_ENGINE)) == 0)
3637 if (prog->knownstrings_flags[num] & KNOWNSTRINGFLAG_GCPRUNE)
3639 // string has been marked for pruning two passes in a row
3640 if (prvm_garbagecollection_notify.integer)
3641 Con_DPrintf("prvm_garbagecollection_notify: %s: freeing unreferenced string %i: \"%s\"\n", prog->name, num, prog->knownstrings[num]);
3642 Mem_Free((char *)prog->knownstrings[num]);
3643 prog->knownstrings[num] = NULL;
3644 prog->knownstrings_flags[num] = 0;
3645 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
3649 // mark it for pruning next pass
3650 prog->knownstrings_flags[num] |= KNOWNSTRINGFLAG_GCPRUNE;
3654 if (gc->knownstrings_sweep_progress >= prog->numknownstrings)
3659 memset(gc, 0, sizeof(*gc));