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 = 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 == realtime)
242 if(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(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 = 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))
404 mfunction_t *PRVM_ED_FindFunction (prvm_prog_t *prog, const char *name)
409 for (i = 0;i < prog->numfunctions;i++)
411 func = &prog->functions[i];
412 if (!strcmp(PRVM_GetString(prog, func->s_name), name))
423 Returns a string describing *data in a type specific manner
426 static char *PRVM_ValueString (prvm_prog_t *prog, etype_t type, prvm_eval_t *val, char *line, size_t linelength)
432 type = (etype_t)((int) type & ~DEF_SAVEGLOBAL);
437 strlcpy (line, PRVM_GetString (prog, val->string), linelength);
441 if (n < 0 || n >= prog->max_edicts)
442 dpsnprintf (line, linelength, "entity %i (invalid!)", n);
444 dpsnprintf (line, linelength, "entity %i", n);
447 if ((unsigned int)val->function < (unsigned int)prog->progs_numfunctions)
449 f = prog->functions + val->function;
450 dpsnprintf (line, linelength, "%s()", PRVM_GetString(prog, f->s_name));
453 dpsnprintf (line, linelength, "function%lli() (invalid!)", val->function);
456 def = PRVM_ED_FieldAtOfs ( prog, val->_int );
458 dpsnprintf (line, linelength, ".%s", PRVM_GetString(prog, def->s_name));
460 dpsnprintf (line, linelength, "field%lli (invalid!)", val->_int );
463 dpsnprintf (line, linelength, "void");
466 // LadyHavoc: changed from %5.1f to %10.4f
467 dpsnprintf (line, linelength, FLOAT_LOSSLESS_FORMAT, val->_float);
470 // LadyHavoc: changed from %5.1f to %10.4f
471 dpsnprintf (line, linelength, "'" VECTOR_LOSSLESS_FORMAT "'", val->vector[0], val->vector[1], val->vector[2]);
474 dpsnprintf (line, linelength, "pointer");
477 dpsnprintf (line, linelength, "bad type %i", (int) type);
488 Returns a string describing *data in a type specific manner
489 Easier to parse than PR_ValueString
492 char *PRVM_UglyValueString (prvm_prog_t *prog, etype_t type, prvm_eval_t *val, char *line, size_t linelength)
499 type = (etype_t)((int)type & ~DEF_SAVEGLOBAL);
504 // Parse the string a bit to turn special characters
505 // (like newline, specifically) into escape codes,
506 // this fixes saving games from various mods
507 s = PRVM_GetString (prog, val->string);
508 for (i = 0;i < (int)linelength - 2 && *s;)
538 dpsnprintf (line, linelength, "%i", i);
541 if ((unsigned int)val->function < (unsigned int)prog->progs_numfunctions)
543 f = prog->functions + val->function;
544 strlcpy (line, PRVM_GetString (prog, f->s_name), linelength);
547 dpsnprintf (line, linelength, "bad function %lli (invalid!)", val->function);
550 def = PRVM_ED_FieldAtOfs ( prog, val->_int );
552 dpsnprintf (line, linelength, ".%s", PRVM_GetString(prog, def->s_name));
554 dpsnprintf (line, linelength, "field%lli (invalid!)", val->_int );
557 dpsnprintf (line, linelength, "void");
560 dpsnprintf (line, linelength, FLOAT_LOSSLESS_FORMAT, val->_float);
563 dpsnprintf (line, linelength, VECTOR_LOSSLESS_FORMAT, val->vector[0], val->vector[1], val->vector[2]);
566 dpsnprintf (line, linelength, "bad type %i", type);
577 Returns a string with a description and the contents of a global,
578 padded to 20 field width
581 char *PRVM_GlobalString (prvm_prog_t *prog, int ofs, char *line, size_t linelength)
587 char valuebuf[MAX_INPUTLINE];
589 val = (prvm_eval_t *)&prog->globals.fp[ofs];
590 def = PRVM_ED_GlobalAtOfs(prog, ofs);
592 dpsnprintf (line, linelength, "GLOBAL%i", ofs);
595 s = PRVM_ValueString (prog, (etype_t)def->type, val, valuebuf, sizeof(valuebuf));
596 dpsnprintf (line, linelength, "%s (=%s)", PRVM_GetString(prog, def->s_name), s);
600 //for ( ; i<20 ; i++)
601 // strcat (line," ");
607 char *PRVM_GlobalStringNoContents (prvm_prog_t *prog, int ofs, char *line, size_t linelength)
612 def = PRVM_ED_GlobalAtOfs(prog, ofs);
614 dpsnprintf (line, linelength, "GLOBAL%i", ofs);
616 dpsnprintf (line, linelength, "%s", PRVM_GetString(prog, def->s_name));
619 //for ( ; i<20 ; i++)
620 // strcat (line," ");
634 // LadyHavoc: optimized this to print out much more quickly (tempstring)
635 // LadyHavoc: changed to print out every 4096 characters (incase there are a lot of fields to print)
636 void PRVM_ED_Print(prvm_prog_t *prog, prvm_edict_t *ed, const char *wildcard_fieldname)
644 char tempstring[MAX_INPUTLINE], tempstring2[260]; // temporary string buffers
645 char valuebuf[MAX_INPUTLINE];
647 if (ed->priv.required->free)
649 Con_Printf("%s: FREE\n",prog->name);
654 dpsnprintf(tempstring, sizeof(tempstring), "\n%s EDICT %i:\n", prog->name, PRVM_NUM_FOR_EDICT(ed));
655 for (i = 1;i < prog->numfielddefs;i++)
657 d = &prog->fielddefs[i];
658 name = PRVM_GetString(prog, d->s_name);
659 if(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
660 continue; // skip _x, _y, _z vars
662 // Check Field Name Wildcard
663 if(wildcard_fieldname)
664 if( !matchpattern(name, wildcard_fieldname, 1) )
665 // Didn't match; skip
668 val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
670 // if the value is still all 0, skip the field
671 type = d->type & ~DEF_SAVEGLOBAL;
673 for (j=0 ; j<prvm_type_size[type] ; j++)
676 if (j == prvm_type_size[type])
679 if (strlen(name) > sizeof(tempstring2)-4)
681 memcpy (tempstring2, name, sizeof(tempstring2)-4);
682 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
683 tempstring2[sizeof(tempstring2)-1] = 0;
686 strlcat(tempstring, name, sizeof(tempstring));
687 for (l = strlen(name);l < 14;l++)
688 strlcat(tempstring, " ", sizeof(tempstring));
689 strlcat(tempstring, " ", sizeof(tempstring));
691 name = PRVM_ValueString(prog, (etype_t)d->type, val, valuebuf, sizeof(valuebuf));
692 if (strlen(name) > sizeof(tempstring2)-4)
694 memcpy (tempstring2, name, sizeof(tempstring2)-4);
695 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
696 tempstring2[sizeof(tempstring2)-1] = 0;
699 strlcat(tempstring, name, sizeof(tempstring));
700 strlcat(tempstring, "\n", sizeof(tempstring));
701 if (strlen(tempstring) >= sizeof(tempstring)/2)
703 Con_Print(tempstring);
708 Con_Print(tempstring);
718 extern cvar_t developer_entityparsing;
719 void PRVM_ED_Write (prvm_prog_t *prog, qfile_t *f, prvm_edict_t *ed)
727 char valuebuf[MAX_INPUTLINE];
731 if (ed->priv.required->free)
737 for (i = 1;i < prog->numfielddefs;i++)
739 d = &prog->fielddefs[i];
740 name = PRVM_GetString(prog, d->s_name);
742 if(developer_entityparsing.integer)
743 Con_Printf("PRVM_ED_Write: at entity %d field %s\n", PRVM_NUM_FOR_EDICT(ed), name);
745 //if(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
746 if(strlen(name) > 1 && name[strlen(name)-2] == '_')
747 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?)
749 val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
751 // if the value is still all 0, skip the field
752 type = d->type & ~DEF_SAVEGLOBAL;
753 for (j=0 ; j<prvm_type_size[type] ; j++)
756 if (j == prvm_type_size[type])
759 FS_Printf(f,"\"%s\" ",name);
760 prog->statestring = va(vabuf, sizeof(vabuf), "PRVM_ED_Write, ent=%d, name=%s", i, name);
761 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString(prog, (etype_t)d->type, val, valuebuf, sizeof(valuebuf)));
762 prog->statestring = NULL;
768 void PRVM_ED_PrintNum (prvm_prog_t *prog, int ent, const char *wildcard_fieldname)
770 PRVM_ED_Print(prog, PRVM_EDICT_NUM(ent), wildcard_fieldname);
775 PRVM_ED_PrintEdicts_f
777 For debugging, prints all the entities in the current server
780 void PRVM_ED_PrintEdicts_f(cmd_state_t *cmd)
784 const char *wildcard_fieldname;
786 if(Cmd_Argc(cmd) < 2 || Cmd_Argc(cmd) > 3)
788 Con_Print("prvm_edicts <program name> <optional field name wildcard>\n");
792 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
795 if( Cmd_Argc(cmd) == 3)
796 wildcard_fieldname = Cmd_Argv(cmd, 2);
798 wildcard_fieldname = NULL;
800 Con_Printf("%s: %i entities\n", prog->name, prog->num_edicts);
801 for (i=0 ; i<prog->num_edicts ; i++)
802 PRVM_ED_PrintNum (prog, i, wildcard_fieldname);
809 For debugging, prints a single edict
812 static void PRVM_ED_PrintEdict_f(cmd_state_t *cmd)
816 const char *wildcard_fieldname;
818 if(Cmd_Argc(cmd) < 3 || Cmd_Argc(cmd) > 4)
820 Con_Print("prvm_edict <program name> <edict number> <optional field name wildcard>\n");
824 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
827 i = atoi (Cmd_Argv(cmd, 2));
828 if (i >= prog->num_edicts)
830 Con_Print("Bad edict number\n");
833 if( Cmd_Argc(cmd) == 4)
834 // Optional Wildcard Provided
835 wildcard_fieldname = Cmd_Argv(cmd, 3);
838 wildcard_fieldname = NULL;
839 PRVM_ED_PrintNum (prog, i, wildcard_fieldname);
849 // 2 possibilities : 1. just displaying the active edict count
850 // 2. making a function pointer [x]
851 static void PRVM_ED_Count_f(cmd_state_t *cmd)
855 if(Cmd_Argc(cmd) != 2)
857 Con_Print("prvm_count <program name>\n");
861 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
864 prog->count_edicts(prog);
868 ==============================================================================
872 FIXME: need to tag constants, doesn't really work
873 ==============================================================================
881 void PRVM_ED_WriteGlobals (prvm_prog_t *prog, qfile_t *f)
888 char valuebuf[MAX_INPUTLINE];
891 for (i = 0;i < prog->numglobaldefs;i++)
893 def = &prog->globaldefs[i];
895 if ( !(def->type & DEF_SAVEGLOBAL) )
897 type &= ~DEF_SAVEGLOBAL;
899 if (type != ev_string && type != ev_float && type != ev_entity)
902 name = PRVM_GetString(prog, def->s_name);
904 if(developer_entityparsing.integer)
905 Con_Printf("PRVM_ED_WriteGlobals: at global %s\n", name);
907 prog->statestring = va(vabuf, sizeof(vabuf), "PRVM_ED_WriteGlobals, name=%s", name);
908 FS_Printf(f,"\"%s\" ", name);
909 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString(prog, (etype_t)type, (prvm_eval_t *)&prog->globals.fp[def->ofs], valuebuf, sizeof(valuebuf)));
910 prog->statestring = NULL;
920 void PRVM_ED_ParseGlobals (prvm_prog_t *prog, const char *data)
922 char keyname[MAX_INPUTLINE];
928 if (!COM_ParseToken_Simple(&data, false, false, true))
929 prog->error_cmd("PRVM_ED_ParseGlobals: EOF without closing brace");
930 if (com_token[0] == '}')
933 if (developer_entityparsing.integer)
934 Con_Printf("Key: \"%s\"", com_token);
936 strlcpy (keyname, com_token, sizeof(keyname));
939 if (!COM_ParseToken_Simple(&data, false, true, true))
940 prog->error_cmd("PRVM_ED_ParseGlobals: EOF without closing brace");
942 if (developer_entityparsing.integer)
943 Con_Printf(" \"%s\"\n", com_token);
945 if (com_token[0] == '}')
946 prog->error_cmd("PRVM_ED_ParseGlobals: closing brace without data");
948 key = PRVM_ED_FindGlobal (prog, keyname);
951 Con_DPrintf("'%s' is not a global on %s\n", keyname, prog->name);
955 if (!PRVM_ED_ParseEpair(prog, NULL, key, com_token, true))
956 prog->error_cmd("PRVM_ED_ParseGlobals: parse error");
960 //============================================================================
967 Can parse either fields or globals
968 returns false if error
971 qboolean PRVM_ED_ParseEpair(prvm_prog_t *prog, prvm_edict_t *ent, ddef_t *key, const char *s, qboolean parsebackslash)
980 val = (prvm_eval_t *)(ent->fields.fp + key->ofs);
982 val = (prvm_eval_t *)(prog->globals.fp + key->ofs);
983 switch (key->type & ~DEF_SAVEGLOBAL)
986 l = (int)strlen(s) + 1;
987 val->string = PRVM_AllocString(prog, l, &new_p);
988 for (i = 0;i < l;i++)
990 if (s[i] == '\\' && s[i+1] && parsebackslash)
995 else if (s[i] == 'r')
1006 while (*s && ISWHITESPACE(*s))
1008 val->_float = atof(s);
1012 for (i = 0;i < 3;i++)
1014 while (*s && ISWHITESPACE(*s))
1018 val->vector[i] = atof(s);
1019 while (!ISWHITESPACE(*s))
1027 while (*s && ISWHITESPACE(*s))
1030 if (i >= prog->limit_edicts)
1031 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);
1032 while (i >= prog->max_edicts)
1033 PRVM_MEM_IncreaseEdicts(prog);
1034 // if IncreaseEdicts was called the base pointer needs to be updated
1036 val = (prvm_eval_t *)(ent->fields.fp + key->ofs);
1037 val->edict = PRVM_EDICT_TO_PROG(PRVM_EDICT_NUM((int)i));
1043 Con_DPrintf("PRVM_ED_ParseEpair: Bogus field name %s in %s\n", s, prog->name);
1046 def = PRVM_ED_FindField(prog, s + 1);
1049 Con_DPrintf("PRVM_ED_ParseEpair: Can't find field %s in %s\n", s, prog->name);
1052 val->_int = def->ofs;
1056 func = PRVM_ED_FindFunction(prog, s);
1059 Con_Printf("PRVM_ED_ParseEpair: Can't find function %s in %s\n", s, prog->name);
1062 val->function = func - prog->functions;
1066 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);
1076 Console command to send a string to QC function GameCommand of the
1080 sv_cmd adminmsg 3 "do not teamkill"
1081 cl_cmd someclientcommand
1082 menu_cmd somemenucommand
1084 All progs can support this extension; sg calls it in server QC, cg in client
1088 static void PRVM_GameCommand(cmd_state_t *cmd, const char *whichprogs, const char *whichcmd)
1091 if(Cmd_Argc(cmd) < 1)
1093 Con_Printf("%s text...\n", whichcmd);
1097 if (!(prog = PRVM_FriendlyProgFromString(whichprogs)))
1100 if(!PRVM_allfunction(GameCommand))
1102 Con_Printf("%s program do not support GameCommand!\n", whichprogs);
1106 int restorevm_tempstringsbuf_cursize;
1111 restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
1112 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(prog, s ? s : "");
1113 prog->ExecuteProgram(prog, PRVM_allfunction(GameCommand), "QC function GameCommand is missing");
1114 prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1117 static void PRVM_GameCommand_Server_f(cmd_state_t *cmd)
1119 PRVM_GameCommand(cmd, "server", "sv_cmd");
1121 static void PRVM_GameCommand_Client_f(cmd_state_t *cmd)
1123 PRVM_GameCommand(cmd, "client", "cl_cmd");
1125 static void PRVM_GameCommand_Menu_f(cmd_state_t *cmd)
1127 PRVM_GameCommand(cmd, "menu", "menu_cmd");
1134 Console command to load a field of a specified edict
1137 static void PRVM_ED_EdictGet_f(cmd_state_t *cmd)
1144 char valuebuf[MAX_INPUTLINE];
1146 if(Cmd_Argc(cmd) != 4 && Cmd_Argc(cmd) != 5)
1148 Con_Print("prvm_edictget <program name> <edict number> <field> [<cvar>]\n");
1152 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
1155 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(cmd, 2)));
1157 if((key = PRVM_ED_FindField(prog, Cmd_Argv(cmd, 3))) == 0)
1159 Con_Printf("Key %s not found !\n", Cmd_Argv(cmd, 3));
1163 v = (prvm_eval_t *)(ed->fields.fp + key->ofs);
1164 s = PRVM_UglyValueString(prog, (etype_t)key->type, v, valuebuf, sizeof(valuebuf));
1165 if(Cmd_Argc(cmd) == 5)
1167 cvar_t *cvar = Cvar_FindVar(cmd->cvars, Cmd_Argv(cmd, 4), cmd->cvars_flagsmask);
1168 if (cvar && cvar->flags & CVAR_READONLY)
1170 Con_Printf("prvm_edictget: %s is read-only\n", cvar->name);
1173 Cvar_Get(cmd->cvars, Cmd_Argv(cmd, 4), s, cmd->cvars_flagsmask, NULL);
1176 Con_Printf("%s\n", s);
1182 static void PRVM_ED_GlobalGet_f(cmd_state_t *cmd)
1188 char valuebuf[MAX_INPUTLINE];
1190 if(Cmd_Argc(cmd) != 3 && Cmd_Argc(cmd) != 4)
1192 Con_Print("prvm_globalget <program name> <global> [<cvar>]\n");
1196 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
1199 key = PRVM_ED_FindGlobal(prog, Cmd_Argv(cmd, 2));
1202 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(cmd, 2), Cmd_Argv(cmd, 1) );
1206 v = (prvm_eval_t *) &prog->globals.fp[key->ofs];
1207 s = PRVM_UglyValueString(prog, (etype_t)key->type, v, valuebuf, sizeof(valuebuf));
1208 if(Cmd_Argc(cmd) == 4)
1210 cvar_t *cvar = Cvar_FindVar(cmd->cvars, Cmd_Argv(cmd, 3), cmd->cvars_flagsmask);
1211 if (cvar && cvar->flags & CVAR_READONLY)
1213 Con_Printf("prvm_globalget: %s is read-only\n", cvar->name);
1216 Cvar_Get(cmd->cvars, Cmd_Argv(cmd, 3), s, cmd->cvars_flagsmask, NULL);
1219 Con_Printf("%s\n", s);
1229 Console command to set a field of a specified edict
1232 static void PRVM_ED_EdictSet_f(cmd_state_t *cmd)
1238 if(Cmd_Argc(cmd) != 5)
1240 Con_Print("prvm_edictset <program name> <edict number> <field> <value>\n");
1244 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
1247 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(cmd, 2)));
1249 if((key = PRVM_ED_FindField(prog, Cmd_Argv(cmd, 3))) == 0)
1250 Con_Printf("Key %s not found !\n", Cmd_Argv(cmd, 3));
1252 PRVM_ED_ParseEpair(prog, ed, key, Cmd_Argv(cmd, 4), true);
1256 ====================
1259 Parses an edict out of the given string, returning the new position
1260 ed should be a properly initialized empty edict.
1261 Used for initial level load and for savegames.
1262 ====================
1264 const char *PRVM_ED_ParseEdict (prvm_prog_t *prog, const char *data, prvm_edict_t *ent)
1274 // go through all the dictionary pairs
1278 if (!COM_ParseToken_Simple(&data, false, false, true))
1279 prog->error_cmd("PRVM_ED_ParseEdict: EOF without closing brace");
1280 if (developer_entityparsing.integer)
1281 Con_Printf("Key: \"%s\"", com_token);
1282 if (com_token[0] == '}')
1285 // anglehack is to allow QuakeEd to write single scalar angles
1286 // and allow them to be turned into vectors. (FIXME...)
1287 if (!strcmp(com_token, "angle"))
1289 strlcpy (com_token, "angles", sizeof(com_token));
1295 // FIXME: change light to _light to get rid of this hack
1296 if (!strcmp(com_token, "light"))
1297 strlcpy (com_token, "light_lev", sizeof(com_token)); // hack for single light def
1299 strlcpy (keyname, com_token, sizeof(keyname));
1301 // another hack to fix keynames with trailing spaces
1302 n = strlen(keyname);
1303 while (n && keyname[n-1] == ' ')
1310 if (!COM_ParseToken_Simple(&data, false, false, true))
1311 prog->error_cmd("PRVM_ED_ParseEdict: EOF without closing brace");
1312 if (developer_entityparsing.integer)
1313 Con_Printf(" \"%s\"\n", com_token);
1315 if (com_token[0] == '}')
1316 prog->error_cmd("PRVM_ED_ParseEdict: closing brace without data");
1320 // ignore attempts to set key "" (this problem occurs in nehahra neh1m8.bsp)
1324 // keynames with a leading underscore are used for utility comments,
1325 // and are immediately discarded by quake
1326 if (keyname[0] == '_')
1329 key = PRVM_ED_FindField (prog, keyname);
1332 Con_DPrintf("%s: '%s' is not a field\n", prog->name, keyname);
1339 strlcpy (temp, com_token, sizeof(temp));
1340 dpsnprintf (com_token, sizeof(com_token), "0 %s 0", temp);
1343 if (!PRVM_ED_ParseEpair(prog, ent, key, com_token, strcmp(keyname, "wad") != 0))
1344 prog->error_cmd("PRVM_ED_ParseEdict: parse error");
1348 ent->priv.required->free = true;
1349 ent->priv.required->freetime = realtime;
1358 PRVM_ED_LoadFromFile
1360 The entities are directly placed in the array, rather than allocated with
1361 PRVM_ED_Alloc, because otherwise an error loading the map would have entity
1362 number references out of order.
1364 Creates a server's entity / program execution context by
1365 parsing textual entity definitions out of an ent file.
1367 Used for both fresh maps and savegame loads. A fresh map would also need
1368 to call PRVM_ED_CallSpawnFunctions () to let the objects initialize themselves.
1371 void PRVM_ED_LoadFromFile (prvm_prog_t *prog, const char *data)
1374 int parsed, inhibited, spawned, died;
1375 const char *funcname;
1384 prvm_reuseedicts_always_allow = realtime;
1389 // parse the opening brace
1390 if (!COM_ParseToken_Simple(&data, false, false, true))
1392 if (com_token[0] != '{')
1393 prog->error_cmd("PRVM_ED_LoadFromFile: %s: found %s when expecting {", prog->name, com_token);
1395 // CHANGED: this is not conform to PR_LoadFromFile
1396 if(prog->loadintoworld)
1398 prog->loadintoworld = false;
1399 ent = PRVM_EDICT_NUM(0);
1402 ent = PRVM_ED_Alloc(prog);
1405 if (ent != prog->edicts) // hack
1406 memset (ent->fields.fp, 0, prog->entityfields * sizeof(prvm_vec_t));
1408 data = PRVM_ED_ParseEdict (prog, data, ent);
1411 // remove the entity ?
1412 if(!prog->load_edict(prog, ent))
1414 PRVM_ED_Free(prog, ent);
1419 if (PRVM_serverfunction(SV_OnEntityPreSpawnFunction))
1422 PRVM_serverglobalfloat(time) = sv.time;
1423 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1424 // optional entity parameter for self (EXT_ENTITYPARAM)
1425 PRVM_G_INT(OFS_PARM0) = PRVM_EDICT_TO_PROG(ent);
1426 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityPreSpawnFunction), "QC function SV_OnEntityPreSpawnFunction is missing");
1429 if(ent->priv.required->free)
1436 // immediately call spawn function, but only if there is a self global and a classname
1438 if(!ent->priv.required->free)
1440 if (!PRVM_alledictstring(ent, classname))
1442 Con_Print("No classname for:\n");
1443 PRVM_ED_Print(prog, ent, NULL);
1444 PRVM_ED_Free (prog, ent);
1448 // look for the spawn function
1449 funcname = PRVM_GetString(prog, PRVM_alledictstring(ent, classname));
1450 func = PRVM_ED_FindFunction (prog, va(vabuf, sizeof(vabuf), "spawnfunc_%s", funcname));
1452 if(!PRVM_allglobalfloat(require_spawnfunc_prefix))
1453 func = PRVM_ED_FindFunction (prog, funcname);
1457 // check for OnEntityNoSpawnFunction
1458 if (PRVM_serverfunction(SV_OnEntityNoSpawnFunction))
1461 PRVM_serverglobalfloat(time) = sv.time;
1462 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1463 // optional entity parameter for self (EXT_ENTITYPARAM)
1464 PRVM_G_INT(OFS_PARM0) = PRVM_EDICT_TO_PROG(ent);
1465 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityNoSpawnFunction), "QC function SV_OnEntityNoSpawnFunction is missing");
1469 if (developer.integer > 0) // don't confuse non-developers with errors
1471 Con_Print("No spawn function for:\n");
1472 PRVM_ED_Print(prog, ent, NULL);
1474 PRVM_ED_Free (prog, ent);
1475 continue; // not included in "inhibited" count
1481 PRVM_serverglobalfloat(time) = sv.time;
1482 PRVM_allglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1483 prog->ExecuteProgram(prog, func - prog->functions, "");
1487 if(!ent->priv.required->free)
1488 if (PRVM_serverfunction(SV_OnEntityPostSpawnFunction))
1491 PRVM_serverglobalfloat(time) = sv.time;
1492 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1493 // optional entity parameter for self (EXT_ENTITYPARAM)
1494 PRVM_G_INT(OFS_PARM0) = PRVM_EDICT_TO_PROG(ent);
1495 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityPostSpawnFunction), "QC function SV_OnEntityPostSpawnFunction is missing");
1499 if (ent->priv.required->free)
1503 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);
1505 prvm_reuseedicts_always_allow = 0;
1508 static void PRVM_FindOffsets(prvm_prog_t *prog)
1510 // field and global searches use -1 for NULL
1511 memset(&prog->fieldoffsets, -1, sizeof(prog->fieldoffsets));
1512 memset(&prog->globaloffsets, -1, sizeof(prog->globaloffsets));
1513 // function searches use 0 for NULL
1514 memset(&prog->funcoffsets, 0, sizeof(prog->funcoffsets));
1515 #define PRVM_DECLARE_serverglobalfloat(x)
1516 #define PRVM_DECLARE_serverglobalvector(x)
1517 #define PRVM_DECLARE_serverglobalstring(x)
1518 #define PRVM_DECLARE_serverglobaledict(x)
1519 #define PRVM_DECLARE_serverglobalfunction(x)
1520 #define PRVM_DECLARE_clientglobalfloat(x)
1521 #define PRVM_DECLARE_clientglobalvector(x)
1522 #define PRVM_DECLARE_clientglobalstring(x)
1523 #define PRVM_DECLARE_clientglobaledict(x)
1524 #define PRVM_DECLARE_clientglobalfunction(x)
1525 #define PRVM_DECLARE_menuglobalfloat(x)
1526 #define PRVM_DECLARE_menuglobalvector(x)
1527 #define PRVM_DECLARE_menuglobalstring(x)
1528 #define PRVM_DECLARE_menuglobaledict(x)
1529 #define PRVM_DECLARE_menuglobalfunction(x)
1530 #define PRVM_DECLARE_serverfieldfloat(x)
1531 #define PRVM_DECLARE_serverfieldvector(x)
1532 #define PRVM_DECLARE_serverfieldstring(x)
1533 #define PRVM_DECLARE_serverfieldedict(x)
1534 #define PRVM_DECLARE_serverfieldfunction(x)
1535 #define PRVM_DECLARE_clientfieldfloat(x)
1536 #define PRVM_DECLARE_clientfieldvector(x)
1537 #define PRVM_DECLARE_clientfieldstring(x)
1538 #define PRVM_DECLARE_clientfieldedict(x)
1539 #define PRVM_DECLARE_clientfieldfunction(x)
1540 #define PRVM_DECLARE_menufieldfloat(x)
1541 #define PRVM_DECLARE_menufieldvector(x)
1542 #define PRVM_DECLARE_menufieldstring(x)
1543 #define PRVM_DECLARE_menufieldedict(x)
1544 #define PRVM_DECLARE_menufieldfunction(x)
1545 #define PRVM_DECLARE_serverfunction(x)
1546 #define PRVM_DECLARE_clientfunction(x)
1547 #define PRVM_DECLARE_menufunction(x)
1548 #define PRVM_DECLARE_field(x) prog->fieldoffsets.x = PRVM_ED_FindFieldOffset(prog, #x);
1549 #define PRVM_DECLARE_global(x) prog->globaloffsets.x = PRVM_ED_FindGlobalOffset(prog, #x);
1550 #define PRVM_DECLARE_function(x) prog->funcoffsets.x = PRVM_ED_FindFunctionOffset(prog, #x);
1551 #include "prvm_offsets.h"
1552 #undef PRVM_DECLARE_serverglobalfloat
1553 #undef PRVM_DECLARE_serverglobalvector
1554 #undef PRVM_DECLARE_serverglobalstring
1555 #undef PRVM_DECLARE_serverglobaledict
1556 #undef PRVM_DECLARE_serverglobalfunction
1557 #undef PRVM_DECLARE_clientglobalfloat
1558 #undef PRVM_DECLARE_clientglobalvector
1559 #undef PRVM_DECLARE_clientglobalstring
1560 #undef PRVM_DECLARE_clientglobaledict
1561 #undef PRVM_DECLARE_clientglobalfunction
1562 #undef PRVM_DECLARE_menuglobalfloat
1563 #undef PRVM_DECLARE_menuglobalvector
1564 #undef PRVM_DECLARE_menuglobalstring
1565 #undef PRVM_DECLARE_menuglobaledict
1566 #undef PRVM_DECLARE_menuglobalfunction
1567 #undef PRVM_DECLARE_serverfieldfloat
1568 #undef PRVM_DECLARE_serverfieldvector
1569 #undef PRVM_DECLARE_serverfieldstring
1570 #undef PRVM_DECLARE_serverfieldedict
1571 #undef PRVM_DECLARE_serverfieldfunction
1572 #undef PRVM_DECLARE_clientfieldfloat
1573 #undef PRVM_DECLARE_clientfieldvector
1574 #undef PRVM_DECLARE_clientfieldstring
1575 #undef PRVM_DECLARE_clientfieldedict
1576 #undef PRVM_DECLARE_clientfieldfunction
1577 #undef PRVM_DECLARE_menufieldfloat
1578 #undef PRVM_DECLARE_menufieldvector
1579 #undef PRVM_DECLARE_menufieldstring
1580 #undef PRVM_DECLARE_menufieldedict
1581 #undef PRVM_DECLARE_menufieldfunction
1582 #undef PRVM_DECLARE_serverfunction
1583 #undef PRVM_DECLARE_clientfunction
1584 #undef PRVM_DECLARE_menufunction
1585 #undef PRVM_DECLARE_field
1586 #undef PRVM_DECLARE_global
1587 #undef PRVM_DECLARE_function
1592 typedef struct dpfield_s
1599 #define DPFIELDS (sizeof(dpfields) / sizeof(dpfield_t))
1601 dpfield_t dpfields[] =
1612 #define PO_HASHSIZE 16384
1613 typedef struct po_string_s
1616 struct po_string_s *nextonhashchain;
1621 po_string_t *hashtable[PO_HASHSIZE];
1624 static void PRVM_PO_UnparseString(char *out, const char *in, size_t outsize)
1633 case '\a': if(outsize >= 2) { *out++ = '\\'; *out++ = 'a'; outsize -= 2; } break;
1634 case '\b': if(outsize >= 2) { *out++ = '\\'; *out++ = 'b'; outsize -= 2; } break;
1635 case '\t': if(outsize >= 2) { *out++ = '\\'; *out++ = 't'; outsize -= 2; } break;
1636 case '\r': if(outsize >= 2) { *out++ = '\\'; *out++ = 'r'; outsize -= 2; } break;
1637 case '\n': if(outsize >= 2) { *out++ = '\\'; *out++ = 'n'; outsize -= 2; } break;
1638 case '\\': if(outsize >= 2) { *out++ = '\\'; *out++ = '\\'; outsize -= 2; } break;
1639 case '"': if(outsize >= 2) { *out++ = '\\'; *out++ = '"'; outsize -= 2; } break;
1641 if(*in >= 0 && *in <= 0x1F)
1646 *out++ = '0' + ((*in & 0700) >> 6);
1647 *out++ = '0' + ((*in & 0070) >> 3);
1648 *out++ = '0' + (*in & 0007) ;
1665 static void PRVM_PO_ParseString(char *out, const char *in, size_t outsize)
1678 case 'a': if(outsize > 0) { *out++ = '\a'; --outsize; } break;
1679 case 'b': if(outsize > 0) { *out++ = '\b'; --outsize; } break;
1680 case 't': if(outsize > 0) { *out++ = '\t'; --outsize; } break;
1681 case 'r': if(outsize > 0) { *out++ = '\r'; --outsize; } break;
1682 case 'n': if(outsize > 0) { *out++ = '\n'; --outsize; } break;
1683 case '\\': if(outsize > 0) { *out++ = '\\'; --outsize; } break;
1684 case '"': if(outsize > 0) { *out++ = '"'; --outsize; } break;
1685 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
1689 if(*in >= '0' && *in <= '7')
1692 *out = (*out << 3) | (*in - '0');
1695 if(*in >= '0' && *in <= '7')
1698 *out = (*out << 3) | (*in - '0');
1709 if(outsize > 0) { *out++ = *in; --outsize; }
1724 static po_t *PRVM_PO_Load(const char *filename, const char *filename2, mempool_t *pool)
1729 char inbuf[MAX_INPUTLINE];
1730 char decodedbuf[MAX_INPUTLINE];
1733 po_string_t thisstr;
1736 for (i = 0; i < 2; ++i)
1738 const char *buf = (const char *)
1739 FS_LoadFile((i > 0 ? filename : filename2), pool, true, NULL);
1740 // first read filename2, then read filename
1741 // so that progs.dat.de.po wins over common.de.po
1742 // and within file, last item wins
1749 po = (po_t *)Mem_Alloc(pool, sizeof(*po));
1750 memset(po, 0, sizeof(*po));
1753 memset(&thisstr, 0, sizeof(thisstr)); // hush compiler warning
1761 p = strchr(p, '\n');
1767 if(*p == '\r' || *p == '\n')
1772 if(!strncmp(p, "msgid \"", 7))
1777 else if(!strncmp(p, "msgstr \"", 8))
1784 p = strchr(p, '\n');
1794 q = strchr(p, '\n');
1801 if((size_t)(q - p) >= (size_t) sizeof(inbuf))
1803 strlcpy(inbuf, p, q - p); // not - 1, because this adds a NUL
1804 PRVM_PO_ParseString(decodedbuf + decodedpos, inbuf, sizeof(decodedbuf) - decodedpos);
1805 decodedpos += strlen(decodedbuf + decodedpos);
1815 Mem_Free(thisstr.key);
1816 thisstr.key = (char *)Mem_Alloc(pool, decodedpos + 1);
1817 memcpy(thisstr.key, decodedbuf, decodedpos + 1);
1819 else if(decodedpos > 0 && thisstr.key) // skip empty translation results
1821 thisstr.value = (char *)Mem_Alloc(pool, decodedpos + 1);
1822 memcpy(thisstr.value, decodedbuf, decodedpos + 1);
1823 hashindex = CRC_Block((const unsigned char *) thisstr.key, strlen(thisstr.key)) % PO_HASHSIZE;
1824 thisstr.nextonhashchain = po->hashtable[hashindex];
1825 po->hashtable[hashindex] = (po_string_t *)Mem_Alloc(pool, sizeof(thisstr));
1826 memcpy(po->hashtable[hashindex], &thisstr, sizeof(thisstr));
1827 memset(&thisstr, 0, sizeof(thisstr));
1831 Mem_Free((char *) buf);
1836 static const char *PRVM_PO_Lookup(po_t *po, const char *str)
1838 int hashindex = CRC_Block((const unsigned char *) str, strlen(str)) % PO_HASHSIZE;
1839 po_string_t *p = po->hashtable[hashindex];
1842 if(!strcmp(str, p->key))
1844 p = p->nextonhashchain;
1848 static void PRVM_PO_Destroy(po_t *po)
1851 for(i = 0; i < PO_HASHSIZE; ++i)
1853 po_string_t *p = po->hashtable[i];
1857 p = p->nextonhashchain;
1866 void PRVM_LeakTest(prvm_prog_t *prog);
1867 void PRVM_Prog_Reset(prvm_prog_t *prog)
1871 PRVM_LeakTest(prog);
1872 prog->reset_cmd(prog);
1873 Mem_FreePool(&prog->progs_mempool);
1875 PRVM_PO_Destroy((po_t *) prog->po);
1877 memset(prog,0,sizeof(prvm_prog_t));
1878 prog->break_statement = -1;
1879 prog->watch_global_type = ev_void;
1880 prog->watch_field_type = ev_void;
1888 static void PRVM_LoadLNO( prvm_prog_t *prog, const char *progname ) {
1889 fs_offset_t filesize;
1891 unsigned int *header;
1894 FS_StripExtension( progname, filename, sizeof( filename ) );
1895 strlcat( filename, ".lno", sizeof( filename ) );
1897 lno = FS_LoadFile( filename, tempmempool, false, &filesize );
1903 <Spike> SafeWrite (h, &lnotype, sizeof(int));
1904 <Spike> SafeWrite (h, &version, sizeof(int));
1905 <Spike> SafeWrite (h, &numglobaldefs, sizeof(int));
1906 <Spike> SafeWrite (h, &numpr_globals, sizeof(int));
1907 <Spike> SafeWrite (h, &numfielddefs, sizeof(int));
1908 <Spike> SafeWrite (h, &numstatements, sizeof(int));
1909 <Spike> SafeWrite (h, statement_linenums, numstatements*sizeof(int));
1911 if ((unsigned int)filesize < (6 + prog->progs_numstatements) * sizeof(int))
1917 header = (unsigned int *) lno;
1918 if( header[ 0 ] == *(unsigned int *) "LNOF" &&
1919 LittleLong( header[ 1 ] ) == 1 &&
1920 (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs_numglobaldefs &&
1921 (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs_numglobals &&
1922 (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs_numfielddefs &&
1923 (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs_numstatements )
1925 prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof( int ) );
1926 memcpy( prog->statement_linenums, header + 6, prog->progs_numstatements * sizeof( int ) );
1928 /* gmqcc suports columnums */
1929 if ((unsigned int)filesize > ((6 + 2 * prog->progs_numstatements) * sizeof( int )))
1931 prog->statement_columnnums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof( int ) );
1932 memcpy( prog->statement_columnnums, header + 6 + prog->progs_numstatements, prog->progs_numstatements * sizeof( int ) );
1943 static void PRVM_UpdateBreakpoints(prvm_prog_t *prog);
1944 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)
1947 dprograms_t *dprograms;
1948 dstatement_t *instatements;
1949 ddef_t *infielddefs;
1950 ddef_t *inglobaldefs;
1952 dfunction_t *infunctions;
1954 fs_offset_t filesize;
1955 int requiredglobalspace;
1972 prog->error_cmd("PRVM_LoadProgs: there is already a %s program loaded!", prog->name );
1974 Host_LockSession(); // all progs can use the session cvar
1975 Crypto_LoadKeys(); // all progs might use the keys at init time
1979 dprograms = (dprograms_t *) data;
1983 dprograms = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
1984 if (dprograms == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
1985 prog->error_cmd("PRVM_LoadProgs: couldn't load %s for %s", filename, prog->name);
1986 // TODO bounds check header fields (e.g. numstatements), they must never go behind end of file
1988 prog->profiletime = Sys_DirtyTime();
1989 prog->starttime = realtime;
1991 Con_DPrintf("%s programs occupy %iK.\n", prog->name, (int)(filesize/1024));
1993 requiredglobalspace = 0;
1994 for (i = 0;i < numrequiredglobals;i++)
1995 requiredglobalspace += required_global[i].type == ev_vector ? 3 : 1;
1997 prog->filecrc = CRC_Block((unsigned char *)dprograms, filesize);
1999 // byte swap the header
2000 prog->progs_version = LittleLong(dprograms->version);
2001 prog->progs_crc = LittleLong(dprograms->crc);
2002 if (prog->progs_version != PROG_VERSION)
2003 prog->error_cmd("%s: %s has wrong version number (%i should be %i)", prog->name, filename, prog->progs_version, PROG_VERSION);
2004 instatements = (dstatement_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_statements));
2005 prog->progs_numstatements = LittleLong(dprograms->numstatements);
2006 inglobaldefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globaldefs));
2007 prog->progs_numglobaldefs = LittleLong(dprograms->numglobaldefs);
2008 infielddefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_fielddefs));
2009 prog->progs_numfielddefs = LittleLong(dprograms->numfielddefs);
2010 infunctions = (dfunction_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_functions));
2011 prog->progs_numfunctions = LittleLong(dprograms->numfunctions);
2012 instrings = (char *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_strings));
2013 prog->progs_numstrings = LittleLong(dprograms->numstrings);
2014 inglobals = (int *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globals));
2015 prog->progs_numglobals = LittleLong(dprograms->numglobals);
2016 prog->progs_entityfields = LittleLong(dprograms->entityfields);
2018 prog->numstatements = prog->progs_numstatements;
2019 prog->numglobaldefs = prog->progs_numglobaldefs;
2020 prog->numfielddefs = prog->progs_numfielddefs;
2021 prog->numfunctions = prog->progs_numfunctions;
2022 prog->numstrings = prog->progs_numstrings;
2023 prog->numglobals = prog->progs_numglobals;
2024 prog->entityfields = prog->progs_entityfields;
2026 if (LittleLong(dprograms->ofs_strings) + prog->progs_numstrings > (int)filesize)
2027 prog->error_cmd("%s: %s strings go past end of file", prog->name, filename);
2028 prog->strings = (char *)Mem_Alloc(prog->progs_mempool, prog->progs_numstrings);
2029 memcpy(prog->strings, instrings, prog->progs_numstrings);
2030 prog->stringssize = prog->progs_numstrings;
2032 prog->numknownstrings = 0;
2033 prog->maxknownstrings = 0;
2034 prog->knownstrings = NULL;
2035 prog->knownstrings_flags = NULL;
2037 Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
2039 // we need to expand the globaldefs and fielddefs to include engine defs
2040 prog->globaldefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobaldefs + numrequiredglobals) * sizeof(ddef_t));
2041 prog->globals.fp = (prvm_vec_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobals + requiredglobalspace + 2) * sizeof(prvm_vec_t));
2042 // + 2 is because of an otherwise occurring overrun in RETURN instruction
2043 // when trying to return the last or second-last global
2044 // (RETURN always returns a vector, there is no RETURN_F instruction)
2045 prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numfielddefs + numrequiredfields) * sizeof(ddef_t));
2046 // we need to convert the statements to our memory format
2047 prog->statements = (mstatement_t *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(mstatement_t));
2048 // allocate space for profiling statement usage
2049 prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(*prog->statement_profile));
2050 prog->explicit_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(*prog->statement_profile));
2051 // functions need to be converted to the memory format
2052 prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs_numfunctions);
2054 for (i = 0;i < prog->progs_numfunctions;i++)
2056 prog->functions[i].first_statement = LittleLong(infunctions[i].first_statement);
2057 prog->functions[i].parm_start = LittleLong(infunctions[i].parm_start);
2058 prog->functions[i].s_name = LittleLong(infunctions[i].s_name);
2059 prog->functions[i].s_file = LittleLong(infunctions[i].s_file);
2060 prog->functions[i].numparms = LittleLong(infunctions[i].numparms);
2061 prog->functions[i].locals = LittleLong(infunctions[i].locals);
2062 memcpy(prog->functions[i].parm_size, infunctions[i].parm_size, sizeof(infunctions[i].parm_size));
2063 if(prog->functions[i].first_statement >= prog->numstatements)
2064 prog->error_cmd("PRVM_LoadProgs: out of bounds function statement (function %d) in %s", i, prog->name);
2065 // TODO bounds check parm_start, s_name, s_file, numparms, locals, parm_size
2068 // copy the globaldefs to the new globaldefs list
2069 for (i=0 ; i<prog->numglobaldefs ; i++)
2071 prog->globaldefs[i].type = LittleShort(inglobaldefs[i].type);
2072 prog->globaldefs[i].ofs = LittleShort(inglobaldefs[i].ofs);
2073 prog->globaldefs[i].s_name = LittleLong(inglobaldefs[i].s_name);
2074 // TODO bounds check ofs, s_name
2077 // append the required globals
2078 for (i = 0;i < numrequiredglobals;i++)
2080 prog->globaldefs[prog->numglobaldefs].type = required_global[i].type;
2081 prog->globaldefs[prog->numglobaldefs].ofs = prog->numglobals;
2082 prog->globaldefs[prog->numglobaldefs].s_name = PRVM_SetEngineString(prog, required_global[i].name);
2083 if (prog->globaldefs[prog->numglobaldefs].type == ev_vector)
2084 prog->numglobals += 3;
2087 prog->numglobaldefs++;
2090 // copy the progs fields to the new fields list
2091 for (i = 0;i < prog->numfielddefs;i++)
2093 prog->fielddefs[i].type = LittleShort(infielddefs[i].type);
2094 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
2095 prog->error_cmd("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", prog->name);
2096 prog->fielddefs[i].ofs = LittleShort(infielddefs[i].ofs);
2097 prog->fielddefs[i].s_name = LittleLong(infielddefs[i].s_name);
2098 // TODO bounds check ofs, s_name
2101 // append the required fields
2102 for (i = 0;i < numrequiredfields;i++)
2104 prog->fielddefs[prog->numfielddefs].type = required_field[i].type;
2105 prog->fielddefs[prog->numfielddefs].ofs = prog->entityfields;
2106 prog->fielddefs[prog->numfielddefs].s_name = PRVM_SetEngineString(prog, required_field[i].name);
2107 if (prog->fielddefs[prog->numfielddefs].type == ev_vector)
2108 prog->entityfields += 3;
2110 prog->entityfields++;
2111 prog->numfielddefs++;
2114 // LadyHavoc: TODO: reorder globals to match engine struct
2115 // LadyHavoc: TODO: reorder fields to match engine struct
2116 #define remapglobal(index) (index)
2117 #define remapfield(index) (index)
2120 // FIXME: LadyHavoc: this uses a crude way to identify integer constants, rather than checking for matching globaldefs and checking their type
2121 for (i = 0;i < prog->progs_numglobals;i++)
2123 u.i = LittleLong(inglobals[i]);
2124 // most globals are 0, we only need to deal with the ones that are not
2127 d = u.i & 0xFF800000;
2128 if ((d == 0xFF800000) || (d == 0))
2130 // Looks like an integer (expand to int64)
2131 prog->globals.ip[remapglobal(i)] = u.i;
2135 // Looks like a float (expand to double)
2136 prog->globals.fp[remapglobal(i)] = u.f;
2141 // LadyHavoc: TODO: support 32bit progs statement formats
2142 // copy, remap globals in statements, bounds check
2143 for (i = 0;i < prog->progs_numstatements;i++)
2145 op = (opcode_t)LittleShort(instatements[i].op);
2146 a = (unsigned short)LittleShort(instatements[i].a);
2147 b = (unsigned short)LittleShort(instatements[i].b);
2148 c = (unsigned short)LittleShort(instatements[i].c);
2154 if (a >= prog->progs_numglobals || b + i < 0 || b + i >= prog->progs_numstatements)
2155 prog->error_cmd("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", i, prog->name);
2156 prog->statements[i].op = op;
2157 prog->statements[i].operand[0] = remapglobal(a);
2158 prog->statements[i].operand[1] = -1;
2159 prog->statements[i].operand[2] = -1;
2160 prog->statements[i].jumpabsolute = i + b;
2164 if (a + i < 0 || a + i >= prog->progs_numstatements)
2165 prog->error_cmd("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, prog->name);
2166 prog->statements[i].op = op;
2167 prog->statements[i].operand[0] = -1;
2168 prog->statements[i].operand[1] = -1;
2169 prog->statements[i].operand[2] = -1;
2170 prog->statements[i].jumpabsolute = i + a;
2173 Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", (int)op, i, prog->name);
2175 // global global global
2210 if (a >= prog->progs_numglobals || b >= prog->progs_numglobals || c >= prog->progs_numglobals)
2211 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
2212 prog->statements[i].op = op;
2213 prog->statements[i].operand[0] = remapglobal(a);
2214 prog->statements[i].operand[1] = remapglobal(b);
2215 prog->statements[i].operand[2] = remapglobal(c);
2216 prog->statements[i].jumpabsolute = -1;
2218 // global none global
2224 if (a >= prog->progs_numglobals || c >= prog->progs_numglobals)
2225 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2226 prog->statements[i].op = op;
2227 prog->statements[i].operand[0] = remapglobal(a);
2228 prog->statements[i].operand[1] = -1;
2229 prog->statements[i].operand[2] = remapglobal(c);
2230 prog->statements[i].jumpabsolute = -1;
2246 if (a >= prog->progs_numglobals || b >= prog->progs_numglobals)
2247 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2248 prog->statements[i].op = op;
2249 prog->statements[i].operand[0] = remapglobal(a);
2250 prog->statements[i].operand[1] = remapglobal(b);
2251 prog->statements[i].operand[2] = -1;
2252 prog->statements[i].jumpabsolute = -1;
2256 if ( a < prog->progs_numglobals)
2257 if ( prog->globals.ip[remapglobal(a)] >= 0 )
2258 if ( prog->globals.ip[remapglobal(a)] < prog->progs_numfunctions )
2259 if ( prog->functions[prog->globals.ip[remapglobal(a)]].first_statement == -642 )
2260 ++prog->numexplicitcoveragestatements;
2271 if ( a >= prog->progs_numglobals)
2272 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2273 prog->statements[i].op = op;
2274 prog->statements[i].operand[0] = remapglobal(a);
2275 prog->statements[i].operand[1] = -1;
2276 prog->statements[i].operand[2] = -1;
2277 prog->statements[i].jumpabsolute = -1;
2281 if(prog->numstatements < 1)
2283 prog->error_cmd("PRVM_LoadProgs: empty program in %s", prog->name);
2285 else switch(prog->statements[prog->numstatements - 1].op)
2292 prog->error_cmd("PRVM_LoadProgs: program may fall off the edge (does not end with RETURN, GOTO or DONE) in %s", prog->name);
2296 // we're done with the file now
2298 Mem_Free(dprograms);
2301 // check required functions
2302 for(i=0 ; i < numrequiredfunc ; i++)
2303 if(PRVM_ED_FindFunction(prog, required_func[i]) == 0)
2304 prog->error_cmd("%s: %s not found in %s",prog->name, required_func[i], filename);
2306 PRVM_LoadLNO(prog, filename);
2308 PRVM_Init_Exec(prog);
2310 if(*prvm_language.string)
2311 // in CSQC we really shouldn't be able to change how stuff works... sorry for now
2312 // later idea: include a list of authorized .po file checksums with the csprogs
2314 qboolean deftrans = prog == CLVM_prog;
2315 const char *realfilename = (prog != CLVM_prog ? filename : csqc_progname.string);
2316 if(deftrans) // once we have dotranslate_ strings, ALWAYS use the opt-in method!
2318 for (i=0 ; i<prog->numglobaldefs ; i++)
2321 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2322 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2323 if(name && !strncmp(name, "dotranslate_", 12))
2330 if(!strcmp(prvm_language.string, "dump"))
2332 qfile_t *f = FS_OpenRealFile(va(vabuf, sizeof(vabuf), "%s.pot", realfilename), "w", false);
2333 Con_Printf("Dumping to %s.pot\n", realfilename);
2336 for (i=0 ; i<prog->numglobaldefs ; i++)
2339 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2340 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2341 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2343 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2344 const char *value = PRVM_GetString(prog, val->string);
2347 char buf[MAX_INPUTLINE];
2348 PRVM_PO_UnparseString(buf, value, sizeof(buf));
2349 FS_Printf(f, "msgid \"%s\"\nmsgstr \"\"\n\n", buf);
2358 po_t *po = PRVM_PO_Load(
2359 va(vabuf, sizeof(vabuf), "%s.%s.po", realfilename, prvm_language.string),
2360 va(vabuf2, sizeof(vabuf2), "common.%s.po", prvm_language.string),
2361 prog->progs_mempool);
2364 for (i=0 ; i<prog->numglobaldefs ; i++)
2367 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2368 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2369 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2371 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2372 const char *value = PRVM_GetString(prog, val->string);
2375 value = PRVM_PO_Lookup(po, value);
2377 val->string = PRVM_SetEngineString(prog, value);
2385 for (cvar = prog->console_cmd->cvars->vars; cvar; cvar = cvar->next)
2386 cvar->globaldefindex[prog - prvm_prog_list] = -1;
2388 for (i=0 ; i<prog->numglobaldefs ; i++)
2391 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2392 //Con_Printf("found var %s\n", name);
2394 && !strncmp(name, "autocvar_", 9)
2395 && !(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
2398 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2399 cvar = Cvar_FindVar(prog->console_cmd->cvars, name + 9, prog->console_cmd->cvars_flagsmask);
2400 //Con_Printf("PRVM_LoadProgs: autocvar global %s in %s, processing...\n", name, prog->name);
2405 Con_DPrintf("PRVM_LoadProgs: no cvar for autocvar global %s in %s, creating...\n", name, prog->name);
2406 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2409 if((float)((int)(val->_float)) == val->_float)
2410 dpsnprintf(buf, sizeof(buf), "%i", (int)(val->_float));
2412 dpsnprintf(buf, sizeof(buf), "%.9g", val->_float);
2416 dpsnprintf(buf, sizeof(buf), "%.9g %.9g %.9g", val->vector[0], val->vector[1], val->vector[2]); value = buf;
2419 value = PRVM_GetString(prog, val->string);
2422 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2425 cvar = Cvar_Get(prog->console_cmd->cvars, name + 9, value, prog->console_cmd->cvars_flagsmask, NULL);
2426 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2428 val->string = PRVM_SetEngineString(prog, cvar->string);
2429 cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2432 prog->error_cmd("PRVM_LoadProgs: could not create cvar for autocvar global %s in %s", name, prog->name);
2433 cvar->globaldefindex[prog - prvm_prog_list] = i;
2435 else if((cvar->flags & CVAR_PRIVATE) == 0)
2437 // MUST BE SYNCED WITH cvar.c Cvar_Set
2440 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2443 val->_float = cvar->value;
2447 VectorClear(val->vector);
2448 for (j = 0;j < 3;j++)
2450 while (*s && ISWHITESPACE(*s))
2454 val->vector[j] = atof(s);
2455 while (!ISWHITESPACE(*s))
2462 val->string = PRVM_SetEngineString(prog, cvar->string);
2463 cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2466 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2469 cvar->globaldefindex[prog - prvm_prog_list] = i;
2472 Con_Printf("PRVM_LoadProgs: private cvar for autocvar global %s in %s\n", name, prog->name);
2478 prog->loaded = true;
2480 PRVM_UpdateBreakpoints(prog);
2482 // set flags & ddef_ts in prog
2486 PRVM_FindOffsets(prog);
2488 prog->init_cmd(prog);
2491 PRVM_MEM_Alloc(prog);
2493 // Inittime is at least the time when this function finished. However,
2494 // later events may bump it.
2495 prog->inittime = realtime;
2499 static void PRVM_Fields_f(cmd_state_t *cmd)
2502 int i, j, ednum, used, usedamount;
2504 char tempstring[MAX_INPUTLINE], tempstring2[260];
2514 Con_Print("no progs loaded\n");
2519 if(Cmd_Argc(cmd) != 2)
2521 Con_Print("prvm_fields <program name>\n");
2525 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2528 counts = (int *)Mem_Alloc(tempmempool, prog->numfielddefs * sizeof(int));
2529 for (ednum = 0;ednum < prog->max_edicts;ednum++)
2531 ed = PRVM_EDICT_NUM(ednum);
2532 if (ed->priv.required->free)
2534 for (i = 1;i < prog->numfielddefs;i++)
2536 d = &prog->fielddefs[i];
2537 name = PRVM_GetString(prog, d->s_name);
2538 if (name[strlen(name)-2] == '_')
2539 continue; // skip _x, _y, _z vars
2540 val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
2541 // if the value is still all 0, skip the field
2542 for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
2544 if (val->ivector[j])
2555 for (i = 0;i < prog->numfielddefs;i++)
2557 d = &prog->fielddefs[i];
2558 name = PRVM_GetString(prog, d->s_name);
2559 if (name[strlen(name)-2] == '_')
2560 continue; // skip _x, _y, _z vars
2561 switch(d->type & ~DEF_SAVEGLOBAL)
2564 strlcat(tempstring, "string ", sizeof(tempstring));
2567 strlcat(tempstring, "entity ", sizeof(tempstring));
2570 strlcat(tempstring, "function ", sizeof(tempstring));
2573 strlcat(tempstring, "field ", sizeof(tempstring));
2576 strlcat(tempstring, "void ", sizeof(tempstring));
2579 strlcat(tempstring, "float ", sizeof(tempstring));
2582 strlcat(tempstring, "vector ", sizeof(tempstring));
2585 strlcat(tempstring, "pointer ", sizeof(tempstring));
2588 dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
2589 strlcat(tempstring, tempstring2, sizeof(tempstring));
2592 if (strlen(name) > sizeof(tempstring2)-4)
2594 memcpy (tempstring2, name, sizeof(tempstring2)-4);
2595 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
2596 tempstring2[sizeof(tempstring2)-1] = 0;
2599 strlcat(tempstring, name, sizeof(tempstring));
2600 for (j = (int)strlen(name);j < 25;j++)
2601 strlcat(tempstring, " ", sizeof(tempstring));
2602 dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
2603 strlcat(tempstring, tempstring2, sizeof(tempstring));
2604 strlcat(tempstring, "\n", sizeof(tempstring));
2605 if (strlen(tempstring) >= sizeof(tempstring)/2)
2607 Con_Print(tempstring);
2613 usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
2617 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);
2620 static void PRVM_Globals_f(cmd_state_t *cmd)
2624 const char *wildcard;
2630 Con_Print("no progs loaded\n");
2633 if(Cmd_Argc (cmd) < 2 || Cmd_Argc(cmd) > 3)
2635 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
2639 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2642 if( Cmd_Argc(cmd) == 3)
2643 wildcard = Cmd_Argv(cmd, 2);
2647 Con_Printf("%s :", prog->name);
2649 for (i = 0;i < prog->numglobaldefs;i++)
2652 if( !matchpattern( PRVM_GetString(prog, prog->globaldefs[i].s_name), wildcard, 1) )
2657 Con_Printf("%s\n", PRVM_GetString(prog, prog->globaldefs[i].s_name));
2659 Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->numglobals, numculled, prog->numglobals * 4);
2667 static void PRVM_Global_f(cmd_state_t *cmd)
2671 char valuebuf[MAX_INPUTLINE];
2672 if( Cmd_Argc(cmd) != 3 ) {
2673 Con_Printf( "prvm_global <program name> <global name>\n" );
2677 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2680 global = PRVM_ED_FindGlobal( prog, Cmd_Argv(cmd, 2) );
2682 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(cmd, 2), Cmd_Argv(cmd, 1) );
2684 Con_Printf( "%s: %s\n", Cmd_Argv(cmd, 2), PRVM_ValueString( prog, (etype_t)global->type, PRVM_GLOBALFIELDVALUE(global->ofs), valuebuf, sizeof(valuebuf) ) );
2692 static void PRVM_GlobalSet_f(cmd_state_t *cmd)
2696 if( Cmd_Argc(cmd) != 4 ) {
2697 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2701 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2704 global = PRVM_ED_FindGlobal( prog, Cmd_Argv(cmd, 2) );
2706 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(cmd, 2), Cmd_Argv(cmd, 1) );
2708 PRVM_ED_ParseEpair( prog, NULL, global, Cmd_Argv(cmd, 3), true );
2712 ======================
2713 Break- and Watchpoints
2714 ======================
2718 char break_statement[256];
2719 char watch_global[256];
2721 char watch_field[256];
2724 static debug_data_t debug_data[PRVM_PROG_MAX];
2726 void PRVM_Breakpoint(prvm_prog_t *prog, int stack_index, const char *text)
2729 Con_Printf("PRVM_Breakpoint: %s\n", text);
2730 PRVM_PrintState(prog, stack_index);
2731 if (prvm_breakpointdump.integer)
2732 Host_Savegame_to(prog, va(vabuf, sizeof(vabuf), "breakpoint-%s.dmp", prog->name));
2735 void PRVM_Watchpoint(prvm_prog_t *prog, int stack_index, const char *text, etype_t type, prvm_eval_t *o, prvm_eval_t *n)
2737 size_t sz = sizeof(prvm_vec_t) * ((type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2738 if (memcmp(o, n, sz))
2741 char valuebuf_o[128];
2742 char valuebuf_n[128];
2743 PRVM_UglyValueString(prog, type, o, valuebuf_o, sizeof(valuebuf_o));
2744 PRVM_UglyValueString(prog, type, n, valuebuf_n, sizeof(valuebuf_n));
2745 dpsnprintf(buf, sizeof(buf), "%s: %s -> %s", text, valuebuf_o, valuebuf_n);
2746 PRVM_Breakpoint(prog, stack_index, buf);
2751 static void PRVM_UpdateBreakpoints(prvm_prog_t *prog)
2753 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2756 if (debug->break_statement[0])
2758 if (debug->break_statement[0] >= '0' && debug->break_statement[0] <= '9')
2760 prog->break_statement = atoi(debug->break_statement);
2761 prog->break_stack_index = 0;
2766 func = PRVM_ED_FindFunction (prog, debug->break_statement);
2769 Con_Printf("%s progs: no function or statement named %s to break on!\n", prog->name, debug->break_statement);
2770 prog->break_statement = -1;
2774 prog->break_statement = func->first_statement;
2775 prog->break_stack_index = 1;
2778 if (prog->break_statement >= -1)
2779 Con_Printf("%s progs: breakpoint is at statement %d\n", prog->name, prog->break_statement);
2782 prog->break_statement = -1;
2784 if (debug->watch_global[0])
2786 ddef_t *global = PRVM_ED_FindGlobal( prog, debug->watch_global );
2789 Con_Printf( "%s progs: no global named '%s' to watch!\n", prog->name, debug->watch_global );
2790 prog->watch_global_type = ev_void;
2794 size_t sz = sizeof(prvm_vec_t) * ((global->type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2795 prog->watch_global = global->ofs;
2796 prog->watch_global_type = (etype_t)global->type;
2797 memcpy(&prog->watch_global_value, PRVM_GLOBALFIELDVALUE(prog->watch_global), sz);
2799 if (prog->watch_global_type != ev_void)
2800 Con_Printf("%s progs: global watchpoint is at global index %d\n", prog->name, prog->watch_global);
2803 prog->watch_global_type = ev_void;
2805 if (debug->watch_field[0])
2807 ddef_t *field = PRVM_ED_FindField( prog, debug->watch_field );
2810 Con_Printf( "%s progs: no field named '%s' to watch!\n", prog->name, debug->watch_field );
2811 prog->watch_field_type = ev_void;
2815 size_t sz = sizeof(prvm_vec_t) * ((field->type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2816 prog->watch_edict = debug->watch_edict;
2817 prog->watch_field = field->ofs;
2818 prog->watch_field_type = (etype_t)field->type;
2819 if (prog->watch_edict < prog->num_edicts)
2820 memcpy(&prog->watch_edictfield_value, PRVM_EDICTFIELDVALUE(PRVM_EDICT_NUM(prog->watch_edict), prog->watch_field), sz);
2822 memset(&prog->watch_edictfield_value, 0, sz);
2824 if (prog->watch_edict != ev_void)
2825 Con_Printf("%s progs: edict field watchpoint is at edict %d field index %d\n", prog->name, prog->watch_edict, prog->watch_field);
2828 prog->watch_field_type = ev_void;
2831 static void PRVM_Breakpoint_f(cmd_state_t *cmd)
2835 if( Cmd_Argc(cmd) == 2 ) {
2836 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2839 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2840 debug->break_statement[0] = 0;
2842 PRVM_UpdateBreakpoints(prog);
2845 if( Cmd_Argc(cmd) != 3 ) {
2846 Con_Printf( "prvm_breakpoint <program name> <function name | statement>\n" );
2850 if (!(prog = PRVM_ProgFromString(Cmd_Argv(cmd, 1))))
2854 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2855 strlcpy(debug->break_statement, Cmd_Argv(cmd, 2), sizeof(debug->break_statement));
2857 PRVM_UpdateBreakpoints(prog);
2860 static void PRVM_GlobalWatchpoint_f(cmd_state_t *cmd)
2864 if( Cmd_Argc(cmd) == 2 ) {
2865 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2868 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2869 debug->watch_global[0] = 0;
2871 PRVM_UpdateBreakpoints(prog);
2874 if( Cmd_Argc(cmd) != 3 ) {
2875 Con_Printf( "prvm_globalwatchpoint <program name> <global name>\n" );
2879 if (!(prog = PRVM_ProgFromString(Cmd_Argv(cmd, 1))))
2883 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2884 strlcpy(debug->watch_global, Cmd_Argv(cmd, 2), sizeof(debug->watch_global));
2886 PRVM_UpdateBreakpoints(prog);
2889 static void PRVM_EdictWatchpoint_f(cmd_state_t *cmd)
2893 if( Cmd_Argc(cmd) == 2 ) {
2894 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2897 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2898 debug->watch_field[0] = 0;
2900 PRVM_UpdateBreakpoints(prog);
2903 if( Cmd_Argc(cmd) != 4 ) {
2904 Con_Printf( "prvm_edictwatchpoint <program name> <edict number> <field name>\n" );
2908 if (!(prog = PRVM_ProgFromString(Cmd_Argv(cmd, 1))))
2912 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2913 debug->watch_edict = atoi(Cmd_Argv(cmd, 2));
2914 strlcpy(debug->watch_field, Cmd_Argv(cmd, 3), sizeof(debug->watch_field));
2916 PRVM_UpdateBreakpoints(prog);
2924 void PRVM_Init (void)
2926 Cmd_AddCommand(&cmd_client, "prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2927 Cmd_AddCommand(&cmd_client, "prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2928 Cmd_AddCommand(&cmd_client, "prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2929 Cmd_AddCommand(&cmd_client, "prvm_profile", PRVM_Profile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu)");
2930 Cmd_AddCommand(&cmd_client, "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");
2931 Cmd_AddCommand(&cmd_client, "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)");
2932 Cmd_AddCommand(&cmd_client, "prvm_fields", PRVM_Fields_f, "prints usage statistics on properties (how many entities have non-zero values) in the selected VM (server, client, menu)");
2933 Cmd_AddCommand(&cmd_client, "prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2934 Cmd_AddCommand(&cmd_client, "prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2935 Cmd_AddCommand(&cmd_client, "prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2936 Cmd_AddCommand(&cmd_client, "prvm_edictset", PRVM_ED_EdictSet_f, "changes value of a specified property of a specified entity in the selected VM (server, client, menu)");
2937 Cmd_AddCommand(&cmd_client, "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");
2938 Cmd_AddCommand(&cmd_client, "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");
2939 Cmd_AddCommand(&cmd_client, "prvm_printfunction", PRVM_PrintFunction_f, "prints a disassembly (QuakeC instructions) of the specified function in the selected VM (server, client, menu)");
2940 Cmd_AddCommand(&cmd_client, "cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2941 Cmd_AddCommand(&cmd_client, "menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2942 Cmd_AddCommand(&cmd_client, "sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2943 Cmd_AddCommand(&cmd_client, "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");
2944 Cmd_AddCommand(&cmd_client, "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");
2945 Cmd_AddCommand(&cmd_client, "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");
2947 Cmd_AddCommand(&cmd_server, "prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2948 Cmd_AddCommand(&cmd_server, "prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2949 Cmd_AddCommand(&cmd_server, "prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2950 Cmd_AddCommand(&cmd_server, "prvm_profile", PRVM_Profile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu)");
2951 Cmd_AddCommand(&cmd_server, "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");
2952 Cmd_AddCommand(&cmd_server, "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)");
2953 Cmd_AddCommand(&cmd_server, "prvm_fields", PRVM_Fields_f, "prints usage statistics on properties (how many entities have non-zero values) in the selected VM (server, client, menu)");
2954 Cmd_AddCommand(&cmd_server, "prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2955 Cmd_AddCommand(&cmd_server, "prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2956 Cmd_AddCommand(&cmd_server, "prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2957 Cmd_AddCommand(&cmd_server, "prvm_edictset", PRVM_ED_EdictSet_f, "changes value of a specified property of a specified entity in the selected VM (server, client, menu)");
2958 Cmd_AddCommand(&cmd_server, "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");
2959 Cmd_AddCommand(&cmd_server, "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");
2960 Cmd_AddCommand(&cmd_server, "prvm_printfunction", PRVM_PrintFunction_f, "prints a disassembly (QuakeC instructions) of the specified function in the selected VM (server, client, menu)");
2961 Cmd_AddCommand(&cmd_server, "cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2962 Cmd_AddCommand(&cmd_server, "menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2963 Cmd_AddCommand(&cmd_server, "sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2964 Cmd_AddCommand(&cmd_server, "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");
2965 Cmd_AddCommand(&cmd_server, "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");
2966 Cmd_AddCommand(&cmd_server, "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");
2968 Cvar_RegisterVariable (&prvm_language);
2969 Cvar_RegisterVariable (&prvm_traceqc);
2970 Cvar_RegisterVariable (&prvm_statementprofiling);
2971 Cvar_RegisterVariable (&prvm_timeprofiling);
2972 Cvar_RegisterVariable (&prvm_coverage);
2973 Cvar_RegisterVariable (&prvm_backtraceforwarnings);
2974 Cvar_RegisterVariable (&prvm_leaktest);
2975 Cvar_RegisterVariable (&prvm_leaktest_follow_targetname);
2976 Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames);
2977 Cvar_RegisterVariable (&prvm_errordump);
2978 Cvar_RegisterVariable (&prvm_breakpointdump);
2979 Cvar_RegisterVariable (&prvm_reuseedicts_startuptime);
2980 Cvar_RegisterVariable (&prvm_reuseedicts_neverinsameframe);
2981 Cvar_RegisterVariable (&prvm_garbagecollection_enable);
2982 Cvar_RegisterVariable (&prvm_garbagecollection_notify);
2983 Cvar_RegisterVariable (&prvm_garbagecollection_scan_limit);
2984 Cvar_RegisterVariable (&prvm_garbagecollection_strings);
2985 Cvar_RegisterVariable (&prvm_stringdebug);
2987 // COMMANDLINEOPTION: PRVM: -norunaway disables the runaway loop check (it might be impossible to exit DarkPlaces if used!)
2988 prvm_runawaycheck = !COM_CheckParm("-norunaway");
2998 void PRVM_Prog_Init(prvm_prog_t *prog, cmd_state_t *cmd)
3000 PRVM_Prog_Reset(prog);
3001 prog->leaktest_active = prvm_leaktest.integer != 0;
3002 prog->console_cmd = cmd;
3005 // LadyHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
3006 unsigned int PRVM_EDICT_NUM_ERROR(prvm_prog_t *prog, unsigned int n, const char *filename, int fileline)
3008 prog->error_cmd("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", prog->name, n, filename, fileline);
3012 #define PRVM_KNOWNSTRINGBASE 0x40000000
3014 const char *PRVM_GetString(prvm_prog_t *prog, int num)
3019 if (prvm_stringdebug.integer)
3020 VM_Warning(prog, "PRVM_GetString: Invalid string offset (%i < 0)\n", num);
3023 else if (num < prog->stringssize)
3025 // constant string from progs.dat
3026 return prog->strings + num;
3028 else if (num <= prog->stringssize + prog->tempstringsbuf.maxsize)
3030 // tempstring returned by engine to QC (becomes invalid after returning to engine)
3031 num -= prog->stringssize;
3032 if (num < prog->tempstringsbuf.cursize)
3033 return (char *)prog->tempstringsbuf.data + num;
3036 if (prvm_stringdebug.integer)
3037 VM_Warning(prog, "PRVM_GetString: Invalid temp-string offset (%i >= %i prog->tempstringsbuf.cursize)\n", num, prog->tempstringsbuf.cursize);
3041 else if (num & PRVM_KNOWNSTRINGBASE)
3044 num = num - PRVM_KNOWNSTRINGBASE;
3045 if (num >= 0 && num < prog->numknownstrings)
3047 if (!prog->knownstrings[num])
3049 if (prvm_stringdebug.integer)
3050 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
3053 // refresh the garbage collection on the string - this guards
3054 // against a certain sort of repeated migration to earlier
3055 // points in the scan that could otherwise result in the string
3056 // being freed for being unused
3057 prog->knownstrings_flags[num] = (prog->knownstrings_flags[num] & ~KNOWNSTRINGFLAG_GCPRUNE) | KNOWNSTRINGFLAG_GCMARK;
3058 return prog->knownstrings[num];
3062 if (prvm_stringdebug.integer)
3063 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
3069 // invalid string offset
3070 if (prvm_stringdebug.integer)
3071 VM_Warning(prog, "PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
3076 const char *PRVM_ChangeEngineString(prvm_prog_t *prog, int i, const char *s)
3079 i = i - PRVM_KNOWNSTRINGBASE;
3080 if (i < 0 || i >= prog->numknownstrings)
3081 prog->error_cmd("PRVM_ChangeEngineString: string index %i is out of bounds", i);
3082 else if ((prog->knownstrings_flags[i] & KNOWNSTRINGFLAG_ENGINE) == 0)
3083 prog->error_cmd("PRVM_ChangeEngineString: string index %i is not an engine string", i);
3084 old = prog->knownstrings[i];
3085 prog->knownstrings[i] = s;
3089 static void PRVM_NewKnownString(prvm_prog_t *prog, int i, int flags, const char *s)
3091 if (i >= prog->numknownstrings)
3093 if (i >= prog->maxknownstrings)
3095 const char **oldstrings = prog->knownstrings;
3096 const unsigned char *oldstrings_flags = prog->knownstrings_flags;
3097 const char **oldstrings_origin = prog->knownstrings_origin;
3098 prog->maxknownstrings += 128;
3099 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3100 prog->knownstrings_flags = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
3101 if (prog->leaktest_active)
3102 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3103 if (prog->numknownstrings)
3105 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
3106 memcpy((char **)prog->knownstrings_flags, oldstrings_flags, prog->numknownstrings * sizeof(unsigned char));
3107 if (prog->leaktest_active)
3108 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
3111 prog->numknownstrings++;
3113 prog->firstfreeknownstring = i + 1;
3114 prog->knownstrings[i] = s;
3115 // it's in use right now, spare it until the next gc pass - that said, it is not freeable so this is probably moot
3116 prog->knownstrings_flags[i] = flags;
3117 if (prog->leaktest_active)
3118 prog->knownstrings_origin[i] = NULL;
3121 int PRVM_SetEngineString(prvm_prog_t *prog, const char *s)
3126 if (s >= prog->strings && s <= prog->strings + prog->stringssize)
3127 prog->error_cmd("PRVM_SetEngineString: s in prog->strings area");
3128 // if it's in the tempstrings area, use a reserved range
3129 // (otherwise we'd get millions of useless string offsets cluttering the database)
3130 if (s >= (char *)prog->tempstringsbuf.data && s < (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.maxsize)
3131 return prog->stringssize + (s - (char *)prog->tempstringsbuf.data);
3132 // see if it's a known string address
3133 for (i = 0;i < prog->numknownstrings;i++)
3134 if (prog->knownstrings[i] == s)
3135 return PRVM_KNOWNSTRINGBASE + i;
3136 // new unknown engine string
3137 if (developer_insane.integer)
3138 Con_DPrintf("new engine string %p = \"%s\"\n", s, s);
3139 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3140 if (!prog->knownstrings[i])
3142 PRVM_NewKnownString(prog, i, KNOWNSTRINGFLAG_GCMARK | KNOWNSTRINGFLAG_ENGINE, s);
3143 return PRVM_KNOWNSTRINGBASE + i;
3146 // temp string handling
3148 // all tempstrings go into this buffer consecutively, and it is reset
3149 // whenever PRVM_ExecuteProgram returns to the engine
3150 // (technically each PRVM_ExecuteProgram call saves the cursize value and
3151 // restores it on return, so multiple recursive calls can share the same
3153 // the buffer size is automatically grown as needed
3155 int PRVM_SetTempString(prvm_prog_t *prog, const char *s)
3161 size = (int)strlen(s) + 1;
3162 if (developer_insane.integer)
3163 Con_DPrintf("PRVM_SetTempString: cursize %i, size %i\n", prog->tempstringsbuf.cursize, size);
3164 if (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
3166 sizebuf_t old = prog->tempstringsbuf;
3167 if (prog->tempstringsbuf.cursize + size >= 1<<28)
3168 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);
3169 prog->tempstringsbuf.maxsize = max(prog->tempstringsbuf.maxsize, 65536);
3170 while (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
3171 prog->tempstringsbuf.maxsize *= 2;
3172 if (prog->tempstringsbuf.maxsize != old.maxsize || prog->tempstringsbuf.data == NULL)
3174 Con_DPrintf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, prog->tempstringsbuf.maxsize/1024);
3175 prog->tempstringsbuf.data = (unsigned char *) Mem_Alloc(prog->progs_mempool, prog->tempstringsbuf.maxsize);
3179 memcpy(prog->tempstringsbuf.data, old.data, old.cursize);
3184 t = (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.cursize;
3186 prog->tempstringsbuf.cursize += size;
3187 return PRVM_SetEngineString(prog, t);
3190 int PRVM_AllocString(prvm_prog_t *prog, size_t bufferlength, char **pointer)
3200 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3201 if (!prog->knownstrings[i])
3203 s = PRVM_Alloc(bufferlength);
3204 PRVM_NewKnownString(prog, i, KNOWNSTRINGFLAG_GCMARK, s);
3205 if(prog->leaktest_active)
3206 prog->knownstrings_origin[i] = PRVM_AllocationOrigin(prog);
3208 *pointer = (char *)(prog->knownstrings[i]);
3209 return PRVM_KNOWNSTRINGBASE + i;
3212 void PRVM_FreeString(prvm_prog_t *prog, int num)
3215 prog->error_cmd("PRVM_FreeString: attempt to free a NULL string");
3216 else if (num >= 0 && num < prog->stringssize)
3217 prog->error_cmd("PRVM_FreeString: attempt to free a constant string");
3218 else if (num >= PRVM_KNOWNSTRINGBASE && num < PRVM_KNOWNSTRINGBASE + prog->numknownstrings)
3220 num = num - PRVM_KNOWNSTRINGBASE;
3221 if (!prog->knownstrings[num])
3222 prog->error_cmd("PRVM_FreeString: attempt to free a non-existent or already freed string");
3223 if (!prog->knownstrings_flags[num])
3224 prog->error_cmd("PRVM_FreeString: attempt to free a string owned by the engine");
3225 PRVM_Free((char *)prog->knownstrings[num]);
3226 if(prog->leaktest_active)
3227 if(prog->knownstrings_origin[num])
3228 PRVM_Free((char *)prog->knownstrings_origin[num]);
3229 prog->knownstrings[num] = NULL;
3230 prog->knownstrings_flags[num] = 0;
3231 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
3234 prog->error_cmd("PRVM_FreeString: invalid string offset %i", num);
3237 static qboolean PRVM_IsStringReferenced(prvm_prog_t *prog, string_t string)
3241 for (i = 0;i < prog->numglobaldefs;i++)
3243 ddef_t *d = &prog->globaldefs[i];
3244 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3246 if(string == PRVM_GLOBALFIELDSTRING(d->ofs))
3250 for(j = 0; j < prog->num_edicts; ++j)
3252 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3253 if (ed->priv.required->free)
3255 for (i=0; i<prog->numfielddefs; ++i)
3257 ddef_t *d = &prog->fielddefs[i];
3258 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3260 if(string == PRVM_EDICTFIELDSTRING(ed, d->ofs))
3268 static qboolean PRVM_IsEdictRelevant(prvm_prog_t *prog, prvm_edict_t *edict)
3272 if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
3273 return true; // world or clients
3274 if (edict->priv.required->freetime <= prog->inittime)
3275 return true; // created during startup
3276 if (prog == SVVM_prog)
3278 if(PRVM_serveredictfloat(edict, solid)) // can block other stuff, or is a trigger?
3280 if(PRVM_serveredictfloat(edict, modelindex)) // visible ent?
3282 if(PRVM_serveredictfloat(edict, effects)) // particle effect?
3284 if(PRVM_serveredictfunction(edict, think)) // has a think function?
3285 if(PRVM_serveredictfloat(edict, nextthink) > 0) // that actually will eventually run?
3287 if(PRVM_serveredictfloat(edict, takedamage))
3289 if(*prvm_leaktest_ignore_classnames.string)
3291 if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_serveredictstring(edict, classname)))))
3295 else if (prog == CLVM_prog)
3297 // TODO someone add more stuff here
3298 if(PRVM_clientedictfloat(edict, entnum)) // csqc networked
3300 if(PRVM_clientedictfloat(edict, modelindex)) // visible ent?
3302 if(PRVM_clientedictfloat(edict, effects)) // particle effect?
3304 if(PRVM_clientedictfunction(edict, think)) // has a think function?
3305 if(PRVM_clientedictfloat(edict, nextthink) > 0) // that actually will eventually run?
3307 if(*prvm_leaktest_ignore_classnames.string)
3309 if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_clientedictstring(edict, classname)))))
3315 // menu prog does not have classnames
3320 static qboolean PRVM_IsEdictReferenced(prvm_prog_t *prog, prvm_edict_t *edict, int mark)
3323 int edictnum = PRVM_NUM_FOR_EDICT(edict);
3324 const char *targetname = NULL;
3326 if (prog == SVVM_prog && prvm_leaktest_follow_targetname.integer)
3327 targetname = PRVM_GetString(prog, PRVM_serveredictstring(edict, targetname));
3330 if(!*targetname) // ""
3333 for(j = 0; j < prog->num_edicts; ++j)
3335 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3336 if (ed->priv.required->mark < mark)
3342 const char *target = PRVM_GetString(prog, PRVM_serveredictstring(ed, target));
3344 if(!strcmp(target, targetname))
3347 for (i=0; i<prog->numfielddefs; ++i)
3349 ddef_t *d = &prog->fielddefs[i];
3350 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3352 if(edictnum == PRVM_EDICTFIELDEDICT(ed, d->ofs))
3360 static void PRVM_MarkReferencedEdicts(prvm_prog_t *prog)
3366 // Stage 1: world, all entities that are relevant, and all entities that are referenced by globals.
3368 for(j = 0; j < prog->num_edicts; ++j)
3370 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3371 if(ed->priv.required->free)
3373 ed->priv.required->mark = PRVM_IsEdictRelevant(prog, ed) ? stage : 0;
3375 for (i = 0;i < prog->numglobaldefs;i++)
3377 ddef_t *d = &prog->globaldefs[i];
3379 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3381 j = PRVM_GLOBALFIELDEDICT(d->ofs);
3382 if (i < 0 || j >= prog->max_edicts) {
3383 Con_Printf("Invalid entity reference from global %s.\n", PRVM_GetString(prog, d->s_name));
3386 ed = PRVM_EDICT_NUM(j);;
3387 ed->priv.required->mark = stage;
3390 // Future stages: all entities that are referenced by an entity of the previous stage.
3394 for(j = 0; j < prog->num_edicts; ++j)
3396 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3397 if(ed->priv.required->free)
3399 if(ed->priv.required->mark)
3401 if(PRVM_IsEdictReferenced(prog, ed, stage))
3403 ed->priv.required->mark = stage + 1;
3410 Con_DPrintf("leak check used %d stages to find all references\n", stage);
3413 void PRVM_LeakTest(prvm_prog_t *prog)
3416 qboolean leaked = false;
3418 if(!prog->leaktest_active)
3422 for (i = 0; i < prog->numknownstrings; ++i)
3424 if(prog->knownstrings[i])
3425 if(prog->knownstrings_flags[i])
3426 if(prog->knownstrings_origin[i])
3427 if(!PRVM_IsStringReferenced(prog, PRVM_KNOWNSTRINGBASE + i))
3429 Con_Printf("Unreferenced string found!\n Value: %s\n Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
3435 PRVM_MarkReferencedEdicts(prog);
3436 for(j = 0; j < prog->num_edicts; ++j)
3438 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3439 if(ed->priv.required->free)
3441 if(!ed->priv.required->mark)
3442 if(ed->priv.required->allocation_origin)
3444 Con_Printf("Unreferenced edict found!\n Allocated at: %s\n", ed->priv.required->allocation_origin);
3445 PRVM_ED_Print(prog, ed, NULL);
3450 ed->priv.required->mark = 0; // clear marks again when done
3453 for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
3455 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
3457 if(stringbuffer->origin)
3459 Con_Printf("Open string buffer handle found!\n Allocated at: %s\n", stringbuffer->origin);
3464 for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
3466 if(prog->openfiles[i])
3467 if(prog->openfiles_origin[i])
3469 Con_Printf("Open file handle found!\n Allocated at: %s\n", prog->openfiles_origin[i]);
3474 for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
3476 if(prog->opensearches[i])
3477 if(prog->opensearches_origin[i])
3479 Con_Printf("Open search handle found!\n Allocated at: %s\n", prog->opensearches_origin[i]);
3485 Con_Printf("Congratulations. No leaks found.\n");
3488 void PRVM_GarbageCollection(prvm_prog_t *prog)
3490 int limit = prvm_garbagecollection_scan_limit.integer;
3491 prvm_prog_garbagecollection_state_t *gc = &prog->gc;
3492 if (!prvm_garbagecollection_enable.integer)
3495 // we like to limit how much scanning we do so it doesn't put a significant
3496 // burden on the cpu, so each of these are not complete scans, we also like
3497 // to have consistent cpu usage so we do a bit of work on each category of
3498 // leaked object every frame
3504 case PRVM_GC_GLOBALS_MARK:
3505 for (; gc->globals_mark_progress < prog->numglobaldefs && (limit--) > 0; gc->globals_mark_progress++)
3507 ddef_t *d = &prog->globaldefs[gc->globals_mark_progress];
3512 prvm_int_t s = prog->globals.ip[d->ofs];
3513 if (s & PRVM_KNOWNSTRINGBASE)
3515 prvm_int_t num = s - PRVM_KNOWNSTRINGBASE;
3516 if (!prog->knownstrings[num])
3519 Con_DPrintf("PRVM_GarbageCollection: Found bogus strzone reference in global %i (global name: \"%s\"), erasing reference", d->ofs, PRVM_GetString(prog, d->s_name));
3520 prog->globals.ip[d->ofs] = 0;
3523 prog->knownstrings_flags[num] = (prog->knownstrings_flags[num] | KNOWNSTRINGFLAG_GCMARK) & ~KNOWNSTRINGFLAG_GCPRUNE;
3531 if (gc->globals_mark_progress >= prog->numglobaldefs)
3534 case PRVM_GC_FIELDS_MARK:
3535 for (; gc->fields_mark_progress < prog->numfielddefs && limit > 0;)
3537 ddef_t *d = &prog->fielddefs[gc->fields_mark_progress];
3541 //for (gc-> entityindex = 0; entityindex < prog->num_edicts; entityindex++)
3542 for (;gc->fields_mark_progress_entity < prog->num_edicts && (limit--) > 0;gc->fields_mark_progress_entity++)
3544 int entityindex = gc->fields_mark_progress_entity;
3545 prvm_int_t s = prog->edictsfields.ip[entityindex * prog->entityfields + d->ofs];
3546 if (s & PRVM_KNOWNSTRINGBASE)
3548 prvm_int_t num = s - PRVM_KNOWNSTRINGBASE;
3549 if (!prog->knownstrings[num])
3552 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));
3553 prog->edictsfields.ip[entityindex * prog->entityfields + d->ofs] = 0;
3556 prog->knownstrings_flags[num] = (prog->knownstrings_flags[num] | KNOWNSTRINGFLAG_GCMARK) & ~KNOWNSTRINGFLAG_GCPRUNE;
3559 if (gc->fields_mark_progress_entity >= prog->num_edicts)
3561 gc->fields_mark_progress_entity = 0;
3562 gc->fields_mark_progress++;
3566 gc->fields_mark_progress_entity = 0;
3567 gc->fields_mark_progress++;
3571 if (gc->fields_mark_progress >= prog->numfielddefs)
3574 case PRVM_GC_KNOWNSTRINGS_SWEEP:
3575 // free any strzone'd strings that are not marked
3576 if (!prvm_garbagecollection_strings.integer)
3581 for (;gc->knownstrings_sweep_progress < prog->numknownstrings && (limit--) > 0;gc->knownstrings_sweep_progress++)
3583 int num = gc->knownstrings_sweep_progress;
3584 if (prog->knownstrings[num] && (prog->knownstrings_flags[num] & (KNOWNSTRINGFLAG_GCMARK | KNOWNSTRINGFLAG_ENGINE)) == 0)
3586 if (prog->knownstrings_flags[num] & KNOWNSTRINGFLAG_GCPRUNE)
3588 // string has been marked for pruning two passes in a row
3589 if (prvm_garbagecollection_notify.integer)
3590 Con_DPrintf("prvm_garbagecollection_notify: %s: freeing unreferenced string %i: \"%s\"\n", prog->name, num, prog->knownstrings[num]);
3591 Mem_Free((char *)prog->knownstrings[num]);
3592 prog->knownstrings[num] = NULL;
3593 prog->knownstrings_flags[num] = 0;
3594 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
3598 // mark it for pruning next pass
3599 prog->knownstrings_flags[num] |= KNOWNSTRINGFLAG_GCPRUNE;
3603 if (gc->knownstrings_sweep_progress >= prog->numknownstrings)
3608 memset(gc, 0, sizeof(*gc));