2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26 prvm_prog_t prvm_prog_list[PRVM_PROG_MAX];
28 int prvm_type_size[8] = {1,sizeof(string_t)/4,1,3,1,1,sizeof(func_t)/4,sizeof(void *)/4};
30 prvm_eval_t prvm_badvalue; // used only for error returns
32 cvar_t prvm_language = {CVAR_CLIENT | CVAR_SERVER | CVAR_SAVE, "prvm_language", "", "when set, loads PROGSFILE.LANGUAGENAME.po and common.LANGUAGENAME.po for string translations; when set to dump, PROGSFILE.pot is written from the strings in the progs"};
33 // LadyHavoc: prints every opcode as it executes - warning: this is significant spew
34 cvar_t prvm_traceqc = {CVAR_CLIENT | CVAR_SERVER, "prvm_traceqc", "0", "prints every QuakeC statement as it is executed (only for really thorough debugging!)"};
35 // LadyHavoc: counts usage of each QuakeC statement
36 cvar_t prvm_statementprofiling = {CVAR_CLIENT | CVAR_SERVER, "prvm_statementprofiling", "0", "counts how many times each QuakeC statement has been executed, these counts are displayed in prvm_printfunction output (if enabled)"};
37 cvar_t prvm_timeprofiling = {CVAR_CLIENT | CVAR_SERVER, "prvm_timeprofiling", "0", "counts how long each function has been executed, these counts are displayed in prvm_profile output (if enabled)"};
38 cvar_t prvm_coverage = {CVAR_CLIENT | CVAR_SERVER, "prvm_coverage", "0", "report and count coverage events (1: per-function, 2: coverage() builtin, 4: per-statement)"};
39 cvar_t prvm_backtraceforwarnings = {CVAR_CLIENT | CVAR_SERVER, "prvm_backtraceforwarnings", "0", "print a backtrace for warnings too"};
40 cvar_t prvm_leaktest = {CVAR_CLIENT | CVAR_SERVER, "prvm_leaktest", "0", "try to detect memory leaks in strings or entities"};
41 cvar_t prvm_leaktest_follow_targetname = {CVAR_CLIENT | CVAR_SERVER, "prvm_leaktest_follow_targetname", "0", "if set, target/targetname links are considered when leak testing; this should normally not be required, as entities created during startup - e.g. info_notnull - are never considered leaky"};
42 cvar_t prvm_leaktest_ignore_classnames = {CVAR_CLIENT | CVAR_SERVER, "prvm_leaktest_ignore_classnames", "", "classnames of entities to NOT leak check because they are found by find(world, classname, ...) but are actually spawned by QC code (NOT map entities)"};
43 cvar_t prvm_errordump = {CVAR_CLIENT | CVAR_SERVER, "prvm_errordump", "0", "write a savegame on crash to crash-server.dmp"};
44 cvar_t prvm_breakpointdump = {CVAR_CLIENT | CVAR_SERVER, "prvm_breakpointdump", "0", "write a savegame on breakpoint to breakpoint-server.dmp"};
45 cvar_t prvm_reuseedicts_startuptime = {CVAR_CLIENT | CVAR_SERVER, "prvm_reuseedicts_startuptime", "2", "allows immediate re-use of freed entity slots during start of new level (value in seconds)"};
46 cvar_t prvm_reuseedicts_neverinsameframe = {CVAR_CLIENT | CVAR_SERVER, "prvm_reuseedicts_neverinsameframe", "1", "never allows re-use of freed entity slots during same frame"};
47 cvar_t prvm_garbagecollection_enable = {CVAR_CLIENT | CVAR_SERVER, "prvm_garbagecollection_enable", "1", "automatically scan for and free resources that are not referenced by the code being executed in the VM"};
48 cvar_t prvm_garbagecollection_notify = {CVAR_CLIENT | CVAR_SERVER, "prvm_garbagecollection_notify", "0", "print out a notification for each resource freed by garbage collection"};
49 cvar_t prvm_garbagecollection_scan_limit = {CVAR_CLIENT | CVAR_SERVER, "prvm_garbagecollection_scan_limit", "10000", "scan this many fields or resources per frame to free up unreferenced resources"};
50 cvar_t prvm_garbagecollection_strings = {CVAR_CLIENT | CVAR_SERVER, "prvm_garbagecollection_strings", "1", "automatically call strunzone() on strings that are not referenced"};
51 cvar_t prvm_stringdebug = {CVAR_CLIENT | CVAR_SERVER, "prvm_stringdebug", "0", "Print debug and warning messages related to strings"};
53 static double prvm_reuseedicts_always_allow = 0;
54 qboolean prvm_runawaycheck = true;
56 //============================================================================
64 static void PRVM_MEM_Alloc(prvm_prog_t *prog)
68 // reserve space for the null entity aka world
69 // check bound of max_edicts
70 prog->max_edicts = bound(1 + prog->reserved_edicts, prog->max_edicts, prog->limit_edicts);
71 prog->num_edicts = bound(1 + prog->reserved_edicts, prog->num_edicts, prog->max_edicts);
73 // edictprivate_size has to be min as big prvm_edict_private_t
74 prog->edictprivate_size = max(prog->edictprivate_size,(int)sizeof(prvm_edict_private_t));
77 prog->edicts = (prvm_edict_t *)Mem_Alloc(prog->progs_mempool,prog->limit_edicts * sizeof(prvm_edict_t));
79 // alloc edict private space
80 prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size);
83 prog->entityfieldsarea = prog->entityfields * prog->max_edicts;
84 prog->edictsfields.fp = (prvm_vec_t *)Mem_Alloc(prog->progs_mempool, prog->entityfieldsarea * sizeof(prvm_vec_t));
87 for(i = 0; i < prog->max_edicts; i++)
89 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
90 prog->edicts[i].fields.fp = prog->edictsfields.fp + i * prog->entityfields;
96 PRVM_MEM_IncreaseEdicts
99 void PRVM_MEM_IncreaseEdicts(prvm_prog_t *prog)
103 if(prog->max_edicts >= prog->limit_edicts)
106 prog->begin_increase_edicts(prog);
109 prog->max_edicts = min(prog->max_edicts + 256, prog->limit_edicts);
111 prog->entityfieldsarea = prog->entityfields * prog->max_edicts;
112 prog->edictsfields.fp = (prvm_vec_t*)Mem_Realloc(prog->progs_mempool, (void *)prog->edictsfields.fp, prog->entityfieldsarea * sizeof(prvm_vec_t));
113 prog->edictprivate = (void *)Mem_Realloc(prog->progs_mempool, (void *)prog->edictprivate, prog->max_edicts * prog->edictprivate_size);
115 //set e and v pointers
116 for(i = 0; i < prog->max_edicts; i++)
118 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
119 prog->edicts[i].fields.fp = prog->edictsfields.fp + i * prog->entityfields;
122 prog->end_increase_edicts(prog);
125 //============================================================================
128 int PRVM_ED_FindFieldOffset(prvm_prog_t *prog, const char *field)
131 d = PRVM_ED_FindField(prog, field);
137 int PRVM_ED_FindGlobalOffset(prvm_prog_t *prog, const char *global)
140 d = PRVM_ED_FindGlobal(prog, global);
146 func_t PRVM_ED_FindFunctionOffset(prvm_prog_t *prog, const char *function)
149 f = PRVM_ED_FindFunction(prog, function);
152 return (func_t)(f - prog->functions);
160 prvm_prog_t *PRVM_ProgFromString(const char *str)
162 if (!strcmp(str, "server"))
164 if (!strcmp(str, "client"))
167 if (!strcmp(str, "menu"))
175 PRVM_FriendlyProgFromString
178 prvm_prog_t *PRVM_FriendlyProgFromString(const char *str)
180 prvm_prog_t *prog = PRVM_ProgFromString(str);
183 Con_Printf("%s: unknown program name\n", str);
188 Con_Printf("%s: program is not loaded\n", str);
198 Sets everything to NULL.
200 Nota bene: this also marks the entity as allocated if it has been previously
201 freed and sets the allocation origin.
204 void PRVM_ED_ClearEdict(prvm_prog_t *prog, prvm_edict_t *e)
206 memset(e->fields.fp, 0, prog->entityfields * sizeof(prvm_vec_t));
207 e->priv.required->free = false;
208 e->priv.required->freetime = host.realtime;
209 if(e->priv.required->allocation_origin)
210 Mem_Free((char *)e->priv.required->allocation_origin);
211 e->priv.required->allocation_origin = PRVM_AllocationOrigin(prog);
213 // AK: Let the init_edict function determine if something needs to be initialized
214 prog->init_edict(prog, e);
217 const char *PRVM_AllocationOrigin(prvm_prog_t *prog)
220 if(prog->leaktest_active)
221 if(prog->depth > 0) // actually in QC code and not just parsing the entities block of a map/savegame
223 buf = (char *)PRVM_Alloc(256);
224 PRVM_ShortStackTrace(prog, buf, 256);
233 Returns if this particular edict could get allocated by PRVM_ED_Alloc
236 qboolean PRVM_ED_CanAlloc(prvm_prog_t *prog, prvm_edict_t *e)
238 if(!e->priv.required->free)
240 if(prvm_reuseedicts_always_allow == host.realtime)
242 if(host.realtime <= e->priv.required->freetime + 0.1 && prvm_reuseedicts_neverinsameframe.integer)
243 return false; // never allow reuse in same frame (causes networking trouble)
244 if(e->priv.required->freetime < prog->starttime + prvm_reuseedicts_startuptime.value)
246 if(host.realtime > e->priv.required->freetime + 1)
248 return false; // entity slot still blocked because the entity was freed less than one second ago
255 Either finds a free edict, or allocates a new one.
256 Try to avoid reusing an entity that was recently freed, because it
257 can cause the client to think the entity morphed into something else
258 instead of being removed and recreated, which can cause interpolated
259 angles and bad trails.
262 prvm_edict_t *PRVM_ED_Alloc(prvm_prog_t *prog)
267 // the client qc dont need maxclients
268 // thus it doesnt need to use svs.maxclients
269 // AK: changed i=svs.maxclients+1
270 // AK: changed so the edict 0 wont spawn -> used as reserved/world entity
271 // although the menu/client has no world
272 for (i = prog->reserved_edicts + 1;i < prog->num_edicts;i++)
274 e = PRVM_EDICT_NUM(i);
275 if(PRVM_ED_CanAlloc(prog, e))
277 PRVM_ED_ClearEdict (prog, e);
282 if (i == prog->limit_edicts)
283 prog->error_cmd("%s: PRVM_ED_Alloc: no free edicts", prog->name);
286 if (prog->num_edicts >= prog->max_edicts)
287 PRVM_MEM_IncreaseEdicts(prog);
289 e = PRVM_EDICT_NUM(i);
291 PRVM_ED_ClearEdict(prog, e);
299 Marks the edict as free
300 FIXME: walk all entities and NULL out references to this entity
303 void PRVM_ED_Free(prvm_prog_t *prog, prvm_edict_t *ed)
305 // dont delete the null entity (world) or reserved edicts
306 if (ed - prog->edicts <= prog->reserved_edicts)
309 prog->free_edict(prog, ed);
311 ed->priv.required->free = true;
312 ed->priv.required->freetime = host.realtime;
313 if(ed->priv.required->allocation_origin)
315 Mem_Free((char *)ed->priv.required->allocation_origin);
316 ed->priv.required->allocation_origin = NULL;
320 //===========================================================================
327 static ddef_t *PRVM_ED_GlobalAtOfs (prvm_prog_t *prog, int ofs)
332 for (i = 0;i < prog->numglobaldefs;i++)
334 def = &prog->globaldefs[i];
346 ddef_t *PRVM_ED_FieldAtOfs (prvm_prog_t *prog, int ofs)
351 for (i = 0;i < prog->numfielddefs;i++)
353 def = &prog->fielddefs[i];
365 ddef_t *PRVM_ED_FindField (prvm_prog_t *prog, const char *name)
370 for (i = 0;i < prog->numfielddefs;i++)
372 def = &prog->fielddefs[i];
373 if (!strcmp(PRVM_GetString(prog, def->s_name), name))
384 ddef_t *PRVM_ED_FindGlobal (prvm_prog_t *prog, const char *name)
389 for (i = 0;i < prog->numglobaldefs;i++)
391 def = &prog->globaldefs[i];
392 if (!strcmp(PRVM_GetString(prog, def->s_name), name))
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);
1169 if(Cvar_Readonly(cvar, "prvm_edictget"))
1172 Cvar_Get(cmd->cvars, Cmd_Argv(cmd, 4), s, cmd->cvars_flagsmask, NULL);
1175 Con_Printf("%s\n", s);
1181 static void PRVM_ED_GlobalGet_f(cmd_state_t *cmd)
1187 char valuebuf[MAX_INPUTLINE];
1189 if(Cmd_Argc(cmd) != 3 && Cmd_Argc(cmd) != 4)
1191 Con_Print("prvm_globalget <program name> <global> [<cvar>]\n");
1195 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
1198 key = PRVM_ED_FindGlobal(prog, Cmd_Argv(cmd, 2));
1201 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(cmd, 2), Cmd_Argv(cmd, 1) );
1205 v = (prvm_eval_t *) &prog->globals.fp[key->ofs];
1206 s = PRVM_UglyValueString(prog, (etype_t)key->type, v, valuebuf, sizeof(valuebuf));
1207 if(Cmd_Argc(cmd) == 4)
1209 cvar_t *cvar = Cvar_FindVar(cmd->cvars, Cmd_Argv(cmd, 3), cmd->cvars_flagsmask);
1211 if(Cvar_Readonly(cvar, "prvm_globalget"))
1213 Cvar_Get(cmd->cvars, Cmd_Argv(cmd, 3), s, cmd->cvars_flagsmask, NULL);
1216 Con_Printf("%s\n", s);
1226 Console command to set a field of a specified edict
1229 static void PRVM_ED_EdictSet_f(cmd_state_t *cmd)
1235 if(Cmd_Argc(cmd) != 5)
1237 Con_Print("prvm_edictset <program name> <edict number> <field> <value>\n");
1241 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
1244 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(cmd, 2)));
1246 if((key = PRVM_ED_FindField(prog, Cmd_Argv(cmd, 3))) == 0)
1247 Con_Printf("Key %s not found !\n", Cmd_Argv(cmd, 3));
1249 PRVM_ED_ParseEpair(prog, ed, key, Cmd_Argv(cmd, 4), true);
1253 ====================
1256 Parses an edict out of the given string, returning the new position
1257 ed should be a properly initialized empty edict.
1258 Used for initial level load and for savegames.
1259 ====================
1261 const char *PRVM_ED_ParseEdict (prvm_prog_t *prog, const char *data, prvm_edict_t *ent)
1271 // go through all the dictionary pairs
1275 if (!COM_ParseToken_Simple(&data, false, false, true))
1276 prog->error_cmd("PRVM_ED_ParseEdict: EOF without closing brace");
1277 if (developer_entityparsing.integer)
1278 Con_Printf("Key: \"%s\"", com_token);
1279 if (com_token[0] == '}')
1282 // anglehack is to allow QuakeEd to write single scalar angles
1283 // and allow them to be turned into vectors. (FIXME...)
1284 if (!strcmp(com_token, "angle"))
1286 strlcpy (com_token, "angles", sizeof(com_token));
1292 // FIXME: change light to _light to get rid of this hack
1293 if (!strcmp(com_token, "light"))
1294 strlcpy (com_token, "light_lev", sizeof(com_token)); // hack for single light def
1296 strlcpy (keyname, com_token, sizeof(keyname));
1298 // another hack to fix keynames with trailing spaces
1299 n = strlen(keyname);
1300 while (n && keyname[n-1] == ' ')
1307 if (!COM_ParseToken_Simple(&data, false, false, true))
1308 prog->error_cmd("PRVM_ED_ParseEdict: EOF without closing brace");
1309 if (developer_entityparsing.integer)
1310 Con_Printf(" \"%s\"\n", com_token);
1312 if (com_token[0] == '}')
1313 prog->error_cmd("PRVM_ED_ParseEdict: closing brace without data");
1317 // ignore attempts to set key "" (this problem occurs in nehahra neh1m8.bsp)
1321 // keynames with a leading underscore are used for utility comments,
1322 // and are immediately discarded by quake
1323 if (keyname[0] == '_')
1326 key = PRVM_ED_FindField (prog, keyname);
1329 Con_DPrintf("%s: '%s' is not a field\n", prog->name, keyname);
1336 strlcpy (temp, com_token, sizeof(temp));
1337 dpsnprintf (com_token, sizeof(com_token), "0 %s 0", temp);
1340 if (!PRVM_ED_ParseEpair(prog, ent, key, com_token, strcmp(keyname, "wad") != 0))
1341 prog->error_cmd("PRVM_ED_ParseEdict: parse error");
1345 ent->priv.required->free = true;
1346 ent->priv.required->freetime = host.realtime;
1355 PRVM_ED_LoadFromFile
1357 The entities are directly placed in the array, rather than allocated with
1358 PRVM_ED_Alloc, because otherwise an error loading the map would have entity
1359 number references out of order.
1361 Creates a server's entity / program execution context by
1362 parsing textual entity definitions out of an ent file.
1364 Used for both fresh maps and savegame loads. A fresh map would also need
1365 to call PRVM_ED_CallSpawnFunctions () to let the objects initialize themselves.
1368 void PRVM_ED_LoadFromFile (prvm_prog_t *prog, const char *data)
1371 int parsed, inhibited, spawned, died;
1372 const char *funcname;
1381 prvm_reuseedicts_always_allow = host.realtime;
1386 // parse the opening brace
1387 if (!COM_ParseToken_Simple(&data, false, false, true))
1389 if (com_token[0] != '{')
1390 prog->error_cmd("PRVM_ED_LoadFromFile: %s: found %s when expecting {", prog->name, com_token);
1392 // CHANGED: this is not conform to PR_LoadFromFile
1393 if(prog->loadintoworld)
1395 prog->loadintoworld = false;
1396 ent = PRVM_EDICT_NUM(0);
1399 ent = PRVM_ED_Alloc(prog);
1402 if (ent != prog->edicts) // hack
1403 memset (ent->fields.fp, 0, prog->entityfields * sizeof(prvm_vec_t));
1405 data = PRVM_ED_ParseEdict (prog, data, ent);
1408 // remove the entity ?
1409 if(!prog->load_edict(prog, ent))
1411 PRVM_ED_Free(prog, ent);
1416 if (PRVM_serverfunction(SV_OnEntityPreSpawnFunction))
1419 PRVM_serverglobalfloat(time) = sv.time;
1420 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1421 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityPreSpawnFunction), "QC function SV_OnEntityPreSpawnFunction is missing");
1424 if(ent->priv.required->free)
1431 // immediately call spawn function, but only if there is a self global and a classname
1433 if(!ent->priv.required->free)
1435 if (!PRVM_alledictstring(ent, classname))
1437 Con_Print("No classname for:\n");
1438 PRVM_ED_Print(prog, ent, NULL);
1439 PRVM_ED_Free (prog, ent);
1443 // look for the spawn function
1444 funcname = PRVM_GetString(prog, PRVM_alledictstring(ent, classname));
1445 func = PRVM_ED_FindFunction (prog, va(vabuf, sizeof(vabuf), "spawnfunc_%s", funcname));
1447 if(!PRVM_allglobalfloat(require_spawnfunc_prefix))
1448 func = PRVM_ED_FindFunction (prog, funcname);
1452 // check for OnEntityNoSpawnFunction
1453 if (PRVM_serverfunction(SV_OnEntityNoSpawnFunction))
1456 PRVM_serverglobalfloat(time) = sv.time;
1457 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1458 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityNoSpawnFunction), "QC function SV_OnEntityNoSpawnFunction is missing");
1462 if (developer.integer > 0) // don't confuse non-developers with errors
1464 Con_Print("No spawn function for:\n");
1465 PRVM_ED_Print(prog, ent, NULL);
1467 PRVM_ED_Free (prog, ent);
1468 continue; // not included in "inhibited" count
1474 PRVM_serverglobalfloat(time) = sv.time;
1475 PRVM_allglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1476 prog->ExecuteProgram(prog, func - prog->functions, "");
1480 if(!ent->priv.required->free)
1481 if (PRVM_serverfunction(SV_OnEntityPostSpawnFunction))
1484 PRVM_serverglobalfloat(time) = sv.time;
1485 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1486 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityPostSpawnFunction), "QC function SV_OnEntityPostSpawnFunction is missing");
1490 if (ent->priv.required->free)
1494 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);
1496 prvm_reuseedicts_always_allow = 0;
1499 static void PRVM_FindOffsets(prvm_prog_t *prog)
1501 // field and global searches use -1 for NULL
1502 memset(&prog->fieldoffsets, -1, sizeof(prog->fieldoffsets));
1503 memset(&prog->globaloffsets, -1, sizeof(prog->globaloffsets));
1504 // function searches use 0 for NULL
1505 memset(&prog->funcoffsets, 0, sizeof(prog->funcoffsets));
1506 #define PRVM_DECLARE_serverglobalfloat(x)
1507 #define PRVM_DECLARE_serverglobalvector(x)
1508 #define PRVM_DECLARE_serverglobalstring(x)
1509 #define PRVM_DECLARE_serverglobaledict(x)
1510 #define PRVM_DECLARE_serverglobalfunction(x)
1511 #define PRVM_DECLARE_clientglobalfloat(x)
1512 #define PRVM_DECLARE_clientglobalvector(x)
1513 #define PRVM_DECLARE_clientglobalstring(x)
1514 #define PRVM_DECLARE_clientglobaledict(x)
1515 #define PRVM_DECLARE_clientglobalfunction(x)
1516 #define PRVM_DECLARE_menuglobalfloat(x)
1517 #define PRVM_DECLARE_menuglobalvector(x)
1518 #define PRVM_DECLARE_menuglobalstring(x)
1519 #define PRVM_DECLARE_menuglobaledict(x)
1520 #define PRVM_DECLARE_menuglobalfunction(x)
1521 #define PRVM_DECLARE_serverfieldfloat(x)
1522 #define PRVM_DECLARE_serverfieldvector(x)
1523 #define PRVM_DECLARE_serverfieldstring(x)
1524 #define PRVM_DECLARE_serverfieldedict(x)
1525 #define PRVM_DECLARE_serverfieldfunction(x)
1526 #define PRVM_DECLARE_clientfieldfloat(x)
1527 #define PRVM_DECLARE_clientfieldvector(x)
1528 #define PRVM_DECLARE_clientfieldstring(x)
1529 #define PRVM_DECLARE_clientfieldedict(x)
1530 #define PRVM_DECLARE_clientfieldfunction(x)
1531 #define PRVM_DECLARE_menufieldfloat(x)
1532 #define PRVM_DECLARE_menufieldvector(x)
1533 #define PRVM_DECLARE_menufieldstring(x)
1534 #define PRVM_DECLARE_menufieldedict(x)
1535 #define PRVM_DECLARE_menufieldfunction(x)
1536 #define PRVM_DECLARE_serverfunction(x)
1537 #define PRVM_DECLARE_clientfunction(x)
1538 #define PRVM_DECLARE_menufunction(x)
1539 #define PRVM_DECLARE_field(x) prog->fieldoffsets.x = PRVM_ED_FindFieldOffset(prog, #x);
1540 #define PRVM_DECLARE_global(x) prog->globaloffsets.x = PRVM_ED_FindGlobalOffset(prog, #x);
1541 #define PRVM_DECLARE_function(x) prog->funcoffsets.x = PRVM_ED_FindFunctionOffset(prog, #x);
1542 #include "prvm_offsets.h"
1543 #undef PRVM_DECLARE_serverglobalfloat
1544 #undef PRVM_DECLARE_serverglobalvector
1545 #undef PRVM_DECLARE_serverglobalstring
1546 #undef PRVM_DECLARE_serverglobaledict
1547 #undef PRVM_DECLARE_serverglobalfunction
1548 #undef PRVM_DECLARE_clientglobalfloat
1549 #undef PRVM_DECLARE_clientglobalvector
1550 #undef PRVM_DECLARE_clientglobalstring
1551 #undef PRVM_DECLARE_clientglobaledict
1552 #undef PRVM_DECLARE_clientglobalfunction
1553 #undef PRVM_DECLARE_menuglobalfloat
1554 #undef PRVM_DECLARE_menuglobalvector
1555 #undef PRVM_DECLARE_menuglobalstring
1556 #undef PRVM_DECLARE_menuglobaledict
1557 #undef PRVM_DECLARE_menuglobalfunction
1558 #undef PRVM_DECLARE_serverfieldfloat
1559 #undef PRVM_DECLARE_serverfieldvector
1560 #undef PRVM_DECLARE_serverfieldstring
1561 #undef PRVM_DECLARE_serverfieldedict
1562 #undef PRVM_DECLARE_serverfieldfunction
1563 #undef PRVM_DECLARE_clientfieldfloat
1564 #undef PRVM_DECLARE_clientfieldvector
1565 #undef PRVM_DECLARE_clientfieldstring
1566 #undef PRVM_DECLARE_clientfieldedict
1567 #undef PRVM_DECLARE_clientfieldfunction
1568 #undef PRVM_DECLARE_menufieldfloat
1569 #undef PRVM_DECLARE_menufieldvector
1570 #undef PRVM_DECLARE_menufieldstring
1571 #undef PRVM_DECLARE_menufieldedict
1572 #undef PRVM_DECLARE_menufieldfunction
1573 #undef PRVM_DECLARE_serverfunction
1574 #undef PRVM_DECLARE_clientfunction
1575 #undef PRVM_DECLARE_menufunction
1576 #undef PRVM_DECLARE_field
1577 #undef PRVM_DECLARE_global
1578 #undef PRVM_DECLARE_function
1583 typedef struct dpfield_s
1590 #define DPFIELDS (sizeof(dpfields) / sizeof(dpfield_t))
1592 dpfield_t dpfields[] =
1603 #define PO_HASHSIZE 16384
1604 typedef struct po_string_s
1607 struct po_string_s *nextonhashchain;
1612 po_string_t *hashtable[PO_HASHSIZE];
1615 static void PRVM_PO_UnparseString(char *out, const char *in, size_t outsize)
1624 case '\a': if(outsize >= 2) { *out++ = '\\'; *out++ = 'a'; outsize -= 2; } break;
1625 case '\b': if(outsize >= 2) { *out++ = '\\'; *out++ = 'b'; outsize -= 2; } break;
1626 case '\t': if(outsize >= 2) { *out++ = '\\'; *out++ = 't'; outsize -= 2; } break;
1627 case '\r': if(outsize >= 2) { *out++ = '\\'; *out++ = 'r'; outsize -= 2; } break;
1628 case '\n': if(outsize >= 2) { *out++ = '\\'; *out++ = 'n'; outsize -= 2; } break;
1629 case '\\': if(outsize >= 2) { *out++ = '\\'; *out++ = '\\'; outsize -= 2; } break;
1630 case '"': if(outsize >= 2) { *out++ = '\\'; *out++ = '"'; outsize -= 2; } break;
1632 if(*in >= 0 && *in <= 0x1F)
1637 *out++ = '0' + ((*in & 0700) >> 6);
1638 *out++ = '0' + ((*in & 0070) >> 3);
1639 *out++ = '0' + (*in & 0007) ;
1656 static void PRVM_PO_ParseString(char *out, const char *in, size_t outsize)
1669 case 'a': if(outsize > 0) { *out++ = '\a'; --outsize; } break;
1670 case 'b': if(outsize > 0) { *out++ = '\b'; --outsize; } break;
1671 case 't': if(outsize > 0) { *out++ = '\t'; --outsize; } break;
1672 case 'r': if(outsize > 0) { *out++ = '\r'; --outsize; } break;
1673 case 'n': if(outsize > 0) { *out++ = '\n'; --outsize; } break;
1674 case '\\': if(outsize > 0) { *out++ = '\\'; --outsize; } break;
1675 case '"': if(outsize > 0) { *out++ = '"'; --outsize; } break;
1676 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
1680 if(*in >= '0' && *in <= '7')
1683 *out = (*out << 3) | (*in - '0');
1686 if(*in >= '0' && *in <= '7')
1689 *out = (*out << 3) | (*in - '0');
1700 if(outsize > 0) { *out++ = *in; --outsize; }
1715 static po_t *PRVM_PO_Load(const char *filename, const char *filename2, mempool_t *pool)
1720 char inbuf[MAX_INPUTLINE];
1721 char decodedbuf[MAX_INPUTLINE];
1724 po_string_t thisstr;
1727 for (i = 0; i < 2; ++i)
1729 const char *buf = (const char *)
1730 FS_LoadFile((i > 0 ? filename : filename2), pool, true, NULL);
1731 // first read filename2, then read filename
1732 // so that progs.dat.de.po wins over common.de.po
1733 // and within file, last item wins
1740 po = (po_t *)Mem_Alloc(pool, sizeof(*po));
1741 memset(po, 0, sizeof(*po));
1744 memset(&thisstr, 0, sizeof(thisstr)); // hush compiler warning
1752 p = strchr(p, '\n');
1758 if(*p == '\r' || *p == '\n')
1763 if(!strncmp(p, "msgid \"", 7))
1768 else if(!strncmp(p, "msgstr \"", 8))
1775 p = strchr(p, '\n');
1785 q = strchr(p, '\n');
1792 if((size_t)(q - p) >= (size_t) sizeof(inbuf))
1794 strlcpy(inbuf, p, q - p); // not - 1, because this adds a NUL
1795 PRVM_PO_ParseString(decodedbuf + decodedpos, inbuf, sizeof(decodedbuf) - decodedpos);
1796 decodedpos += strlen(decodedbuf + decodedpos);
1806 Mem_Free(thisstr.key);
1807 thisstr.key = (char *)Mem_Alloc(pool, decodedpos + 1);
1808 memcpy(thisstr.key, decodedbuf, decodedpos + 1);
1810 else if(decodedpos > 0 && thisstr.key) // skip empty translation results
1812 thisstr.value = (char *)Mem_Alloc(pool, decodedpos + 1);
1813 memcpy(thisstr.value, decodedbuf, decodedpos + 1);
1814 hashindex = CRC_Block((const unsigned char *) thisstr.key, strlen(thisstr.key)) % PO_HASHSIZE;
1815 thisstr.nextonhashchain = po->hashtable[hashindex];
1816 po->hashtable[hashindex] = (po_string_t *)Mem_Alloc(pool, sizeof(thisstr));
1817 memcpy(po->hashtable[hashindex], &thisstr, sizeof(thisstr));
1818 memset(&thisstr, 0, sizeof(thisstr));
1822 Mem_Free((char *) buf);
1827 static const char *PRVM_PO_Lookup(po_t *po, const char *str)
1829 int hashindex = CRC_Block((const unsigned char *) str, strlen(str)) % PO_HASHSIZE;
1830 po_string_t *p = po->hashtable[hashindex];
1833 if(!strcmp(str, p->key))
1835 p = p->nextonhashchain;
1839 static void PRVM_PO_Destroy(po_t *po)
1842 for(i = 0; i < PO_HASHSIZE; ++i)
1844 po_string_t *p = po->hashtable[i];
1848 p = p->nextonhashchain;
1857 void PRVM_LeakTest(prvm_prog_t *prog);
1858 void PRVM_Prog_Reset(prvm_prog_t *prog)
1862 PRVM_LeakTest(prog);
1863 prog->reset_cmd(prog);
1864 Mem_FreePool(&prog->progs_mempool);
1866 PRVM_PO_Destroy((po_t *) prog->po);
1868 memset(prog,0,sizeof(prvm_prog_t));
1869 prog->break_statement = -1;
1870 prog->watch_global_type = ev_void;
1871 prog->watch_field_type = ev_void;
1879 static void PRVM_LoadLNO( prvm_prog_t *prog, const char *progname ) {
1880 fs_offset_t filesize;
1882 unsigned int *header;
1885 FS_StripExtension( progname, filename, sizeof( filename ) );
1886 strlcat( filename, ".lno", sizeof( filename ) );
1888 lno = FS_LoadFile( filename, tempmempool, false, &filesize );
1894 <Spike> SafeWrite (h, &lnotype, sizeof(int));
1895 <Spike> SafeWrite (h, &version, sizeof(int));
1896 <Spike> SafeWrite (h, &numglobaldefs, sizeof(int));
1897 <Spike> SafeWrite (h, &numpr_globals, sizeof(int));
1898 <Spike> SafeWrite (h, &numfielddefs, sizeof(int));
1899 <Spike> SafeWrite (h, &numstatements, sizeof(int));
1900 <Spike> SafeWrite (h, statement_linenums, numstatements*sizeof(int));
1902 if ((unsigned int)filesize < (6 + prog->progs_numstatements) * sizeof(int))
1908 header = (unsigned int *) lno;
1909 if( header[ 0 ] == *(unsigned int *) "LNOF" &&
1910 LittleLong( header[ 1 ] ) == 1 &&
1911 (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs_numglobaldefs &&
1912 (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs_numglobals &&
1913 (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs_numfielddefs &&
1914 (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs_numstatements )
1916 prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof( int ) );
1917 memcpy( prog->statement_linenums, header + 6, prog->progs_numstatements * sizeof( int ) );
1919 /* gmqcc suports columnums */
1920 if ((unsigned int)filesize > ((6 + 2 * prog->progs_numstatements) * sizeof( int )))
1922 prog->statement_columnnums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof( int ) );
1923 memcpy( prog->statement_columnnums, header + 6 + prog->progs_numstatements, prog->progs_numstatements * sizeof( int ) );
1934 static void PRVM_UpdateBreakpoints(prvm_prog_t *prog);
1935 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)
1938 dprograms_t *dprograms;
1939 dstatement_t *instatements;
1940 ddef_t *infielddefs;
1941 ddef_t *inglobaldefs;
1943 dfunction_t *infunctions;
1945 fs_offset_t filesize;
1946 int requiredglobalspace;
1963 prog->error_cmd("PRVM_LoadProgs: there is already a %s program loaded!", prog->name );
1965 Host_LockSession(); // all progs can use the session cvar
1966 Crypto_LoadKeys(); // all progs might use the keys at init time
1970 dprograms = (dprograms_t *) data;
1974 dprograms = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
1975 if (dprograms == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
1976 prog->error_cmd("PRVM_LoadProgs: couldn't load %s for %s", filename, prog->name);
1977 // TODO bounds check header fields (e.g. numstatements), they must never go behind end of file
1979 prog->profiletime = Sys_DirtyTime();
1980 prog->starttime = host.realtime;
1982 Con_DPrintf("%s programs occupy %iK.\n", prog->name, (int)(filesize/1024));
1984 requiredglobalspace = 0;
1985 for (i = 0;i < numrequiredglobals;i++)
1986 requiredglobalspace += required_global[i].type == ev_vector ? 3 : 1;
1988 prog->filecrc = CRC_Block((unsigned char *)dprograms, filesize);
1990 // byte swap the header
1991 prog->progs_version = LittleLong(dprograms->version);
1992 prog->progs_crc = LittleLong(dprograms->crc);
1993 if (prog->progs_version != PROG_VERSION)
1994 prog->error_cmd("%s: %s has wrong version number (%i should be %i)", prog->name, filename, prog->progs_version, PROG_VERSION);
1995 instatements = (dstatement_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_statements));
1996 prog->progs_numstatements = LittleLong(dprograms->numstatements);
1997 inglobaldefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globaldefs));
1998 prog->progs_numglobaldefs = LittleLong(dprograms->numglobaldefs);
1999 infielddefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_fielddefs));
2000 prog->progs_numfielddefs = LittleLong(dprograms->numfielddefs);
2001 infunctions = (dfunction_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_functions));
2002 prog->progs_numfunctions = LittleLong(dprograms->numfunctions);
2003 instrings = (char *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_strings));
2004 prog->progs_numstrings = LittleLong(dprograms->numstrings);
2005 inglobals = (int *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globals));
2006 prog->progs_numglobals = LittleLong(dprograms->numglobals);
2007 prog->progs_entityfields = LittleLong(dprograms->entityfields);
2009 prog->numstatements = prog->progs_numstatements;
2010 prog->numglobaldefs = prog->progs_numglobaldefs;
2011 prog->numfielddefs = prog->progs_numfielddefs;
2012 prog->numfunctions = prog->progs_numfunctions;
2013 prog->numstrings = prog->progs_numstrings;
2014 prog->numglobals = prog->progs_numglobals;
2015 prog->entityfields = prog->progs_entityfields;
2017 if (LittleLong(dprograms->ofs_strings) + prog->progs_numstrings > (int)filesize)
2018 prog->error_cmd("%s: %s strings go past end of file", prog->name, filename);
2019 prog->strings = (char *)Mem_Alloc(prog->progs_mempool, prog->progs_numstrings);
2020 memcpy(prog->strings, instrings, prog->progs_numstrings);
2021 prog->stringssize = prog->progs_numstrings;
2023 prog->numknownstrings = 0;
2024 prog->maxknownstrings = 0;
2025 prog->knownstrings = NULL;
2026 prog->knownstrings_flags = NULL;
2028 Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
2030 // we need to expand the globaldefs and fielddefs to include engine defs
2031 prog->globaldefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobaldefs + numrequiredglobals) * sizeof(ddef_t));
2032 prog->globals.fp = (prvm_vec_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobals + requiredglobalspace + 2) * sizeof(prvm_vec_t));
2033 // + 2 is because of an otherwise occurring overrun in RETURN instruction
2034 // when trying to return the last or second-last global
2035 // (RETURN always returns a vector, there is no RETURN_F instruction)
2036 prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numfielddefs + numrequiredfields) * sizeof(ddef_t));
2037 // we need to convert the statements to our memory format
2038 prog->statements = (mstatement_t *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(mstatement_t));
2039 // allocate space for profiling statement usage
2040 prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(*prog->statement_profile));
2041 prog->explicit_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(*prog->statement_profile));
2042 // functions need to be converted to the memory format
2043 prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs_numfunctions);
2045 for (i = 0;i < prog->progs_numfunctions;i++)
2047 prog->functions[i].first_statement = LittleLong(infunctions[i].first_statement);
2048 prog->functions[i].parm_start = LittleLong(infunctions[i].parm_start);
2049 prog->functions[i].s_name = LittleLong(infunctions[i].s_name);
2050 prog->functions[i].s_file = LittleLong(infunctions[i].s_file);
2051 prog->functions[i].numparms = LittleLong(infunctions[i].numparms);
2052 prog->functions[i].locals = LittleLong(infunctions[i].locals);
2053 memcpy(prog->functions[i].parm_size, infunctions[i].parm_size, sizeof(infunctions[i].parm_size));
2054 if(prog->functions[i].first_statement >= prog->numstatements)
2055 prog->error_cmd("PRVM_LoadProgs: out of bounds function statement (function %d) in %s", i, prog->name);
2056 // TODO bounds check parm_start, s_name, s_file, numparms, locals, parm_size
2059 // copy the globaldefs to the new globaldefs list
2060 for (i=0 ; i<prog->numglobaldefs ; i++)
2062 prog->globaldefs[i].type = LittleShort(inglobaldefs[i].type);
2063 prog->globaldefs[i].ofs = LittleShort(inglobaldefs[i].ofs);
2064 prog->globaldefs[i].s_name = LittleLong(inglobaldefs[i].s_name);
2065 // TODO bounds check ofs, s_name
2068 // append the required globals
2069 for (i = 0;i < numrequiredglobals;i++)
2071 prog->globaldefs[prog->numglobaldefs].type = required_global[i].type;
2072 prog->globaldefs[prog->numglobaldefs].ofs = prog->numglobals;
2073 prog->globaldefs[prog->numglobaldefs].s_name = PRVM_SetEngineString(prog, required_global[i].name);
2074 if (prog->globaldefs[prog->numglobaldefs].type == ev_vector)
2075 prog->numglobals += 3;
2078 prog->numglobaldefs++;
2081 // copy the progs fields to the new fields list
2082 for (i = 0;i < prog->numfielddefs;i++)
2084 prog->fielddefs[i].type = LittleShort(infielddefs[i].type);
2085 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
2086 prog->error_cmd("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", prog->name);
2087 prog->fielddefs[i].ofs = LittleShort(infielddefs[i].ofs);
2088 prog->fielddefs[i].s_name = LittleLong(infielddefs[i].s_name);
2089 // TODO bounds check ofs, s_name
2092 // append the required fields
2093 for (i = 0;i < numrequiredfields;i++)
2095 prog->fielddefs[prog->numfielddefs].type = required_field[i].type;
2096 prog->fielddefs[prog->numfielddefs].ofs = prog->entityfields;
2097 prog->fielddefs[prog->numfielddefs].s_name = PRVM_SetEngineString(prog, required_field[i].name);
2098 if (prog->fielddefs[prog->numfielddefs].type == ev_vector)
2099 prog->entityfields += 3;
2101 prog->entityfields++;
2102 prog->numfielddefs++;
2105 // LadyHavoc: TODO: reorder globals to match engine struct
2106 // LadyHavoc: TODO: reorder fields to match engine struct
2107 #define remapglobal(index) (index)
2108 #define remapfield(index) (index)
2111 // FIXME: LadyHavoc: this uses a crude way to identify integer constants, rather than checking for matching globaldefs and checking their type
2112 for (i = 0;i < prog->progs_numglobals;i++)
2114 u.i = LittleLong(inglobals[i]);
2115 // most globals are 0, we only need to deal with the ones that are not
2118 d = u.i & 0xFF800000;
2119 if ((d == 0xFF800000) || (d == 0))
2121 // Looks like an integer (expand to int64)
2122 prog->globals.ip[remapglobal(i)] = u.i;
2126 // Looks like a float (expand to double)
2127 prog->globals.fp[remapglobal(i)] = u.f;
2132 // LadyHavoc: TODO: support 32bit progs statement formats
2133 // copy, remap globals in statements, bounds check
2134 for (i = 0;i < prog->progs_numstatements;i++)
2136 op = (opcode_t)LittleShort(instatements[i].op);
2137 a = (unsigned short)LittleShort(instatements[i].a);
2138 b = (unsigned short)LittleShort(instatements[i].b);
2139 c = (unsigned short)LittleShort(instatements[i].c);
2145 if (a >= prog->progs_numglobals || b + i < 0 || b + i >= prog->progs_numstatements)
2146 prog->error_cmd("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", i, prog->name);
2147 prog->statements[i].op = op;
2148 prog->statements[i].operand[0] = remapglobal(a);
2149 prog->statements[i].operand[1] = -1;
2150 prog->statements[i].operand[2] = -1;
2151 prog->statements[i].jumpabsolute = i + b;
2155 if (a + i < 0 || a + i >= prog->progs_numstatements)
2156 prog->error_cmd("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, prog->name);
2157 prog->statements[i].op = op;
2158 prog->statements[i].operand[0] = -1;
2159 prog->statements[i].operand[1] = -1;
2160 prog->statements[i].operand[2] = -1;
2161 prog->statements[i].jumpabsolute = i + a;
2164 Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", (int)op, i, prog->name);
2166 // global global global
2201 if (a >= prog->progs_numglobals || b >= prog->progs_numglobals || c >= prog->progs_numglobals)
2202 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
2203 prog->statements[i].op = op;
2204 prog->statements[i].operand[0] = remapglobal(a);
2205 prog->statements[i].operand[1] = remapglobal(b);
2206 prog->statements[i].operand[2] = remapglobal(c);
2207 prog->statements[i].jumpabsolute = -1;
2209 // global none global
2215 if (a >= prog->progs_numglobals || c >= prog->progs_numglobals)
2216 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2217 prog->statements[i].op = op;
2218 prog->statements[i].operand[0] = remapglobal(a);
2219 prog->statements[i].operand[1] = -1;
2220 prog->statements[i].operand[2] = remapglobal(c);
2221 prog->statements[i].jumpabsolute = -1;
2237 if (a >= prog->progs_numglobals || b >= prog->progs_numglobals)
2238 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2239 prog->statements[i].op = op;
2240 prog->statements[i].operand[0] = remapglobal(a);
2241 prog->statements[i].operand[1] = remapglobal(b);
2242 prog->statements[i].operand[2] = -1;
2243 prog->statements[i].jumpabsolute = -1;
2247 if ( a < prog->progs_numglobals)
2248 if ( prog->globals.ip[remapglobal(a)] >= 0 )
2249 if ( prog->globals.ip[remapglobal(a)] < prog->progs_numfunctions )
2250 if ( prog->functions[prog->globals.ip[remapglobal(a)]].first_statement == -642 )
2251 ++prog->numexplicitcoveragestatements;
2262 if ( a >= prog->progs_numglobals)
2263 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2264 prog->statements[i].op = op;
2265 prog->statements[i].operand[0] = remapglobal(a);
2266 prog->statements[i].operand[1] = -1;
2267 prog->statements[i].operand[2] = -1;
2268 prog->statements[i].jumpabsolute = -1;
2272 if(prog->numstatements < 1)
2274 prog->error_cmd("PRVM_LoadProgs: empty program in %s", prog->name);
2276 else switch(prog->statements[prog->numstatements - 1].op)
2283 prog->error_cmd("PRVM_LoadProgs: program may fall off the edge (does not end with RETURN, GOTO or DONE) in %s", prog->name);
2287 // we're done with the file now
2289 Mem_Free(dprograms);
2292 // check required functions
2293 for(i=0 ; i < numrequiredfunc ; i++)
2294 if(PRVM_ED_FindFunction(prog, required_func[i]) == 0)
2295 prog->error_cmd("%s: %s not found in %s",prog->name, required_func[i], filename);
2297 PRVM_LoadLNO(prog, filename);
2299 PRVM_Init_Exec(prog);
2301 if(*prvm_language.string)
2302 // in CSQC we really shouldn't be able to change how stuff works... sorry for now
2303 // later idea: include a list of authorized .po file checksums with the csprogs
2305 qboolean deftrans = prog == CLVM_prog;
2306 const char *realfilename = (prog != CLVM_prog ? filename : csqc_progname.string);
2307 if(deftrans) // once we have dotranslate_ strings, ALWAYS use the opt-in method!
2309 for (i=0 ; i<prog->numglobaldefs ; i++)
2312 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2313 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2314 if(name && !strncmp(name, "dotranslate_", 12))
2321 if(!strcmp(prvm_language.string, "dump"))
2323 qfile_t *f = FS_OpenRealFile(va(vabuf, sizeof(vabuf), "%s.pot", realfilename), "w", false);
2324 Con_Printf("Dumping to %s.pot\n", realfilename);
2327 for (i=0 ; i<prog->numglobaldefs ; i++)
2330 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2331 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2332 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2334 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2335 const char *value = PRVM_GetString(prog, val->string);
2338 char buf[MAX_INPUTLINE];
2339 PRVM_PO_UnparseString(buf, value, sizeof(buf));
2340 FS_Printf(f, "msgid \"%s\"\nmsgstr \"\"\n\n", buf);
2349 po_t *po = PRVM_PO_Load(
2350 va(vabuf, sizeof(vabuf), "%s.%s.po", realfilename, prvm_language.string),
2351 va(vabuf2, sizeof(vabuf2), "common.%s.po", prvm_language.string),
2352 prog->progs_mempool);
2355 for (i=0 ; i<prog->numglobaldefs ; i++)
2358 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2359 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2360 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2362 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2363 const char *value = PRVM_GetString(prog, val->string);
2366 value = PRVM_PO_Lookup(po, value);
2368 val->string = PRVM_SetEngineString(prog, value);
2376 for (cvar = prog->console_cmd->cvars->vars; cvar; cvar = cvar->next)
2377 cvar->globaldefindex[prog - prvm_prog_list] = -1;
2379 for (i=0 ; i<prog->numglobaldefs ; i++)
2382 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2383 //Con_Printf("found var %s\n", name);
2385 && !strncmp(name, "autocvar_", 9)
2386 && !(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
2389 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2390 cvar = Cvar_FindVar(prog->console_cmd->cvars, name + 9, prog->console_cmd->cvars_flagsmask);
2391 //Con_Printf("PRVM_LoadProgs: autocvar global %s in %s, processing...\n", name, prog->name);
2396 Con_DPrintf("PRVM_LoadProgs: no cvar for autocvar global %s in %s, creating...\n", name, prog->name);
2397 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2400 if((float)((int)(val->_float)) == val->_float)
2401 dpsnprintf(buf, sizeof(buf), "%i", (int)(val->_float));
2403 dpsnprintf(buf, sizeof(buf), "%.9g", val->_float);
2407 dpsnprintf(buf, sizeof(buf), "%.9g %.9g %.9g", val->vector[0], val->vector[1], val->vector[2]); value = buf;
2410 value = PRVM_GetString(prog, val->string);
2413 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2416 cvar = Cvar_Get(prog->console_cmd->cvars, name + 9, value, prog->console_cmd->cvars_flagsmask, NULL);
2417 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2419 val->string = PRVM_SetEngineString(prog, cvar->string);
2420 cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2423 prog->error_cmd("PRVM_LoadProgs: could not create cvar for autocvar global %s in %s", name, prog->name);
2424 cvar->globaldefindex[prog - prvm_prog_list] = i;
2426 else if((cvar->flags & CVAR_PRIVATE) == 0)
2428 // MUST BE SYNCED WITH cvar.c Cvar_Set
2431 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2434 val->_float = cvar->value;
2438 VectorClear(val->vector);
2439 for (j = 0;j < 3;j++)
2441 while (*s && ISWHITESPACE(*s))
2445 val->vector[j] = atof(s);
2446 while (!ISWHITESPACE(*s))
2453 val->string = PRVM_SetEngineString(prog, cvar->string);
2454 cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2457 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2460 cvar->globaldefindex[prog - prvm_prog_list] = i;
2463 Con_Printf("PRVM_LoadProgs: private cvar for autocvar global %s in %s\n", name, prog->name);
2469 prog->loaded = true;
2471 PRVM_UpdateBreakpoints(prog);
2473 // set flags & ddef_ts in prog
2477 PRVM_FindOffsets(prog);
2479 prog->init_cmd(prog);
2482 PRVM_MEM_Alloc(prog);
2484 // Inittime is at least the time when this function finished. However,
2485 // later events may bump it.
2486 prog->inittime = host.realtime;
2490 static void PRVM_Fields_f(cmd_state_t *cmd)
2493 int i, j, ednum, used, usedamount;
2495 char tempstring[MAX_INPUTLINE], tempstring2[260];
2505 Con_Print("no progs loaded\n");
2510 if(Cmd_Argc(cmd) != 2)
2512 Con_Print("prvm_fields <program name>\n");
2516 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2519 counts = (int *)Mem_Alloc(tempmempool, prog->numfielddefs * sizeof(int));
2520 for (ednum = 0;ednum < prog->max_edicts;ednum++)
2522 ed = PRVM_EDICT_NUM(ednum);
2523 if (ed->priv.required->free)
2525 for (i = 1;i < prog->numfielddefs;i++)
2527 d = &prog->fielddefs[i];
2528 name = PRVM_GetString(prog, d->s_name);
2529 if (name[strlen(name)-2] == '_')
2530 continue; // skip _x, _y, _z vars
2531 val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
2532 // if the value is still all 0, skip the field
2533 for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
2535 if (val->ivector[j])
2546 for (i = 0;i < prog->numfielddefs;i++)
2548 d = &prog->fielddefs[i];
2549 name = PRVM_GetString(prog, d->s_name);
2550 if (name[strlen(name)-2] == '_')
2551 continue; // skip _x, _y, _z vars
2552 switch(d->type & ~DEF_SAVEGLOBAL)
2555 strlcat(tempstring, "string ", sizeof(tempstring));
2558 strlcat(tempstring, "entity ", sizeof(tempstring));
2561 strlcat(tempstring, "function ", sizeof(tempstring));
2564 strlcat(tempstring, "field ", sizeof(tempstring));
2567 strlcat(tempstring, "void ", sizeof(tempstring));
2570 strlcat(tempstring, "float ", sizeof(tempstring));
2573 strlcat(tempstring, "vector ", sizeof(tempstring));
2576 strlcat(tempstring, "pointer ", sizeof(tempstring));
2579 dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
2580 strlcat(tempstring, tempstring2, sizeof(tempstring));
2583 if (strlen(name) > sizeof(tempstring2)-4)
2585 memcpy (tempstring2, name, sizeof(tempstring2)-4);
2586 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
2587 tempstring2[sizeof(tempstring2)-1] = 0;
2590 strlcat(tempstring, name, sizeof(tempstring));
2591 for (j = (int)strlen(name);j < 25;j++)
2592 strlcat(tempstring, " ", sizeof(tempstring));
2593 dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
2594 strlcat(tempstring, tempstring2, sizeof(tempstring));
2595 strlcat(tempstring, "\n", sizeof(tempstring));
2596 if (strlen(tempstring) >= sizeof(tempstring)/2)
2598 Con_Print(tempstring);
2604 usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
2608 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);
2611 static void PRVM_Globals_f(cmd_state_t *cmd)
2615 const char *wildcard;
2621 Con_Print("no progs loaded\n");
2624 if(Cmd_Argc (cmd) < 2 || Cmd_Argc(cmd) > 3)
2626 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
2630 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2633 if( Cmd_Argc(cmd) == 3)
2634 wildcard = Cmd_Argv(cmd, 2);
2638 Con_Printf("%s :", prog->name);
2640 for (i = 0;i < prog->numglobaldefs;i++)
2643 if( !matchpattern( PRVM_GetString(prog, prog->globaldefs[i].s_name), wildcard, 1) )
2648 Con_Printf("%s\n", PRVM_GetString(prog, prog->globaldefs[i].s_name));
2650 Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->numglobals, numculled, prog->numglobals * 4);
2658 static void PRVM_Global_f(cmd_state_t *cmd)
2662 char valuebuf[MAX_INPUTLINE];
2663 if( Cmd_Argc(cmd) != 3 ) {
2664 Con_Printf( "prvm_global <program name> <global name>\n" );
2668 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2671 global = PRVM_ED_FindGlobal( prog, Cmd_Argv(cmd, 2) );
2673 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(cmd, 2), Cmd_Argv(cmd, 1) );
2675 Con_Printf( "%s: %s\n", Cmd_Argv(cmd, 2), PRVM_ValueString( prog, (etype_t)global->type, PRVM_GLOBALFIELDVALUE(global->ofs), valuebuf, sizeof(valuebuf) ) );
2683 static void PRVM_GlobalSet_f(cmd_state_t *cmd)
2687 if( Cmd_Argc(cmd) != 4 ) {
2688 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2692 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2695 global = PRVM_ED_FindGlobal( prog, Cmd_Argv(cmd, 2) );
2697 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(cmd, 2), Cmd_Argv(cmd, 1) );
2699 PRVM_ED_ParseEpair( prog, NULL, global, Cmd_Argv(cmd, 3), true );
2703 ======================
2704 Break- and Watchpoints
2705 ======================
2709 char break_statement[256];
2710 char watch_global[256];
2712 char watch_field[256];
2715 static debug_data_t debug_data[PRVM_PROG_MAX];
2717 void PRVM_Breakpoint(prvm_prog_t *prog, int stack_index, const char *text)
2720 Con_Printf("PRVM_Breakpoint: %s\n", text);
2721 PRVM_PrintState(prog, stack_index);
2722 if (prvm_breakpointdump.integer)
2723 Host_Savegame_to(prog, va(vabuf, sizeof(vabuf), "breakpoint-%s.dmp", prog->name));
2726 void PRVM_Watchpoint(prvm_prog_t *prog, int stack_index, const char *text, etype_t type, prvm_eval_t *o, prvm_eval_t *n)
2728 size_t sz = sizeof(prvm_vec_t) * ((type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2729 if (memcmp(o, n, sz))
2732 char valuebuf_o[128];
2733 char valuebuf_n[128];
2734 PRVM_UglyValueString(prog, type, o, valuebuf_o, sizeof(valuebuf_o));
2735 PRVM_UglyValueString(prog, type, n, valuebuf_n, sizeof(valuebuf_n));
2736 dpsnprintf(buf, sizeof(buf), "%s: %s -> %s", text, valuebuf_o, valuebuf_n);
2737 PRVM_Breakpoint(prog, stack_index, buf);
2742 static void PRVM_UpdateBreakpoints(prvm_prog_t *prog)
2744 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2747 if (debug->break_statement[0])
2749 if (debug->break_statement[0] >= '0' && debug->break_statement[0] <= '9')
2751 prog->break_statement = atoi(debug->break_statement);
2752 prog->break_stack_index = 0;
2757 func = PRVM_ED_FindFunction (prog, debug->break_statement);
2760 Con_Printf("%s progs: no function or statement named %s to break on!\n", prog->name, debug->break_statement);
2761 prog->break_statement = -1;
2765 prog->break_statement = func->first_statement;
2766 prog->break_stack_index = 1;
2769 if (prog->break_statement >= -1)
2770 Con_Printf("%s progs: breakpoint is at statement %d\n", prog->name, prog->break_statement);
2773 prog->break_statement = -1;
2775 if (debug->watch_global[0])
2777 ddef_t *global = PRVM_ED_FindGlobal( prog, debug->watch_global );
2780 Con_Printf( "%s progs: no global named '%s' to watch!\n", prog->name, debug->watch_global );
2781 prog->watch_global_type = ev_void;
2785 size_t sz = sizeof(prvm_vec_t) * ((global->type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2786 prog->watch_global = global->ofs;
2787 prog->watch_global_type = (etype_t)global->type;
2788 memcpy(&prog->watch_global_value, PRVM_GLOBALFIELDVALUE(prog->watch_global), sz);
2790 if (prog->watch_global_type != ev_void)
2791 Con_Printf("%s progs: global watchpoint is at global index %d\n", prog->name, prog->watch_global);
2794 prog->watch_global_type = ev_void;
2796 if (debug->watch_field[0])
2798 ddef_t *field = PRVM_ED_FindField( prog, debug->watch_field );
2801 Con_Printf( "%s progs: no field named '%s' to watch!\n", prog->name, debug->watch_field );
2802 prog->watch_field_type = ev_void;
2806 size_t sz = sizeof(prvm_vec_t) * ((field->type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2807 prog->watch_edict = debug->watch_edict;
2808 prog->watch_field = field->ofs;
2809 prog->watch_field_type = (etype_t)field->type;
2810 if (prog->watch_edict < prog->num_edicts)
2811 memcpy(&prog->watch_edictfield_value, PRVM_EDICTFIELDVALUE(PRVM_EDICT_NUM(prog->watch_edict), prog->watch_field), sz);
2813 memset(&prog->watch_edictfield_value, 0, sz);
2815 if (prog->watch_edict != ev_void)
2816 Con_Printf("%s progs: edict field watchpoint is at edict %d field index %d\n", prog->name, prog->watch_edict, prog->watch_field);
2819 prog->watch_field_type = ev_void;
2822 static void PRVM_Breakpoint_f(cmd_state_t *cmd)
2826 if( Cmd_Argc(cmd) == 2 ) {
2827 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2830 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2831 debug->break_statement[0] = 0;
2833 PRVM_UpdateBreakpoints(prog);
2836 if( Cmd_Argc(cmd) != 3 ) {
2837 Con_Printf( "prvm_breakpoint <program name> <function name | statement>\n" );
2841 if (!(prog = PRVM_ProgFromString(Cmd_Argv(cmd, 1))))
2845 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2846 strlcpy(debug->break_statement, Cmd_Argv(cmd, 2), sizeof(debug->break_statement));
2848 PRVM_UpdateBreakpoints(prog);
2851 static void PRVM_GlobalWatchpoint_f(cmd_state_t *cmd)
2855 if( Cmd_Argc(cmd) == 2 ) {
2856 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2859 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2860 debug->watch_global[0] = 0;
2862 PRVM_UpdateBreakpoints(prog);
2865 if( Cmd_Argc(cmd) != 3 ) {
2866 Con_Printf( "prvm_globalwatchpoint <program name> <global name>\n" );
2870 if (!(prog = PRVM_ProgFromString(Cmd_Argv(cmd, 1))))
2874 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2875 strlcpy(debug->watch_global, Cmd_Argv(cmd, 2), sizeof(debug->watch_global));
2877 PRVM_UpdateBreakpoints(prog);
2880 static void PRVM_EdictWatchpoint_f(cmd_state_t *cmd)
2884 if( Cmd_Argc(cmd) == 2 ) {
2885 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2888 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2889 debug->watch_field[0] = 0;
2891 PRVM_UpdateBreakpoints(prog);
2894 if( Cmd_Argc(cmd) != 4 ) {
2895 Con_Printf( "prvm_edictwatchpoint <program name> <edict number> <field name>\n" );
2899 if (!(prog = PRVM_ProgFromString(Cmd_Argv(cmd, 1))))
2903 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2904 debug->watch_edict = atoi(Cmd_Argv(cmd, 2));
2905 strlcpy(debug->watch_field, Cmd_Argv(cmd, 3), sizeof(debug->watch_field));
2907 PRVM_UpdateBreakpoints(prog);
2915 void PRVM_Init (void)
2917 Cmd_AddCommand(CMD_SHARED, "prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2918 Cmd_AddCommand(CMD_SHARED, "prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2919 Cmd_AddCommand(CMD_SHARED, "prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2920 Cmd_AddCommand(CMD_SHARED, "prvm_profile", PRVM_Profile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu)");
2921 Cmd_AddCommand(CMD_SHARED, "prvm_childprofile", PRVM_ChildProfile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu), sorted by time taken in function with child calls");
2922 Cmd_AddCommand(CMD_SHARED, "prvm_callprofile", PRVM_CallProfile_f, "prints execution statistics about the most time consuming QuakeC calls from the engine in the selected VM (server, client, menu)");
2923 Cmd_AddCommand(CMD_SHARED, "prvm_fields", PRVM_Fields_f, "prints usage statistics on properties (how many entities have non-zero values) in the selected VM (server, client, menu)");
2924 Cmd_AddCommand(CMD_SHARED, "prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2925 Cmd_AddCommand(CMD_SHARED, "prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2926 Cmd_AddCommand(CMD_SHARED, "prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2927 Cmd_AddCommand(CMD_SHARED, "prvm_edictset", PRVM_ED_EdictSet_f, "changes value of a specified property of a specified entity in the selected VM (server, client, menu)");
2928 Cmd_AddCommand(CMD_SHARED, "prvm_edictget", PRVM_ED_EdictGet_f, "retrieves the value of a specified property of a specified entity in the selected VM (server, client menu) into a cvar or to the console");
2929 Cmd_AddCommand(CMD_SHARED, "prvm_globalget", PRVM_ED_GlobalGet_f, "retrieves the value of a specified global variable in the selected VM (server, client menu) into a cvar or to the console");
2930 Cmd_AddCommand(CMD_SHARED, "prvm_printfunction", PRVM_PrintFunction_f, "prints a disassembly (QuakeC instructions) of the specified function in the selected VM (server, client, menu)");
2931 Cmd_AddCommand(CMD_SHARED, "cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2932 Cmd_AddCommand(CMD_SHARED, "menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2933 Cmd_AddCommand(CMD_SHARED, "sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2934 Cmd_AddCommand(CMD_SHARED, "prvm_breakpoint", PRVM_Breakpoint_f, "marks a statement or function as breakpoint (when this is executed, a stack trace is printed); to actually halt and investigate state, combine this with a gdb breakpoint on PRVM_Breakpoint, or with prvm_breakpointdump; run with just progs name to clear breakpoint");
2935 Cmd_AddCommand(CMD_SHARED, "prvm_globalwatchpoint", PRVM_GlobalWatchpoint_f, "marks a global as watchpoint (when this is executed, a stack trace is printed); to actually halt and investigate state, combine this with a gdb breakpoint on PRVM_Breakpoint, or with prvm_breakpointdump; run with just progs name to clear watchpoint");
2936 Cmd_AddCommand(CMD_SHARED, "prvm_edictwatchpoint", PRVM_EdictWatchpoint_f, "marks an entity field as watchpoint (when this is executed, a stack trace is printed); to actually halt and investigate state, combine this with a gdb breakpoint on PRVM_Breakpoint, or with prvm_breakpointdump; run with just progs name to clear watchpoint");
2938 Cvar_RegisterVariable (&prvm_language);
2939 Cvar_RegisterVariable (&prvm_traceqc);
2940 Cvar_RegisterVariable (&prvm_statementprofiling);
2941 Cvar_RegisterVariable (&prvm_timeprofiling);
2942 Cvar_RegisterVariable (&prvm_coverage);
2943 Cvar_RegisterVariable (&prvm_backtraceforwarnings);
2944 Cvar_RegisterVariable (&prvm_leaktest);
2945 Cvar_RegisterVariable (&prvm_leaktest_follow_targetname);
2946 Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames);
2947 Cvar_RegisterVariable (&prvm_errordump);
2948 Cvar_RegisterVariable (&prvm_breakpointdump);
2949 Cvar_RegisterVariable (&prvm_reuseedicts_startuptime);
2950 Cvar_RegisterVariable (&prvm_reuseedicts_neverinsameframe);
2951 Cvar_RegisterVariable (&prvm_garbagecollection_enable);
2952 Cvar_RegisterVariable (&prvm_garbagecollection_notify);
2953 Cvar_RegisterVariable (&prvm_garbagecollection_scan_limit);
2954 Cvar_RegisterVariable (&prvm_garbagecollection_strings);
2955 Cvar_RegisterVariable (&prvm_stringdebug);
2957 // COMMANDLINEOPTION: PRVM: -norunaway disables the runaway loop check (it might be impossible to exit DarkPlaces if used!)
2958 prvm_runawaycheck = !COM_CheckParm("-norunaway");
2968 void PRVM_Prog_Init(prvm_prog_t *prog, cmd_state_t *cmd)
2970 PRVM_Prog_Reset(prog);
2971 prog->leaktest_active = prvm_leaktest.integer != 0;
2972 prog->console_cmd = cmd;
2975 // LadyHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
2976 unsigned int PRVM_EDICT_NUM_ERROR(prvm_prog_t *prog, unsigned int n, const char *filename, int fileline)
2978 prog->error_cmd("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", prog->name, n, filename, fileline);
2982 #define PRVM_KNOWNSTRINGBASE 0x40000000
2984 const char *PRVM_GetString(prvm_prog_t *prog, int num)
2989 if (prvm_stringdebug.integer)
2990 VM_Warning(prog, "PRVM_GetString: Invalid string offset (%i < 0)\n", num);
2993 else if (num < prog->stringssize)
2995 // constant string from progs.dat
2996 return prog->strings + num;
2998 else if (num <= prog->stringssize + prog->tempstringsbuf.maxsize)
3000 // tempstring returned by engine to QC (becomes invalid after returning to engine)
3001 num -= prog->stringssize;
3002 if (num < prog->tempstringsbuf.cursize)
3003 return (char *)prog->tempstringsbuf.data + num;
3006 if (prvm_stringdebug.integer)
3007 VM_Warning(prog, "PRVM_GetString: Invalid temp-string offset (%i >= %i prog->tempstringsbuf.cursize)\n", num, prog->tempstringsbuf.cursize);
3011 else if (num & PRVM_KNOWNSTRINGBASE)
3014 num = num - PRVM_KNOWNSTRINGBASE;
3015 if (num >= 0 && num < prog->numknownstrings)
3017 if (!prog->knownstrings[num])
3019 if (prvm_stringdebug.integer)
3020 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
3023 // refresh the garbage collection on the string - this guards
3024 // against a certain sort of repeated migration to earlier
3025 // points in the scan that could otherwise result in the string
3026 // being freed for being unused
3027 prog->knownstrings_flags[num] = (prog->knownstrings_flags[num] & ~KNOWNSTRINGFLAG_GCPRUNE) | KNOWNSTRINGFLAG_GCMARK;
3028 return prog->knownstrings[num];
3032 if (prvm_stringdebug.integer)
3033 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
3039 // invalid string offset
3040 if (prvm_stringdebug.integer)
3041 VM_Warning(prog, "PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
3046 const char *PRVM_ChangeEngineString(prvm_prog_t *prog, int i, const char *s)
3049 i = i - PRVM_KNOWNSTRINGBASE;
3050 if (i < 0 || i >= prog->numknownstrings)
3051 prog->error_cmd("PRVM_ChangeEngineString: string index %i is out of bounds", i);
3052 else if ((prog->knownstrings_flags[i] & KNOWNSTRINGFLAG_ENGINE) == 0)
3053 prog->error_cmd("PRVM_ChangeEngineString: string index %i is not an engine string", i);
3054 old = prog->knownstrings[i];
3055 prog->knownstrings[i] = s;
3059 static void PRVM_NewKnownString(prvm_prog_t *prog, int i, int flags, const char *s)
3061 if (i >= prog->numknownstrings)
3063 if (i >= prog->maxknownstrings)
3065 const char **oldstrings = prog->knownstrings;
3066 const unsigned char *oldstrings_flags = prog->knownstrings_flags;
3067 const char **oldstrings_origin = prog->knownstrings_origin;
3068 prog->maxknownstrings += 128;
3069 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3070 prog->knownstrings_flags = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
3071 if (prog->leaktest_active)
3072 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3073 if (prog->numknownstrings)
3075 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
3076 memcpy((char **)prog->knownstrings_flags, oldstrings_flags, prog->numknownstrings * sizeof(unsigned char));
3077 if (prog->leaktest_active)
3078 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
3081 prog->numknownstrings++;
3083 prog->firstfreeknownstring = i + 1;
3084 prog->knownstrings[i] = s;
3085 // it's in use right now, spare it until the next gc pass - that said, it is not freeable so this is probably moot
3086 prog->knownstrings_flags[i] = flags;
3087 if (prog->leaktest_active)
3088 prog->knownstrings_origin[i] = NULL;
3091 int PRVM_SetEngineString(prvm_prog_t *prog, const char *s)
3096 if (s >= prog->strings && s <= prog->strings + prog->stringssize)
3097 prog->error_cmd("PRVM_SetEngineString: s in prog->strings area");
3098 // if it's in the tempstrings area, use a reserved range
3099 // (otherwise we'd get millions of useless string offsets cluttering the database)
3100 if (s >= (char *)prog->tempstringsbuf.data && s < (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.maxsize)
3101 return prog->stringssize + (s - (char *)prog->tempstringsbuf.data);
3102 // see if it's a known string address
3103 for (i = 0;i < prog->numknownstrings;i++)
3104 if (prog->knownstrings[i] == s)
3105 return PRVM_KNOWNSTRINGBASE + i;
3106 // new unknown engine string
3107 if (developer_insane.integer)
3108 Con_DPrintf("new engine string %p = \"%s\"\n", s, s);
3109 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3110 if (!prog->knownstrings[i])
3112 PRVM_NewKnownString(prog, i, KNOWNSTRINGFLAG_GCMARK | KNOWNSTRINGFLAG_ENGINE, s);
3113 return PRVM_KNOWNSTRINGBASE + i;
3116 // temp string handling
3118 // all tempstrings go into this buffer consecutively, and it is reset
3119 // whenever PRVM_ExecuteProgram returns to the engine
3120 // (technically each PRVM_ExecuteProgram call saves the cursize value and
3121 // restores it on return, so multiple recursive calls can share the same
3123 // the buffer size is automatically grown as needed
3125 int PRVM_SetTempString(prvm_prog_t *prog, const char *s)
3131 size = (int)strlen(s) + 1;
3132 if (developer_insane.integer)
3133 Con_DPrintf("PRVM_SetTempString: cursize %i, size %i\n", prog->tempstringsbuf.cursize, size);
3134 if (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
3136 sizebuf_t old = prog->tempstringsbuf;
3137 if (prog->tempstringsbuf.cursize + size >= 1<<28)
3138 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);
3139 prog->tempstringsbuf.maxsize = max(prog->tempstringsbuf.maxsize, 65536);
3140 while (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
3141 prog->tempstringsbuf.maxsize *= 2;
3142 if (prog->tempstringsbuf.maxsize != old.maxsize || prog->tempstringsbuf.data == NULL)
3144 Con_DPrintf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, prog->tempstringsbuf.maxsize/1024);
3145 prog->tempstringsbuf.data = (unsigned char *) Mem_Alloc(prog->progs_mempool, prog->tempstringsbuf.maxsize);
3149 memcpy(prog->tempstringsbuf.data, old.data, old.cursize);
3154 t = (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.cursize;
3156 prog->tempstringsbuf.cursize += size;
3157 return PRVM_SetEngineString(prog, t);
3160 int PRVM_AllocString(prvm_prog_t *prog, size_t bufferlength, char **pointer)
3170 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3171 if (!prog->knownstrings[i])
3173 s = (char *)PRVM_Alloc(bufferlength);
3174 PRVM_NewKnownString(prog, i, KNOWNSTRINGFLAG_GCMARK, s);
3175 if(prog->leaktest_active)
3176 prog->knownstrings_origin[i] = PRVM_AllocationOrigin(prog);
3178 *pointer = (char *)(prog->knownstrings[i]);
3179 return PRVM_KNOWNSTRINGBASE + i;
3182 void PRVM_FreeString(prvm_prog_t *prog, int num)
3185 prog->error_cmd("PRVM_FreeString: attempt to free a NULL string");
3186 else if (num >= 0 && num < prog->stringssize)
3187 prog->error_cmd("PRVM_FreeString: attempt to free a constant string");
3188 else if (num >= PRVM_KNOWNSTRINGBASE && num < PRVM_KNOWNSTRINGBASE + prog->numknownstrings)
3190 num = num - PRVM_KNOWNSTRINGBASE;
3191 if (!prog->knownstrings[num])
3192 prog->error_cmd("PRVM_FreeString: attempt to free a non-existent or already freed string");
3193 if (!prog->knownstrings_flags[num])
3194 prog->error_cmd("PRVM_FreeString: attempt to free a string owned by the engine");
3195 PRVM_Free((char *)prog->knownstrings[num]);
3196 if(prog->leaktest_active)
3197 if(prog->knownstrings_origin[num])
3198 PRVM_Free((char *)prog->knownstrings_origin[num]);
3199 prog->knownstrings[num] = NULL;
3200 prog->knownstrings_flags[num] = 0;
3201 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
3204 prog->error_cmd("PRVM_FreeString: invalid string offset %i", num);
3207 static qboolean PRVM_IsStringReferenced(prvm_prog_t *prog, string_t string)
3211 for (i = 0;i < prog->numglobaldefs;i++)
3213 ddef_t *d = &prog->globaldefs[i];
3214 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3216 if(string == PRVM_GLOBALFIELDSTRING(d->ofs))
3220 for(j = 0; j < prog->num_edicts; ++j)
3222 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3223 if (ed->priv.required->free)
3225 for (i=0; i<prog->numfielddefs; ++i)
3227 ddef_t *d = &prog->fielddefs[i];
3228 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3230 if(string == PRVM_EDICTFIELDSTRING(ed, d->ofs))
3238 static qboolean PRVM_IsEdictRelevant(prvm_prog_t *prog, prvm_edict_t *edict)
3242 if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
3243 return true; // world or clients
3244 if (edict->priv.required->freetime <= prog->inittime)
3245 return true; // created during startup
3246 if (prog == SVVM_prog)
3248 if(PRVM_serveredictfloat(edict, solid)) // can block other stuff, or is a trigger?
3250 if(PRVM_serveredictfloat(edict, modelindex)) // visible ent?
3252 if(PRVM_serveredictfloat(edict, effects)) // particle effect?
3254 if(PRVM_serveredictfunction(edict, think)) // has a think function?
3255 if(PRVM_serveredictfloat(edict, nextthink) > 0) // that actually will eventually run?
3257 if(PRVM_serveredictfloat(edict, takedamage))
3259 if(*prvm_leaktest_ignore_classnames.string)
3261 if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_serveredictstring(edict, classname)))))
3265 else if (prog == CLVM_prog)
3267 // TODO someone add more stuff here
3268 if(PRVM_clientedictfloat(edict, entnum)) // csqc networked
3270 if(PRVM_clientedictfloat(edict, modelindex)) // visible ent?
3272 if(PRVM_clientedictfloat(edict, effects)) // particle effect?
3274 if(PRVM_clientedictfunction(edict, think)) // has a think function?
3275 if(PRVM_clientedictfloat(edict, nextthink) > 0) // that actually will eventually run?
3277 if(*prvm_leaktest_ignore_classnames.string)
3279 if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_clientedictstring(edict, classname)))))
3285 // menu prog does not have classnames
3290 static qboolean PRVM_IsEdictReferenced(prvm_prog_t *prog, prvm_edict_t *edict, int mark)
3293 int edictnum = PRVM_NUM_FOR_EDICT(edict);
3294 const char *targetname = NULL;
3296 if (prog == SVVM_prog && prvm_leaktest_follow_targetname.integer)
3297 targetname = PRVM_GetString(prog, PRVM_serveredictstring(edict, targetname));
3300 if(!*targetname) // ""
3303 for(j = 0; j < prog->num_edicts; ++j)
3305 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3306 if (ed->priv.required->mark < mark)
3312 const char *target = PRVM_GetString(prog, PRVM_serveredictstring(ed, target));
3314 if(!strcmp(target, targetname))
3317 for (i=0; i<prog->numfielddefs; ++i)
3319 ddef_t *d = &prog->fielddefs[i];
3320 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3322 if(edictnum == PRVM_EDICTFIELDEDICT(ed, d->ofs))
3330 static void PRVM_MarkReferencedEdicts(prvm_prog_t *prog)
3336 // Stage 1: world, all entities that are relevant, and all entities that are referenced by globals.
3338 for(j = 0; j < prog->num_edicts; ++j)
3340 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3341 if(ed->priv.required->free)
3343 ed->priv.required->mark = PRVM_IsEdictRelevant(prog, ed) ? stage : 0;
3345 for (i = 0;i < prog->numglobaldefs;i++)
3347 ddef_t *d = &prog->globaldefs[i];
3349 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3351 j = PRVM_GLOBALFIELDEDICT(d->ofs);
3352 if (i < 0 || j >= prog->max_edicts) {
3353 Con_Printf("Invalid entity reference from global %s.\n", PRVM_GetString(prog, d->s_name));
3356 ed = PRVM_EDICT_NUM(j);;
3357 ed->priv.required->mark = stage;
3360 // Future stages: all entities that are referenced by an entity of the previous stage.
3364 for(j = 0; j < prog->num_edicts; ++j)
3366 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3367 if(ed->priv.required->free)
3369 if(ed->priv.required->mark)
3371 if(PRVM_IsEdictReferenced(prog, ed, stage))
3373 ed->priv.required->mark = stage + 1;
3380 Con_DPrintf("leak check used %d stages to find all references\n", stage);
3383 void PRVM_LeakTest(prvm_prog_t *prog)
3386 qboolean leaked = false;
3388 if(!prog->leaktest_active)
3392 for (i = 0; i < prog->numknownstrings; ++i)
3394 if(prog->knownstrings[i])
3395 if(prog->knownstrings_flags[i])
3396 if(prog->knownstrings_origin[i])
3397 if(!PRVM_IsStringReferenced(prog, PRVM_KNOWNSTRINGBASE + i))
3399 Con_Printf("Unreferenced string found!\n Value: %s\n Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
3405 PRVM_MarkReferencedEdicts(prog);
3406 for(j = 0; j < prog->num_edicts; ++j)
3408 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3409 if(ed->priv.required->free)
3411 if(!ed->priv.required->mark)
3412 if(ed->priv.required->allocation_origin)
3414 Con_Printf("Unreferenced edict found!\n Allocated at: %s\n", ed->priv.required->allocation_origin);
3415 PRVM_ED_Print(prog, ed, NULL);
3420 ed->priv.required->mark = 0; // clear marks again when done
3423 for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
3425 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
3427 if(stringbuffer->origin)
3429 Con_Printf("Open string buffer handle found!\n Allocated at: %s\n", stringbuffer->origin);
3434 for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
3436 if(prog->openfiles[i])
3437 if(prog->openfiles_origin[i])
3439 Con_Printf("Open file handle found!\n Allocated at: %s\n", prog->openfiles_origin[i]);
3444 for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
3446 if(prog->opensearches[i])
3447 if(prog->opensearches_origin[i])
3449 Con_Printf("Open search handle found!\n Allocated at: %s\n", prog->opensearches_origin[i]);
3455 Con_Printf("Congratulations. No leaks found.\n");
3458 void PRVM_GarbageCollection(prvm_prog_t *prog)
3460 int limit = prvm_garbagecollection_scan_limit.integer;
3461 prvm_prog_garbagecollection_state_t *gc = &prog->gc;
3462 if (!prvm_garbagecollection_enable.integer)
3465 // we like to limit how much scanning we do so it doesn't put a significant
3466 // burden on the cpu, so each of these are not complete scans, we also like
3467 // to have consistent cpu usage so we do a bit of work on each category of
3468 // leaked object every frame
3474 case PRVM_GC_GLOBALS_MARK:
3475 for (; gc->globals_mark_progress < prog->numglobaldefs && (limit--) > 0; gc->globals_mark_progress++)
3477 ddef_t *d = &prog->globaldefs[gc->globals_mark_progress];
3482 prvm_int_t s = prog->globals.ip[d->ofs];
3483 if (s & PRVM_KNOWNSTRINGBASE)
3485 prvm_int_t num = s - PRVM_KNOWNSTRINGBASE;
3486 if (!prog->knownstrings[num])
3489 Con_DPrintf("PRVM_GarbageCollection: Found bogus strzone reference in global %i (global name: \"%s\"), erasing reference", d->ofs, PRVM_GetString(prog, d->s_name));
3490 prog->globals.ip[d->ofs] = 0;
3493 prog->knownstrings_flags[num] = (prog->knownstrings_flags[num] | KNOWNSTRINGFLAG_GCMARK) & ~KNOWNSTRINGFLAG_GCPRUNE;
3501 if (gc->globals_mark_progress >= prog->numglobaldefs)
3504 case PRVM_GC_FIELDS_MARK:
3505 for (; gc->fields_mark_progress < prog->numfielddefs && limit > 0;)
3507 ddef_t *d = &prog->fielddefs[gc->fields_mark_progress];
3511 //for (gc-> entityindex = 0; entityindex < prog->num_edicts; entityindex++)
3512 for (;gc->fields_mark_progress_entity < prog->num_edicts && (limit--) > 0;gc->fields_mark_progress_entity++)
3514 int entityindex = gc->fields_mark_progress_entity;
3515 prvm_int_t s = prog->edictsfields.ip[entityindex * prog->entityfields + d->ofs];
3516 if (s & PRVM_KNOWNSTRINGBASE)
3518 prvm_int_t num = s - PRVM_KNOWNSTRINGBASE;
3519 if (!prog->knownstrings[num])
3522 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));
3523 prog->edictsfields.ip[entityindex * prog->entityfields + d->ofs] = 0;
3526 prog->knownstrings_flags[num] = (prog->knownstrings_flags[num] | KNOWNSTRINGFLAG_GCMARK) & ~KNOWNSTRINGFLAG_GCPRUNE;
3529 if (gc->fields_mark_progress_entity >= prog->num_edicts)
3531 gc->fields_mark_progress_entity = 0;
3532 gc->fields_mark_progress++;
3536 gc->fields_mark_progress_entity = 0;
3537 gc->fields_mark_progress++;
3541 if (gc->fields_mark_progress >= prog->numfielddefs)
3544 case PRVM_GC_KNOWNSTRINGS_SWEEP:
3545 // free any strzone'd strings that are not marked
3546 if (!prvm_garbagecollection_strings.integer)
3551 for (;gc->knownstrings_sweep_progress < prog->numknownstrings && (limit--) > 0;gc->knownstrings_sweep_progress++)
3553 int num = gc->knownstrings_sweep_progress;
3554 if (prog->knownstrings[num] && (prog->knownstrings_flags[num] & (KNOWNSTRINGFLAG_GCMARK | KNOWNSTRINGFLAG_ENGINE)) == 0)
3556 if (prog->knownstrings_flags[num] & KNOWNSTRINGFLAG_GCPRUNE)
3558 // string has been marked for pruning two passes in a row
3559 if (prvm_garbagecollection_notify.integer)
3560 Con_DPrintf("prvm_garbagecollection_notify: %s: freeing unreferenced string %i: \"%s\"\n", prog->name, num, prog->knownstrings[num]);
3561 Mem_Free((char *)prog->knownstrings[num]);
3562 prog->knownstrings[num] = NULL;
3563 prog->knownstrings_flags[num] = 0;
3564 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
3568 // mark it for pruning next pass
3569 prog->knownstrings_flags[num] |= KNOWNSTRINGFLAG_GCPRUNE;
3573 if (gc->knownstrings_sweep_progress >= prog->numknownstrings)
3578 memset(gc, 0, sizeof(*gc));