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 %" PRVM_PRIi "() (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 %" PRVM_PRIi " (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 %" PRVM_PRIi " (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 %" PRVM_PRIi "(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 void PRVM_ED_Write (prvm_prog_t *prog, qfile_t *f, prvm_edict_t *ed)
726 char valuebuf[MAX_INPUTLINE];
730 if (ed->priv.required->free)
736 for (i = 1;i < prog->numfielddefs;i++)
738 d = &prog->fielddefs[i];
739 name = PRVM_GetString(prog, d->s_name);
741 if(developer_entityparsing.integer)
742 Con_Printf("PRVM_ED_Write: at entity %d field %s\n", PRVM_NUM_FOR_EDICT(ed), name);
744 //if(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
745 if(strlen(name) > 1 && name[strlen(name)-2] == '_')
746 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?)
748 val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
750 // if the value is still all 0, skip the field
751 type = d->type & ~DEF_SAVEGLOBAL;
752 for (j=0 ; j<prvm_type_size[type] ; j++)
755 if (j == prvm_type_size[type])
758 FS_Printf(f,"\"%s\" ",name);
759 prog->statestring = va(vabuf, sizeof(vabuf), "PRVM_ED_Write, ent=%d, name=%s", i, name);
760 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString(prog, (etype_t)d->type, val, valuebuf, sizeof(valuebuf)));
761 prog->statestring = NULL;
767 void PRVM_ED_PrintNum (prvm_prog_t *prog, int ent, const char *wildcard_fieldname)
769 PRVM_ED_Print(prog, PRVM_EDICT_NUM(ent), wildcard_fieldname);
774 PRVM_ED_PrintEdicts_f
776 For debugging, prints all the entities in the current server
779 void PRVM_ED_PrintEdicts_f(cmd_state_t *cmd)
783 const char *wildcard_fieldname;
785 if(Cmd_Argc(cmd) < 2 || Cmd_Argc(cmd) > 3)
787 Con_Print("prvm_edicts <program name> <optional field name wildcard>\n");
791 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
794 if( Cmd_Argc(cmd) == 3)
795 wildcard_fieldname = Cmd_Argv(cmd, 2);
797 wildcard_fieldname = NULL;
799 Con_Printf("%s: %i entities\n", prog->name, prog->num_edicts);
800 for (i=0 ; i<prog->num_edicts ; i++)
801 PRVM_ED_PrintNum (prog, i, wildcard_fieldname);
808 For debugging, prints a single edict
811 static void PRVM_ED_PrintEdict_f(cmd_state_t *cmd)
815 const char *wildcard_fieldname;
817 if(Cmd_Argc(cmd) < 3 || Cmd_Argc(cmd) > 4)
819 Con_Print("prvm_edict <program name> <edict number> <optional field name wildcard>\n");
823 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
826 i = atoi (Cmd_Argv(cmd, 2));
827 if (i >= prog->num_edicts)
829 Con_Print("Bad edict number\n");
832 if( Cmd_Argc(cmd) == 4)
833 // Optional Wildcard Provided
834 wildcard_fieldname = Cmd_Argv(cmd, 3);
837 wildcard_fieldname = NULL;
838 PRVM_ED_PrintNum (prog, i, wildcard_fieldname);
848 // 2 possibilities : 1. just displaying the active edict count
849 // 2. making a function pointer [x]
850 static void PRVM_ED_Count_f(cmd_state_t *cmd)
854 if(Cmd_Argc(cmd) != 2)
856 Con_Print("prvm_count <program name>\n");
860 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
863 prog->count_edicts(prog);
867 ==============================================================================
871 FIXME: need to tag constants, doesn't really work
872 ==============================================================================
880 void PRVM_ED_WriteGlobals (prvm_prog_t *prog, qfile_t *f)
887 char valuebuf[MAX_INPUTLINE];
890 for (i = 0;i < prog->numglobaldefs;i++)
892 def = &prog->globaldefs[i];
894 if ( !(def->type & DEF_SAVEGLOBAL) )
896 type &= ~DEF_SAVEGLOBAL;
898 if (type != ev_string && type != ev_float && type != ev_entity)
901 name = PRVM_GetString(prog, def->s_name);
903 if(developer_entityparsing.integer)
904 Con_Printf("PRVM_ED_WriteGlobals: at global %s\n", name);
906 prog->statestring = va(vabuf, sizeof(vabuf), "PRVM_ED_WriteGlobals, name=%s", name);
907 FS_Printf(f,"\"%s\" ", name);
908 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString(prog, (etype_t)type, (prvm_eval_t *)&prog->globals.fp[def->ofs], valuebuf, sizeof(valuebuf)));
909 prog->statestring = NULL;
919 void PRVM_ED_ParseGlobals (prvm_prog_t *prog, const char *data)
921 char keyname[MAX_INPUTLINE];
927 if (!COM_ParseToken_Simple(&data, false, false, true))
928 prog->error_cmd("PRVM_ED_ParseGlobals: EOF without closing brace");
929 if (com_token[0] == '}')
932 if (developer_entityparsing.integer)
933 Con_Printf("Key: \"%s\"", com_token);
935 strlcpy (keyname, com_token, sizeof(keyname));
938 if (!COM_ParseToken_Simple(&data, false, true, true))
939 prog->error_cmd("PRVM_ED_ParseGlobals: EOF without closing brace");
941 if (developer_entityparsing.integer)
942 Con_Printf(" \"%s\"\n", com_token);
944 if (com_token[0] == '}')
945 prog->error_cmd("PRVM_ED_ParseGlobals: closing brace without data");
947 key = PRVM_ED_FindGlobal (prog, keyname);
950 Con_DPrintf("'%s' is not a global on %s\n", keyname, prog->name);
954 if (!PRVM_ED_ParseEpair(prog, NULL, key, com_token, true))
955 prog->error_cmd("PRVM_ED_ParseGlobals: parse error");
959 //============================================================================
966 Can parse either fields or globals
967 returns false if error
970 qboolean PRVM_ED_ParseEpair(prvm_prog_t *prog, prvm_edict_t *ent, ddef_t *key, const char *s, qboolean parsebackslash)
979 val = (prvm_eval_t *)(ent->fields.fp + key->ofs);
981 val = (prvm_eval_t *)(prog->globals.fp + key->ofs);
982 switch (key->type & ~DEF_SAVEGLOBAL)
985 l = (int)strlen(s) + 1;
986 val->string = PRVM_AllocString(prog, l, &new_p);
987 for (i = 0;i < l;i++)
989 if (s[i] == '\\' && s[i+1] && parsebackslash)
994 else if (s[i] == 'r')
1005 while (*s && ISWHITESPACE(*s))
1007 val->_float = atof(s);
1011 for (i = 0;i < 3;i++)
1013 while (*s && ISWHITESPACE(*s))
1017 val->vector[i] = atof(s);
1018 while (!ISWHITESPACE(*s))
1026 while (*s && ISWHITESPACE(*s))
1029 if (i >= prog->limit_edicts)
1030 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);
1031 while (i >= prog->max_edicts)
1032 PRVM_MEM_IncreaseEdicts(prog);
1033 // if IncreaseEdicts was called the base pointer needs to be updated
1035 val = (prvm_eval_t *)(ent->fields.fp + key->ofs);
1036 val->edict = PRVM_EDICT_TO_PROG(PRVM_EDICT_NUM((int)i));
1042 Con_DPrintf("PRVM_ED_ParseEpair: Bogus field name %s in %s\n", s, prog->name);
1045 def = PRVM_ED_FindField(prog, s + 1);
1048 Con_DPrintf("PRVM_ED_ParseEpair: Can't find field %s in %s\n", s, prog->name);
1051 val->_int = def->ofs;
1055 func = PRVM_ED_FindFunction(prog, s);
1058 Con_Printf("PRVM_ED_ParseEpair: Can't find function %s in %s\n", s, prog->name);
1061 val->function = func - prog->functions;
1065 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);
1075 Console command to send a string to QC function GameCommand of the
1079 sv_cmd adminmsg 3 "do not teamkill"
1080 cl_cmd someclientcommand
1081 menu_cmd somemenucommand
1083 All progs can support this extension; sg calls it in server QC, cg in client
1087 static void PRVM_GameCommand(cmd_state_t *cmd, const char *whichprogs, const char *whichcmd)
1090 if(Cmd_Argc(cmd) < 1)
1092 Con_Printf("%s text...\n", whichcmd);
1096 if (!(prog = PRVM_FriendlyProgFromString(whichprogs)))
1099 if(!PRVM_allfunction(GameCommand))
1101 Con_Printf("%s program do not support GameCommand!\n", whichprogs);
1105 int restorevm_tempstringsbuf_cursize;
1110 restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
1111 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(prog, s ? s : "");
1112 prog->ExecuteProgram(prog, PRVM_allfunction(GameCommand), "QC function GameCommand is missing");
1113 prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1116 static void PRVM_GameCommand_Server_f(cmd_state_t *cmd)
1118 PRVM_GameCommand(cmd, "server", "sv_cmd");
1120 static void PRVM_GameCommand_Client_f(cmd_state_t *cmd)
1122 PRVM_GameCommand(cmd, "client", "cl_cmd");
1124 static void PRVM_GameCommand_Menu_f(cmd_state_t *cmd)
1126 PRVM_GameCommand(cmd, "menu", "menu_cmd");
1133 Console command to load a field of a specified edict
1136 static void PRVM_ED_EdictGet_f(cmd_state_t *cmd)
1143 char valuebuf[MAX_INPUTLINE];
1145 if(Cmd_Argc(cmd) != 4 && Cmd_Argc(cmd) != 5)
1147 Con_Print("prvm_edictget <program name> <edict number> <field> [<cvar>]\n");
1151 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
1154 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(cmd, 2)));
1156 if((key = PRVM_ED_FindField(prog, Cmd_Argv(cmd, 3))) == 0)
1158 Con_Printf("Key %s not found !\n", Cmd_Argv(cmd, 3));
1162 v = (prvm_eval_t *)(ed->fields.fp + key->ofs);
1163 s = PRVM_UglyValueString(prog, (etype_t)key->type, v, valuebuf, sizeof(valuebuf));
1164 if(Cmd_Argc(cmd) == 5)
1166 cvar_t *cvar = Cvar_FindVar(cmd->cvars, Cmd_Argv(cmd, 4), cmd->cvars_flagsmask);
1168 if(Cvar_Readonly(cvar, "prvm_edictget"))
1171 Cvar_Get(cmd->cvars, Cmd_Argv(cmd, 4), s, cmd->cvars_flagsmask, NULL);
1174 Con_Printf("%s\n", s);
1180 static void PRVM_ED_GlobalGet_f(cmd_state_t *cmd)
1186 char valuebuf[MAX_INPUTLINE];
1188 if(Cmd_Argc(cmd) != 3 && Cmd_Argc(cmd) != 4)
1190 Con_Print("prvm_globalget <program name> <global> [<cvar>]\n");
1194 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
1197 key = PRVM_ED_FindGlobal(prog, Cmd_Argv(cmd, 2));
1200 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(cmd, 2), Cmd_Argv(cmd, 1) );
1204 v = (prvm_eval_t *) &prog->globals.fp[key->ofs];
1205 s = PRVM_UglyValueString(prog, (etype_t)key->type, v, valuebuf, sizeof(valuebuf));
1206 if(Cmd_Argc(cmd) == 4)
1208 cvar_t *cvar = Cvar_FindVar(cmd->cvars, Cmd_Argv(cmd, 3), cmd->cvars_flagsmask);
1210 if(Cvar_Readonly(cvar, "prvm_globalget"))
1212 Cvar_Get(cmd->cvars, Cmd_Argv(cmd, 3), s, cmd->cvars_flagsmask, NULL);
1215 Con_Printf("%s\n", s);
1225 Console command to set a field of a specified edict
1228 static void PRVM_ED_EdictSet_f(cmd_state_t *cmd)
1234 if(Cmd_Argc(cmd) != 5)
1236 Con_Print("prvm_edictset <program name> <edict number> <field> <value>\n");
1240 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
1243 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(cmd, 2)));
1245 if((key = PRVM_ED_FindField(prog, Cmd_Argv(cmd, 3))) == 0)
1246 Con_Printf("Key %s not found !\n", Cmd_Argv(cmd, 3));
1248 PRVM_ED_ParseEpair(prog, ed, key, Cmd_Argv(cmd, 4), true);
1252 ====================
1255 Parses an edict out of the given string, returning the new position
1256 ed should be a properly initialized empty edict.
1257 Used for initial level load and for savegames.
1258 ====================
1260 const char *PRVM_ED_ParseEdict (prvm_prog_t *prog, const char *data, prvm_edict_t *ent)
1270 // go through all the dictionary pairs
1274 if (!COM_ParseToken_Simple(&data, false, false, true))
1275 prog->error_cmd("PRVM_ED_ParseEdict: EOF without closing brace");
1276 if (developer_entityparsing.integer)
1277 Con_Printf("Key: \"%s\"", com_token);
1278 if (com_token[0] == '}')
1281 // anglehack is to allow QuakeEd to write single scalar angles
1282 // and allow them to be turned into vectors. (FIXME...)
1283 if (!strcmp(com_token, "angle"))
1285 strlcpy (com_token, "angles", sizeof(com_token));
1291 // FIXME: change light to _light to get rid of this hack
1292 if (!strcmp(com_token, "light"))
1293 strlcpy (com_token, "light_lev", sizeof(com_token)); // hack for single light def
1295 strlcpy (keyname, com_token, sizeof(keyname));
1297 // another hack to fix keynames with trailing spaces
1298 n = strlen(keyname);
1299 while (n && keyname[n-1] == ' ')
1306 if (!COM_ParseToken_Simple(&data, false, false, true))
1307 prog->error_cmd("PRVM_ED_ParseEdict: EOF without closing brace");
1308 if (developer_entityparsing.integer)
1309 Con_Printf(" \"%s\"\n", com_token);
1311 if (com_token[0] == '}')
1312 prog->error_cmd("PRVM_ED_ParseEdict: closing brace without data");
1316 // ignore attempts to set key "" (this problem occurs in nehahra neh1m8.bsp)
1320 // keynames with a leading underscore are used for utility comments,
1321 // and are immediately discarded by quake
1322 if (keyname[0] == '_')
1325 key = PRVM_ED_FindField (prog, keyname);
1328 Con_DPrintf("%s: '%s' is not a field\n", prog->name, keyname);
1335 strlcpy (temp, com_token, sizeof(temp));
1336 dpsnprintf (com_token, sizeof(com_token), "0 %s 0", temp);
1339 if (!PRVM_ED_ParseEpair(prog, ent, key, com_token, strcmp(keyname, "wad") != 0))
1340 prog->error_cmd("PRVM_ED_ParseEdict: parse error");
1344 ent->priv.required->free = true;
1345 ent->priv.required->freetime = host.realtime;
1354 PRVM_ED_LoadFromFile
1356 The entities are directly placed in the array, rather than allocated with
1357 PRVM_ED_Alloc, because otherwise an error loading the map would have entity
1358 number references out of order.
1360 Creates a server's entity / program execution context by
1361 parsing textual entity definitions out of an ent file.
1363 Used for both fresh maps and savegame loads. A fresh map would also need
1364 to call PRVM_ED_CallSpawnFunctions () to let the objects initialize themselves.
1367 void PRVM_ED_LoadFromFile (prvm_prog_t *prog, const char *data)
1371 int parsed, inhibited, spawned, died;
1372 ddef_t *fulldata_ddef = NULL;
1373 prvm_eval_t *fulldata = NULL;
1374 const char *funcname;
1383 prvm_reuseedicts_always_allow = host.realtime;
1390 // parse the opening brace
1391 if (!COM_ParseToken_Simple(&data, false, false, true))
1393 if (com_token[0] != '{')
1394 prog->error_cmd("PRVM_ED_LoadFromFile: %s: found %s when expecting {", prog->name, com_token);
1396 // CHANGED: this is not conform to PR_LoadFromFile
1397 if(prog->loadintoworld)
1399 prog->loadintoworld = false;
1400 ent = PRVM_EDICT_NUM(0);
1403 ent = PRVM_ED_Alloc(prog);
1406 if (ent != prog->edicts) // hack
1407 memset (ent->fields.fp, 0, prog->entityfields * sizeof(prvm_vec_t));
1409 data = PRVM_ED_ParseEdict (prog, data, ent);
1412 // remove the entity ?
1413 if(!prog->load_edict(prog, ent))
1415 PRVM_ED_Free(prog, ent);
1420 if (PRVM_serverfunction(SV_OnEntityPreSpawnFunction))
1423 PRVM_serverglobalfloat(time) = sv.time;
1424 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1425 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityPreSpawnFunction), "QC function SV_OnEntityPreSpawnFunction is missing");
1428 if(ent->priv.required->free)
1435 // immediately call spawn function, but only if there is a self global and a classname
1437 if(!ent->priv.required->free)
1439 if (!PRVM_alledictstring(ent, classname))
1441 Con_Print("No classname for:\n");
1442 PRVM_ED_Print(prog, ent, NULL);
1443 PRVM_ED_Free (prog, ent);
1447 * This is required for FTE compatibility (FreeCS).
1448 * It copies the key/value pairs themselves into a
1449 * global for QC to parse on its own.
1453 fulldata_ddef = PRVM_ED_FindGlobal(prog, "__fullspawndata");
1455 fulldata = (prvm_eval_t *) &prog->globals.fp[fulldata_ddef->ofs];
1460 fulldata->string = PRVM_AllocString(prog, data - start + 1, &spawndata);
1461 for(in = start; in < data; )
1465 *spawndata++ = '\t';
1473 // look for the spawn function
1474 funcname = PRVM_GetString(prog, PRVM_alledictstring(ent, classname));
1475 func = PRVM_ED_FindFunction (prog, va(vabuf, sizeof(vabuf), "spawnfunc_%s", funcname));
1477 if(!PRVM_allglobalfloat(require_spawnfunc_prefix))
1478 func = PRVM_ED_FindFunction (prog, funcname);
1482 // check for OnEntityNoSpawnFunction
1483 if (PRVM_serverfunction(SV_OnEntityNoSpawnFunction))
1486 PRVM_serverglobalfloat(time) = sv.time;
1487 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1488 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityNoSpawnFunction), "QC function SV_OnEntityNoSpawnFunction is missing");
1492 if (developer.integer > 0) // don't confuse non-developers with errors
1494 Con_Print("No spawn function for:\n");
1495 PRVM_ED_Print(prog, ent, NULL);
1497 PRVM_ED_Free (prog, ent);
1498 continue; // not included in "inhibited" count
1504 PRVM_serverglobalfloat(time) = sv.time;
1505 PRVM_allglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1506 prog->ExecuteProgram(prog, func - prog->functions, "");
1510 if(!ent->priv.required->free)
1511 if (PRVM_serverfunction(SV_OnEntityPostSpawnFunction))
1514 PRVM_serverglobalfloat(time) = sv.time;
1515 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1516 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityPostSpawnFunction), "QC function SV_OnEntityPostSpawnFunction is missing");
1520 if (ent->priv.required->free)
1524 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);
1526 prvm_reuseedicts_always_allow = 0;
1529 static void PRVM_FindOffsets(prvm_prog_t *prog)
1531 // field and global searches use -1 for NULL
1532 memset(&prog->fieldoffsets, -1, sizeof(prog->fieldoffsets));
1533 memset(&prog->globaloffsets, -1, sizeof(prog->globaloffsets));
1534 // function searches use 0 for NULL
1535 memset(&prog->funcoffsets, 0, sizeof(prog->funcoffsets));
1536 #define PRVM_DECLARE_serverglobalfloat(x)
1537 #define PRVM_DECLARE_serverglobalvector(x)
1538 #define PRVM_DECLARE_serverglobalstring(x)
1539 #define PRVM_DECLARE_serverglobaledict(x)
1540 #define PRVM_DECLARE_serverglobalfunction(x)
1541 #define PRVM_DECLARE_clientglobalfloat(x)
1542 #define PRVM_DECLARE_clientglobalvector(x)
1543 #define PRVM_DECLARE_clientglobalstring(x)
1544 #define PRVM_DECLARE_clientglobaledict(x)
1545 #define PRVM_DECLARE_clientglobalfunction(x)
1546 #define PRVM_DECLARE_menuglobalfloat(x)
1547 #define PRVM_DECLARE_menuglobalvector(x)
1548 #define PRVM_DECLARE_menuglobalstring(x)
1549 #define PRVM_DECLARE_menuglobaledict(x)
1550 #define PRVM_DECLARE_menuglobalfunction(x)
1551 #define PRVM_DECLARE_serverfieldfloat(x)
1552 #define PRVM_DECLARE_serverfieldvector(x)
1553 #define PRVM_DECLARE_serverfieldstring(x)
1554 #define PRVM_DECLARE_serverfieldedict(x)
1555 #define PRVM_DECLARE_serverfieldfunction(x)
1556 #define PRVM_DECLARE_clientfieldfloat(x)
1557 #define PRVM_DECLARE_clientfieldvector(x)
1558 #define PRVM_DECLARE_clientfieldstring(x)
1559 #define PRVM_DECLARE_clientfieldedict(x)
1560 #define PRVM_DECLARE_clientfieldfunction(x)
1561 #define PRVM_DECLARE_menufieldfloat(x)
1562 #define PRVM_DECLARE_menufieldvector(x)
1563 #define PRVM_DECLARE_menufieldstring(x)
1564 #define PRVM_DECLARE_menufieldedict(x)
1565 #define PRVM_DECLARE_menufieldfunction(x)
1566 #define PRVM_DECLARE_serverfunction(x)
1567 #define PRVM_DECLARE_clientfunction(x)
1568 #define PRVM_DECLARE_menufunction(x)
1569 #define PRVM_DECLARE_field(x) prog->fieldoffsets.x = PRVM_ED_FindFieldOffset(prog, #x);
1570 #define PRVM_DECLARE_global(x) prog->globaloffsets.x = PRVM_ED_FindGlobalOffset(prog, #x);
1571 #define PRVM_DECLARE_function(x) prog->funcoffsets.x = PRVM_ED_FindFunctionOffset(prog, #x);
1572 #include "prvm_offsets.h"
1573 #undef PRVM_DECLARE_serverglobalfloat
1574 #undef PRVM_DECLARE_serverglobalvector
1575 #undef PRVM_DECLARE_serverglobalstring
1576 #undef PRVM_DECLARE_serverglobaledict
1577 #undef PRVM_DECLARE_serverglobalfunction
1578 #undef PRVM_DECLARE_clientglobalfloat
1579 #undef PRVM_DECLARE_clientglobalvector
1580 #undef PRVM_DECLARE_clientglobalstring
1581 #undef PRVM_DECLARE_clientglobaledict
1582 #undef PRVM_DECLARE_clientglobalfunction
1583 #undef PRVM_DECLARE_menuglobalfloat
1584 #undef PRVM_DECLARE_menuglobalvector
1585 #undef PRVM_DECLARE_menuglobalstring
1586 #undef PRVM_DECLARE_menuglobaledict
1587 #undef PRVM_DECLARE_menuglobalfunction
1588 #undef PRVM_DECLARE_serverfieldfloat
1589 #undef PRVM_DECLARE_serverfieldvector
1590 #undef PRVM_DECLARE_serverfieldstring
1591 #undef PRVM_DECLARE_serverfieldedict
1592 #undef PRVM_DECLARE_serverfieldfunction
1593 #undef PRVM_DECLARE_clientfieldfloat
1594 #undef PRVM_DECLARE_clientfieldvector
1595 #undef PRVM_DECLARE_clientfieldstring
1596 #undef PRVM_DECLARE_clientfieldedict
1597 #undef PRVM_DECLARE_clientfieldfunction
1598 #undef PRVM_DECLARE_menufieldfloat
1599 #undef PRVM_DECLARE_menufieldvector
1600 #undef PRVM_DECLARE_menufieldstring
1601 #undef PRVM_DECLARE_menufieldedict
1602 #undef PRVM_DECLARE_menufieldfunction
1603 #undef PRVM_DECLARE_serverfunction
1604 #undef PRVM_DECLARE_clientfunction
1605 #undef PRVM_DECLARE_menufunction
1606 #undef PRVM_DECLARE_field
1607 #undef PRVM_DECLARE_global
1608 #undef PRVM_DECLARE_function
1613 typedef struct dpfield_s
1620 #define DPFIELDS (sizeof(dpfields) / sizeof(dpfield_t))
1622 dpfield_t dpfields[] =
1633 #define PO_HASHSIZE 16384
1634 typedef struct po_string_s
1637 struct po_string_s *nextonhashchain;
1642 po_string_t *hashtable[PO_HASHSIZE];
1645 static void PRVM_PO_UnparseString(char *out, const char *in, size_t outsize)
1654 case '\a': if(outsize >= 2) { *out++ = '\\'; *out++ = 'a'; outsize -= 2; } break;
1655 case '\b': if(outsize >= 2) { *out++ = '\\'; *out++ = 'b'; outsize -= 2; } break;
1656 case '\t': if(outsize >= 2) { *out++ = '\\'; *out++ = 't'; outsize -= 2; } break;
1657 case '\r': if(outsize >= 2) { *out++ = '\\'; *out++ = 'r'; outsize -= 2; } break;
1658 case '\n': if(outsize >= 2) { *out++ = '\\'; *out++ = 'n'; outsize -= 2; } break;
1659 case '\\': if(outsize >= 2) { *out++ = '\\'; *out++ = '\\'; outsize -= 2; } break;
1660 case '"': if(outsize >= 2) { *out++ = '\\'; *out++ = '"'; outsize -= 2; } break;
1662 if(*in >= 0 && *in <= 0x1F)
1667 *out++ = '0' + ((*in & 0700) >> 6);
1668 *out++ = '0' + ((*in & 0070) >> 3);
1669 *out++ = '0' + (*in & 0007) ;
1686 static void PRVM_PO_ParseString(char *out, const char *in, size_t outsize)
1699 case 'a': if(outsize > 0) { *out++ = '\a'; --outsize; } break;
1700 case 'b': if(outsize > 0) { *out++ = '\b'; --outsize; } break;
1701 case 't': if(outsize > 0) { *out++ = '\t'; --outsize; } break;
1702 case 'r': if(outsize > 0) { *out++ = '\r'; --outsize; } break;
1703 case 'n': if(outsize > 0) { *out++ = '\n'; --outsize; } break;
1704 case '\\': if(outsize > 0) { *out++ = '\\'; --outsize; } break;
1705 case '"': if(outsize > 0) { *out++ = '"'; --outsize; } break;
1706 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
1710 if(*in >= '0' && *in <= '7')
1713 *out = (*out << 3) | (*in - '0');
1716 if(*in >= '0' && *in <= '7')
1719 *out = (*out << 3) | (*in - '0');
1730 if(outsize > 0) { *out++ = *in; --outsize; }
1745 static po_t *PRVM_PO_Load(const char *filename, const char *filename2, mempool_t *pool)
1750 char inbuf[MAX_INPUTLINE];
1751 char decodedbuf[MAX_INPUTLINE];
1754 po_string_t thisstr;
1757 for (i = 0; i < 2; ++i)
1759 const char *buf = (const char *)
1760 FS_LoadFile((i > 0 ? filename : filename2), pool, true, NULL);
1761 // first read filename2, then read filename
1762 // so that progs.dat.de.po wins over common.de.po
1763 // and within file, last item wins
1770 po = (po_t *)Mem_Alloc(pool, sizeof(*po));
1771 memset(po, 0, sizeof(*po));
1774 memset(&thisstr, 0, sizeof(thisstr)); // hush compiler warning
1782 p = strchr(p, '\n');
1788 if(*p == '\r' || *p == '\n')
1793 if(!strncmp(p, "msgid \"", 7))
1798 else if(!strncmp(p, "msgstr \"", 8))
1805 p = strchr(p, '\n');
1815 q = strchr(p, '\n');
1822 if((size_t)(q - p) >= (size_t) sizeof(inbuf))
1824 strlcpy(inbuf, p, q - p); // not - 1, because this adds a NUL
1825 PRVM_PO_ParseString(decodedbuf + decodedpos, inbuf, sizeof(decodedbuf) - decodedpos);
1826 decodedpos += strlen(decodedbuf + decodedpos);
1836 Mem_Free(thisstr.key);
1837 thisstr.key = (char *)Mem_Alloc(pool, decodedpos + 1);
1838 memcpy(thisstr.key, decodedbuf, decodedpos + 1);
1840 else if(decodedpos > 0 && thisstr.key) // skip empty translation results
1842 thisstr.value = (char *)Mem_Alloc(pool, decodedpos + 1);
1843 memcpy(thisstr.value, decodedbuf, decodedpos + 1);
1844 hashindex = CRC_Block((const unsigned char *) thisstr.key, strlen(thisstr.key)) % PO_HASHSIZE;
1845 thisstr.nextonhashchain = po->hashtable[hashindex];
1846 po->hashtable[hashindex] = (po_string_t *)Mem_Alloc(pool, sizeof(thisstr));
1847 memcpy(po->hashtable[hashindex], &thisstr, sizeof(thisstr));
1848 memset(&thisstr, 0, sizeof(thisstr));
1852 Mem_Free((char *) buf);
1857 static const char *PRVM_PO_Lookup(po_t *po, const char *str)
1859 int hashindex = CRC_Block((const unsigned char *) str, strlen(str)) % PO_HASHSIZE;
1860 po_string_t *p = po->hashtable[hashindex];
1863 if(!strcmp(str, p->key))
1865 p = p->nextonhashchain;
1869 static void PRVM_PO_Destroy(po_t *po)
1872 for(i = 0; i < PO_HASHSIZE; ++i)
1874 po_string_t *p = po->hashtable[i];
1878 p = p->nextonhashchain;
1887 void PRVM_LeakTest(prvm_prog_t *prog);
1888 void PRVM_Prog_Reset(prvm_prog_t *prog)
1892 if(prog->tempstringsbuf.cursize)
1893 Mem_Free(prog->tempstringsbuf.data);
1894 prog->tempstringsbuf.cursize = 0;
1895 PRVM_LeakTest(prog);
1896 prog->reset_cmd(prog);
1897 Mem_FreePool(&prog->progs_mempool);
1899 PRVM_PO_Destroy((po_t *) prog->po);
1901 memset(prog,0,sizeof(prvm_prog_t));
1902 prog->break_statement = -1;
1903 prog->watch_global_type = ev_void;
1904 prog->watch_field_type = ev_void;
1912 static void PRVM_LoadLNO( prvm_prog_t *prog, const char *progname ) {
1913 fs_offset_t filesize;
1915 unsigned int *header;
1918 FS_StripExtension( progname, filename, sizeof( filename ) );
1919 strlcat( filename, ".lno", sizeof( filename ) );
1921 lno = FS_LoadFile( filename, tempmempool, false, &filesize );
1927 <Spike> SafeWrite (h, &lnotype, sizeof(int));
1928 <Spike> SafeWrite (h, &version, sizeof(int));
1929 <Spike> SafeWrite (h, &numglobaldefs, sizeof(int));
1930 <Spike> SafeWrite (h, &numpr_globals, sizeof(int));
1931 <Spike> SafeWrite (h, &numfielddefs, sizeof(int));
1932 <Spike> SafeWrite (h, &numstatements, sizeof(int));
1933 <Spike> SafeWrite (h, statement_linenums, numstatements*sizeof(int));
1935 if ((unsigned int)filesize < (6 + prog->progs_numstatements) * sizeof(int))
1941 header = (unsigned int *) lno;
1942 if( header[ 0 ] == *(unsigned int *) "LNOF" &&
1943 LittleLong( header[ 1 ] ) == 1 &&
1944 (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs_numglobaldefs &&
1945 (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs_numglobals &&
1946 (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs_numfielddefs &&
1947 (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs_numstatements )
1949 prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof( int ) );
1950 memcpy( prog->statement_linenums, header + 6, prog->progs_numstatements * sizeof( int ) );
1952 /* gmqcc suports columnums */
1953 if ((unsigned int)filesize > ((6 + 2 * prog->progs_numstatements) * sizeof( int )))
1955 prog->statement_columnnums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof( int ) );
1956 memcpy( prog->statement_columnnums, header + 6 + prog->progs_numstatements, prog->progs_numstatements * sizeof( int ) );
1967 static void PRVM_UpdateBreakpoints(prvm_prog_t *prog);
1968 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)
1971 dprograms_t *dprograms;
1972 dstatement_t *instatements;
1973 ddef_t *infielddefs;
1974 ddef_t *inglobaldefs;
1976 dfunction_t *infunctions;
1978 fs_offset_t filesize;
1979 int requiredglobalspace;
1996 prog->error_cmd("PRVM_LoadProgs: there is already a %s program loaded!", prog->name );
1998 Host_LockSession(); // all progs can use the session cvar
1999 Crypto_LoadKeys(); // all progs might use the keys at init time
2003 dprograms = (dprograms_t *) data;
2007 dprograms = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
2008 if (dprograms == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
2009 prog->error_cmd("PRVM_LoadProgs: couldn't load %s for %s", filename, prog->name);
2010 // TODO bounds check header fields (e.g. numstatements), they must never go behind end of file
2012 prog->profiletime = Sys_DirtyTime();
2013 prog->starttime = host.realtime;
2015 Con_DPrintf("%s programs occupy %iK.\n", prog->name, (int)(filesize/1024));
2017 requiredglobalspace = 0;
2018 for (i = 0;i < numrequiredglobals;i++)
2019 requiredglobalspace += required_global[i].type == ev_vector ? 3 : 1;
2021 prog->filecrc = CRC_Block((unsigned char *)dprograms, filesize);
2023 // byte swap the header
2024 prog->progs_version = LittleLong(dprograms->version);
2025 prog->progs_crc = LittleLong(dprograms->crc);
2026 if (prog->progs_version != PROG_VERSION)
2027 prog->error_cmd("%s: %s has wrong version number (%i should be %i)", prog->name, filename, prog->progs_version, PROG_VERSION);
2028 instatements = (dstatement_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_statements));
2029 prog->progs_numstatements = LittleLong(dprograms->numstatements);
2030 inglobaldefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globaldefs));
2031 prog->progs_numglobaldefs = LittleLong(dprograms->numglobaldefs);
2032 infielddefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_fielddefs));
2033 prog->progs_numfielddefs = LittleLong(dprograms->numfielddefs);
2034 infunctions = (dfunction_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_functions));
2035 prog->progs_numfunctions = LittleLong(dprograms->numfunctions);
2036 instrings = (char *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_strings));
2037 prog->progs_numstrings = LittleLong(dprograms->numstrings);
2038 inglobals = (int *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globals));
2039 prog->progs_numglobals = LittleLong(dprograms->numglobals);
2040 prog->progs_entityfields = LittleLong(dprograms->entityfields);
2042 prog->numstatements = prog->progs_numstatements;
2043 prog->numglobaldefs = prog->progs_numglobaldefs;
2044 prog->numfielddefs = prog->progs_numfielddefs;
2045 prog->numfunctions = prog->progs_numfunctions;
2046 prog->numstrings = prog->progs_numstrings;
2047 prog->numglobals = prog->progs_numglobals;
2048 prog->entityfields = prog->progs_entityfields;
2050 if (LittleLong(dprograms->ofs_strings) + prog->progs_numstrings > (int)filesize)
2051 prog->error_cmd("%s: %s strings go past end of file", prog->name, filename);
2052 prog->strings = (char *)Mem_Alloc(prog->progs_mempool, prog->progs_numstrings);
2053 memcpy(prog->strings, instrings, prog->progs_numstrings);
2054 prog->stringssize = prog->progs_numstrings;
2056 prog->numknownstrings = 0;
2057 prog->maxknownstrings = 0;
2058 prog->knownstrings = NULL;
2059 prog->knownstrings_flags = NULL;
2061 Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
2063 // we need to expand the globaldefs and fielddefs to include engine defs
2064 prog->globaldefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobaldefs + numrequiredglobals) * sizeof(ddef_t));
2065 prog->globals.fp = (prvm_vec_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobals + requiredglobalspace + 2) * sizeof(prvm_vec_t));
2066 // + 2 is because of an otherwise occurring overrun in RETURN instruction
2067 // when trying to return the last or second-last global
2068 // (RETURN always returns a vector, there is no RETURN_F instruction)
2069 prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numfielddefs + numrequiredfields) * sizeof(ddef_t));
2070 // we need to convert the statements to our memory format
2071 prog->statements = (mstatement_t *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(mstatement_t));
2072 // allocate space for profiling statement usage
2073 prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(*prog->statement_profile));
2074 prog->explicit_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(*prog->statement_profile));
2075 // functions need to be converted to the memory format
2076 prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs_numfunctions);
2078 for (i = 0;i < prog->progs_numfunctions;i++)
2080 prog->functions[i].first_statement = LittleLong(infunctions[i].first_statement);
2081 prog->functions[i].parm_start = LittleLong(infunctions[i].parm_start);
2082 prog->functions[i].s_name = LittleLong(infunctions[i].s_name);
2083 prog->functions[i].s_file = LittleLong(infunctions[i].s_file);
2084 prog->functions[i].numparms = LittleLong(infunctions[i].numparms);
2085 prog->functions[i].locals = LittleLong(infunctions[i].locals);
2086 memcpy(prog->functions[i].parm_size, infunctions[i].parm_size, sizeof(infunctions[i].parm_size));
2087 if(prog->functions[i].first_statement >= prog->numstatements)
2088 prog->error_cmd("PRVM_LoadProgs: out of bounds function statement (function %d) in %s", i, prog->name);
2089 // TODO bounds check parm_start, s_name, s_file, numparms, locals, parm_size
2092 // copy the globaldefs to the new globaldefs list
2093 for (i=0 ; i<prog->numglobaldefs ; i++)
2095 prog->globaldefs[i].type = LittleShort(inglobaldefs[i].type);
2096 prog->globaldefs[i].ofs = LittleShort(inglobaldefs[i].ofs);
2097 prog->globaldefs[i].s_name = LittleLong(inglobaldefs[i].s_name);
2098 // TODO bounds check ofs, s_name
2101 // append the required globals
2102 for (i = 0;i < numrequiredglobals;i++)
2104 prog->globaldefs[prog->numglobaldefs].type = required_global[i].type;
2105 prog->globaldefs[prog->numglobaldefs].ofs = prog->numglobals;
2106 prog->globaldefs[prog->numglobaldefs].s_name = PRVM_SetEngineString(prog, required_global[i].name);
2107 if (prog->globaldefs[prog->numglobaldefs].type == ev_vector)
2108 prog->numglobals += 3;
2111 prog->numglobaldefs++;
2114 // copy the progs fields to the new fields list
2115 for (i = 0;i < prog->numfielddefs;i++)
2117 prog->fielddefs[i].type = LittleShort(infielddefs[i].type);
2118 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
2119 prog->error_cmd("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", prog->name);
2120 prog->fielddefs[i].ofs = LittleShort(infielddefs[i].ofs);
2121 prog->fielddefs[i].s_name = LittleLong(infielddefs[i].s_name);
2122 // TODO bounds check ofs, s_name
2125 // append the required fields
2126 for (i = 0;i < numrequiredfields;i++)
2128 prog->fielddefs[prog->numfielddefs].type = required_field[i].type;
2129 prog->fielddefs[prog->numfielddefs].ofs = prog->entityfields;
2130 prog->fielddefs[prog->numfielddefs].s_name = PRVM_SetEngineString(prog, required_field[i].name);
2131 if (prog->fielddefs[prog->numfielddefs].type == ev_vector)
2132 prog->entityfields += 3;
2134 prog->entityfields++;
2135 prog->numfielddefs++;
2138 // LadyHavoc: TODO: reorder globals to match engine struct
2139 // LadyHavoc: TODO: reorder fields to match engine struct
2140 #define remapglobal(index) (index)
2141 #define remapfield(index) (index)
2144 // FIXME: LadyHavoc: this uses a crude way to identify integer constants, rather than checking for matching globaldefs and checking their type
2145 for (i = 0;i < prog->progs_numglobals;i++)
2147 u.i = LittleLong(inglobals[i]);
2148 // most globals are 0, we only need to deal with the ones that are not
2151 d = u.i & 0xFF800000;
2152 if ((d == 0xFF800000) || (d == 0))
2154 // Looks like an integer (expand to int64)
2155 prog->globals.ip[remapglobal(i)] = u.i;
2159 // Looks like a float (expand to double)
2160 prog->globals.fp[remapglobal(i)] = u.f;
2165 // LadyHavoc: TODO: support 32bit progs statement formats
2166 // copy, remap globals in statements, bounds check
2167 for (i = 0;i < prog->progs_numstatements;i++)
2169 op = (opcode_t)LittleShort(instatements[i].op);
2170 a = (unsigned short)LittleShort(instatements[i].a);
2171 b = (unsigned short)LittleShort(instatements[i].b);
2172 c = (unsigned short)LittleShort(instatements[i].c);
2178 if (a >= prog->progs_numglobals || b + i < 0 || b + i >= prog->progs_numstatements)
2179 prog->error_cmd("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", i, prog->name);
2180 prog->statements[i].op = op;
2181 prog->statements[i].operand[0] = remapglobal(a);
2182 prog->statements[i].operand[1] = -1;
2183 prog->statements[i].operand[2] = -1;
2184 prog->statements[i].jumpabsolute = i + b;
2188 if (a + i < 0 || a + i >= prog->progs_numstatements)
2189 prog->error_cmd("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, prog->name);
2190 prog->statements[i].op = op;
2191 prog->statements[i].operand[0] = -1;
2192 prog->statements[i].operand[1] = -1;
2193 prog->statements[i].operand[2] = -1;
2194 prog->statements[i].jumpabsolute = i + a;
2197 Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", (int)op, i, prog->name);
2199 // global global global
2234 if (a >= prog->progs_numglobals || b >= prog->progs_numglobals || c >= prog->progs_numglobals)
2235 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
2236 prog->statements[i].op = op;
2237 prog->statements[i].operand[0] = remapglobal(a);
2238 prog->statements[i].operand[1] = remapglobal(b);
2239 prog->statements[i].operand[2] = remapglobal(c);
2240 prog->statements[i].jumpabsolute = -1;
2242 // global none global
2248 if (a >= prog->progs_numglobals || c >= prog->progs_numglobals)
2249 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2250 prog->statements[i].op = op;
2251 prog->statements[i].operand[0] = remapglobal(a);
2252 prog->statements[i].operand[1] = -1;
2253 prog->statements[i].operand[2] = remapglobal(c);
2254 prog->statements[i].jumpabsolute = -1;
2270 if (a >= prog->progs_numglobals || b >= prog->progs_numglobals)
2271 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2272 prog->statements[i].op = op;
2273 prog->statements[i].operand[0] = remapglobal(a);
2274 prog->statements[i].operand[1] = remapglobal(b);
2275 prog->statements[i].operand[2] = -1;
2276 prog->statements[i].jumpabsolute = -1;
2280 if ( a < prog->progs_numglobals)
2281 if ( prog->globals.ip[remapglobal(a)] >= 0 )
2282 if ( prog->globals.ip[remapglobal(a)] < prog->progs_numfunctions )
2283 if ( prog->functions[prog->globals.ip[remapglobal(a)]].first_statement == -642 )
2284 ++prog->numexplicitcoveragestatements;
2295 if ( a >= prog->progs_numglobals)
2296 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2297 prog->statements[i].op = op;
2298 prog->statements[i].operand[0] = remapglobal(a);
2299 prog->statements[i].operand[1] = -1;
2300 prog->statements[i].operand[2] = -1;
2301 prog->statements[i].jumpabsolute = -1;
2305 if(prog->numstatements < 1)
2307 prog->error_cmd("PRVM_LoadProgs: empty program in %s", prog->name);
2309 else switch(prog->statements[prog->numstatements - 1].op)
2316 prog->error_cmd("PRVM_LoadProgs: program may fall off the edge (does not end with RETURN, GOTO or DONE) in %s", prog->name);
2320 // we're done with the file now
2322 Mem_Free(dprograms);
2325 // check required functions
2326 for(i=0 ; i < numrequiredfunc ; i++)
2327 if(PRVM_ED_FindFunction(prog, required_func[i]) == 0)
2328 prog->error_cmd("%s: %s not found in %s",prog->name, required_func[i], filename);
2330 PRVM_LoadLNO(prog, filename);
2332 PRVM_Init_Exec(prog);
2334 if(*prvm_language.string)
2335 // in CSQC we really shouldn't be able to change how stuff works... sorry for now
2336 // later idea: include a list of authorized .po file checksums with the csprogs
2338 qboolean deftrans = prog == CLVM_prog;
2339 const char *realfilename = (prog != CLVM_prog ? filename : csqc_progname.string);
2340 if(deftrans) // once we have dotranslate_ strings, ALWAYS use the opt-in method!
2342 for (i=0 ; i<prog->numglobaldefs ; i++)
2345 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2346 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2347 if(name && !strncmp(name, "dotranslate_", 12))
2354 if(!strcmp(prvm_language.string, "dump"))
2356 qfile_t *f = FS_OpenRealFile(va(vabuf, sizeof(vabuf), "%s.pot", realfilename), "w", false);
2357 Con_Printf("Dumping to %s.pot\n", realfilename);
2360 for (i=0 ; i<prog->numglobaldefs ; i++)
2363 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2364 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2365 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2367 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2368 const char *value = PRVM_GetString(prog, val->string);
2371 char buf[MAX_INPUTLINE];
2372 PRVM_PO_UnparseString(buf, value, sizeof(buf));
2373 FS_Printf(f, "msgid \"%s\"\nmsgstr \"\"\n\n", buf);
2382 po_t *po = PRVM_PO_Load(
2383 va(vabuf, sizeof(vabuf), "%s.%s.po", realfilename, prvm_language.string),
2384 va(vabuf2, sizeof(vabuf2), "common.%s.po", prvm_language.string),
2385 prog->progs_mempool);
2388 for (i=0 ; i<prog->numglobaldefs ; i++)
2391 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2392 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2393 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2395 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2396 const char *value = PRVM_GetString(prog, val->string);
2399 value = PRVM_PO_Lookup(po, value);
2401 val->string = PRVM_SetEngineString(prog, value);
2409 for (cvar = prog->console_cmd->cvars->vars; cvar; cvar = cvar->next)
2410 cvar->globaldefindex[prog - prvm_prog_list] = -1;
2412 for (i=0 ; i<prog->numglobaldefs ; i++)
2415 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2416 //Con_Printf("found var %s\n", name);
2418 && !strncmp(name, "autocvar_", 9)
2419 && !(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
2422 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2423 cvar = Cvar_FindVar(prog->console_cmd->cvars, name + 9, prog->console_cmd->cvars_flagsmask);
2424 //Con_Printf("PRVM_LoadProgs: autocvar global %s in %s, processing...\n", name, prog->name);
2431 Con_DPrintf("PRVM_LoadProgs: no cvar for autocvar global %s in %s, creating...\n", name, prog->name);
2432 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2435 if((float)((int)(val->_float)) == val->_float)
2436 dpsnprintf(buf, sizeof(buf), "%i", (int)(val->_float));
2441 for (int precision = 7; precision <= 9; ++precision) {
2442 dpsnprintf(buf, sizeof(buf), "%.*g", precision, f);
2443 if ((float)atof(buf) == f) {
2451 for (i = 0; i < 3; ++i)
2455 for (int precision = 7; precision <= 9; ++precision) {
2456 dpsnprintf(buf, sizeof(buf), "%.*g", precision, f);
2457 if ((float)atof(buf) == f) {
2458 prec[i] = precision;
2463 dpsnprintf(buf, sizeof(buf), "%.*g %.*g %.*g", prec[0], val->vector[0], prec[1], val->vector[1], prec[2], val->vector[2]);
2467 value = PRVM_GetString(prog, val->string);
2470 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2473 cvar = Cvar_Get(prog->console_cmd->cvars, name + 9, value, prog->console_cmd->cvars_flagsmask, NULL);
2474 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2476 val->string = PRVM_SetEngineString(prog, cvar->string);
2477 cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2480 prog->error_cmd("PRVM_LoadProgs: could not create cvar for autocvar global %s in %s", name, prog->name);
2481 cvar->globaldefindex[prog - prvm_prog_list] = i;
2483 else if((cvar->flags & CVAR_PRIVATE) == 0)
2485 // MUST BE SYNCED WITH cvar.c Cvar_Set
2488 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2491 val->_float = cvar->value;
2495 VectorClear(val->vector);
2496 for (j = 0;j < 3;j++)
2498 while (*s && ISWHITESPACE(*s))
2502 val->vector[j] = atof(s);
2503 while (!ISWHITESPACE(*s))
2510 val->string = PRVM_SetEngineString(prog, cvar->string);
2511 cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2514 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2517 cvar->globaldefindex[prog - prvm_prog_list] = i;
2520 Con_Printf("PRVM_LoadProgs: private cvar for autocvar global %s in %s\n", name, prog->name);
2526 prog->loaded = true;
2528 PRVM_UpdateBreakpoints(prog);
2530 // set flags & ddef_ts in prog
2534 PRVM_FindOffsets(prog);
2536 prog->init_cmd(prog);
2539 PRVM_MEM_Alloc(prog);
2541 // Inittime is at least the time when this function finished. However,
2542 // later events may bump it.
2543 prog->inittime = host.realtime;
2547 static void PRVM_Fields_f(cmd_state_t *cmd)
2550 int i, j, ednum, used, usedamount;
2552 char tempstring[MAX_INPUTLINE], tempstring2[260];
2562 Con_Print("no progs loaded\n");
2567 if(Cmd_Argc(cmd) != 2)
2569 Con_Print("prvm_fields <program name>\n");
2573 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2576 counts = (int *)Mem_Alloc(tempmempool, prog->numfielddefs * sizeof(int));
2577 for (ednum = 0;ednum < prog->max_edicts;ednum++)
2579 ed = PRVM_EDICT_NUM(ednum);
2580 if (ed->priv.required->free)
2582 for (i = 1;i < prog->numfielddefs;i++)
2584 d = &prog->fielddefs[i];
2585 name = PRVM_GetString(prog, d->s_name);
2586 if (name[strlen(name)-2] == '_')
2587 continue; // skip _x, _y, _z vars
2588 val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
2589 // if the value is still all 0, skip the field
2590 for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
2592 if (val->ivector[j])
2603 for (i = 0;i < prog->numfielddefs;i++)
2605 d = &prog->fielddefs[i];
2606 name = PRVM_GetString(prog, d->s_name);
2607 if (name[strlen(name)-2] == '_')
2608 continue; // skip _x, _y, _z vars
2609 switch(d->type & ~DEF_SAVEGLOBAL)
2612 strlcat(tempstring, "string ", sizeof(tempstring));
2615 strlcat(tempstring, "entity ", sizeof(tempstring));
2618 strlcat(tempstring, "function ", sizeof(tempstring));
2621 strlcat(tempstring, "field ", sizeof(tempstring));
2624 strlcat(tempstring, "void ", sizeof(tempstring));
2627 strlcat(tempstring, "float ", sizeof(tempstring));
2630 strlcat(tempstring, "vector ", sizeof(tempstring));
2633 strlcat(tempstring, "pointer ", sizeof(tempstring));
2636 dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
2637 strlcat(tempstring, tempstring2, sizeof(tempstring));
2640 if (strlen(name) > sizeof(tempstring2)-4)
2642 memcpy (tempstring2, name, sizeof(tempstring2)-4);
2643 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
2644 tempstring2[sizeof(tempstring2)-1] = 0;
2647 strlcat(tempstring, name, sizeof(tempstring));
2648 for (j = (int)strlen(name);j < 25;j++)
2649 strlcat(tempstring, " ", sizeof(tempstring));
2650 dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
2651 strlcat(tempstring, tempstring2, sizeof(tempstring));
2652 strlcat(tempstring, "\n", sizeof(tempstring));
2653 if (strlen(tempstring) >= sizeof(tempstring)/2)
2655 Con_Print(tempstring);
2661 usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
2665 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);
2668 static void PRVM_Globals_f(cmd_state_t *cmd)
2672 const char *wildcard;
2678 Con_Print("no progs loaded\n");
2681 if(Cmd_Argc (cmd) < 2 || Cmd_Argc(cmd) > 3)
2683 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
2687 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2690 if( Cmd_Argc(cmd) == 3)
2691 wildcard = Cmd_Argv(cmd, 2);
2695 Con_Printf("%s :", prog->name);
2697 for (i = 0;i < prog->numglobaldefs;i++)
2700 if( !matchpattern( PRVM_GetString(prog, prog->globaldefs[i].s_name), wildcard, 1) )
2705 Con_Printf("%s\n", PRVM_GetString(prog, prog->globaldefs[i].s_name));
2707 Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->numglobals, numculled, prog->numglobals * 4);
2715 static void PRVM_Global_f(cmd_state_t *cmd)
2719 char valuebuf[MAX_INPUTLINE];
2720 if( Cmd_Argc(cmd) != 3 ) {
2721 Con_Printf( "prvm_global <program name> <global name>\n" );
2725 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2728 global = PRVM_ED_FindGlobal( prog, Cmd_Argv(cmd, 2) );
2730 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(cmd, 2), Cmd_Argv(cmd, 1) );
2732 Con_Printf( "%s: %s\n", Cmd_Argv(cmd, 2), PRVM_ValueString( prog, (etype_t)global->type, PRVM_GLOBALFIELDVALUE(global->ofs), valuebuf, sizeof(valuebuf) ) );
2740 static void PRVM_GlobalSet_f(cmd_state_t *cmd)
2744 if( Cmd_Argc(cmd) != 4 ) {
2745 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2749 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2752 global = PRVM_ED_FindGlobal( prog, Cmd_Argv(cmd, 2) );
2754 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(cmd, 2), Cmd_Argv(cmd, 1) );
2756 PRVM_ED_ParseEpair( prog, NULL, global, Cmd_Argv(cmd, 3), true );
2760 ======================
2761 Break- and Watchpoints
2762 ======================
2766 char break_statement[256];
2767 char watch_global[256];
2769 char watch_field[256];
2772 static debug_data_t debug_data[PRVM_PROG_MAX];
2774 void PRVM_Breakpoint(prvm_prog_t *prog, int stack_index, const char *text)
2777 Con_Printf("PRVM_Breakpoint: %s\n", text);
2778 PRVM_PrintState(prog, stack_index);
2779 if (prvm_breakpointdump.integer)
2780 SV_Savegame_to(prog, va(vabuf, sizeof(vabuf), "breakpoint-%s.dmp", prog->name));
2783 void PRVM_Watchpoint(prvm_prog_t *prog, int stack_index, const char *text, etype_t type, prvm_eval_t *o, prvm_eval_t *n)
2785 size_t sz = sizeof(prvm_vec_t) * ((type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2786 if (memcmp(o, n, sz))
2789 char valuebuf_o[128];
2790 char valuebuf_n[128];
2791 PRVM_UglyValueString(prog, type, o, valuebuf_o, sizeof(valuebuf_o));
2792 PRVM_UglyValueString(prog, type, n, valuebuf_n, sizeof(valuebuf_n));
2793 dpsnprintf(buf, sizeof(buf), "%s: %s -> %s", text, valuebuf_o, valuebuf_n);
2794 PRVM_Breakpoint(prog, stack_index, buf);
2799 static void PRVM_UpdateBreakpoints(prvm_prog_t *prog)
2801 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2804 if (debug->break_statement[0])
2806 if (debug->break_statement[0] >= '0' && debug->break_statement[0] <= '9')
2808 prog->break_statement = atoi(debug->break_statement);
2809 prog->break_stack_index = 0;
2814 func = PRVM_ED_FindFunction (prog, debug->break_statement);
2817 Con_Printf("%s progs: no function or statement named %s to break on!\n", prog->name, debug->break_statement);
2818 prog->break_statement = -1;
2822 prog->break_statement = func->first_statement;
2823 prog->break_stack_index = 1;
2826 if (prog->break_statement >= -1)
2827 Con_Printf("%s progs: breakpoint is at statement %d\n", prog->name, prog->break_statement);
2830 prog->break_statement = -1;
2832 if (debug->watch_global[0])
2834 ddef_t *global = PRVM_ED_FindGlobal( prog, debug->watch_global );
2837 Con_Printf( "%s progs: no global named '%s' to watch!\n", prog->name, debug->watch_global );
2838 prog->watch_global_type = ev_void;
2842 size_t sz = sizeof(prvm_vec_t) * ((global->type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2843 prog->watch_global = global->ofs;
2844 prog->watch_global_type = (etype_t)global->type;
2845 memcpy(&prog->watch_global_value, PRVM_GLOBALFIELDVALUE(prog->watch_global), sz);
2847 if (prog->watch_global_type != ev_void)
2848 Con_Printf("%s progs: global watchpoint is at global index %d\n", prog->name, prog->watch_global);
2851 prog->watch_global_type = ev_void;
2853 if (debug->watch_field[0])
2855 ddef_t *field = PRVM_ED_FindField( prog, debug->watch_field );
2858 Con_Printf( "%s progs: no field named '%s' to watch!\n", prog->name, debug->watch_field );
2859 prog->watch_field_type = ev_void;
2863 size_t sz = sizeof(prvm_vec_t) * ((field->type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2864 prog->watch_edict = debug->watch_edict;
2865 prog->watch_field = field->ofs;
2866 prog->watch_field_type = (etype_t)field->type;
2867 if (prog->watch_edict < prog->num_edicts)
2868 memcpy(&prog->watch_edictfield_value, PRVM_EDICTFIELDVALUE(PRVM_EDICT_NUM(prog->watch_edict), prog->watch_field), sz);
2870 memset(&prog->watch_edictfield_value, 0, sz);
2872 if (prog->watch_edict != ev_void)
2873 Con_Printf("%s progs: edict field watchpoint is at edict %d field index %d\n", prog->name, prog->watch_edict, prog->watch_field);
2876 prog->watch_field_type = ev_void;
2879 static void PRVM_Breakpoint_f(cmd_state_t *cmd)
2883 if( Cmd_Argc(cmd) == 2 ) {
2884 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2887 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2888 debug->break_statement[0] = 0;
2890 PRVM_UpdateBreakpoints(prog);
2893 if( Cmd_Argc(cmd) != 3 ) {
2894 Con_Printf( "prvm_breakpoint <program name> <function name | statement>\n" );
2898 if (!(prog = PRVM_ProgFromString(Cmd_Argv(cmd, 1))))
2902 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2903 strlcpy(debug->break_statement, Cmd_Argv(cmd, 2), sizeof(debug->break_statement));
2905 PRVM_UpdateBreakpoints(prog);
2908 static void PRVM_GlobalWatchpoint_f(cmd_state_t *cmd)
2912 if( Cmd_Argc(cmd) == 2 ) {
2913 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2916 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2917 debug->watch_global[0] = 0;
2919 PRVM_UpdateBreakpoints(prog);
2922 if( Cmd_Argc(cmd) != 3 ) {
2923 Con_Printf( "prvm_globalwatchpoint <program name> <global name>\n" );
2927 if (!(prog = PRVM_ProgFromString(Cmd_Argv(cmd, 1))))
2931 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2932 strlcpy(debug->watch_global, Cmd_Argv(cmd, 2), sizeof(debug->watch_global));
2934 PRVM_UpdateBreakpoints(prog);
2937 static void PRVM_EdictWatchpoint_f(cmd_state_t *cmd)
2941 if( Cmd_Argc(cmd) == 2 ) {
2942 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2945 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2946 debug->watch_field[0] = 0;
2948 PRVM_UpdateBreakpoints(prog);
2951 if( Cmd_Argc(cmd) != 4 ) {
2952 Con_Printf( "prvm_edictwatchpoint <program name> <edict number> <field name>\n" );
2956 if (!(prog = PRVM_ProgFromString(Cmd_Argv(cmd, 1))))
2960 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2961 debug->watch_edict = atoi(Cmd_Argv(cmd, 2));
2962 strlcpy(debug->watch_field, Cmd_Argv(cmd, 3), sizeof(debug->watch_field));
2964 PRVM_UpdateBreakpoints(prog);
2972 void PRVM_Init (void)
2974 Cmd_AddCommand(CMD_SHARED, "prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2975 Cmd_AddCommand(CMD_SHARED, "prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2976 Cmd_AddCommand(CMD_SHARED, "prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2977 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)");
2978 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");
2979 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)");
2980 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)");
2981 Cmd_AddCommand(CMD_SHARED, "prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2982 Cmd_AddCommand(CMD_SHARED, "prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2983 Cmd_AddCommand(CMD_SHARED, "prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2984 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)");
2985 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");
2986 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");
2987 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)");
2988 Cmd_AddCommand(CMD_SHARED, "cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2989 Cmd_AddCommand(CMD_SHARED, "menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2990 Cmd_AddCommand(CMD_SHARED, "sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2991 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");
2992 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");
2993 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");
2995 Cvar_RegisterVariable (&prvm_language);
2996 Cvar_RegisterVariable (&prvm_traceqc);
2997 Cvar_RegisterVariable (&prvm_statementprofiling);
2998 Cvar_RegisterVariable (&prvm_timeprofiling);
2999 Cvar_RegisterVariable (&prvm_coverage);
3000 Cvar_RegisterVariable (&prvm_backtraceforwarnings);
3001 Cvar_RegisterVariable (&prvm_leaktest);
3002 Cvar_RegisterVariable (&prvm_leaktest_follow_targetname);
3003 Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames);
3004 Cvar_RegisterVariable (&prvm_errordump);
3005 Cvar_RegisterVariable (&prvm_breakpointdump);
3006 Cvar_RegisterVariable (&prvm_reuseedicts_startuptime);
3007 Cvar_RegisterVariable (&prvm_reuseedicts_neverinsameframe);
3008 Cvar_RegisterVariable (&prvm_garbagecollection_enable);
3009 Cvar_RegisterVariable (&prvm_garbagecollection_notify);
3010 Cvar_RegisterVariable (&prvm_garbagecollection_scan_limit);
3011 Cvar_RegisterVariable (&prvm_garbagecollection_strings);
3012 Cvar_RegisterVariable (&prvm_stringdebug);
3014 // COMMANDLINEOPTION: PRVM: -norunaway disables the runaway loop check (it might be impossible to exit DarkPlaces if used!)
3015 prvm_runawaycheck = !COM_CheckParm("-norunaway");
3025 void PRVM_Prog_Init(prvm_prog_t *prog, cmd_state_t *cmd)
3027 PRVM_Prog_Reset(prog);
3028 prog->leaktest_active = prvm_leaktest.integer != 0;
3029 prog->console_cmd = cmd;
3032 // LadyHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
3033 unsigned int PRVM_EDICT_NUM_ERROR(prvm_prog_t *prog, unsigned int n, const char *filename, int fileline)
3035 prog->error_cmd("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", prog->name, n, filename, fileline);
3039 #define PRVM_KNOWNSTRINGBASE 0x40000000
3041 const char *PRVM_GetString(prvm_prog_t *prog, int num)
3046 if (prvm_stringdebug.integer)
3047 VM_Warning(prog, "PRVM_GetString: Invalid string offset (%i < 0)\n", num);
3050 else if (num < prog->stringssize)
3052 // constant string from progs.dat
3053 return prog->strings + num;
3055 else if (num <= prog->stringssize + prog->tempstringsbuf.maxsize)
3057 // tempstring returned by engine to QC (becomes invalid after returning to engine)
3058 num -= prog->stringssize;
3059 if (num < prog->tempstringsbuf.cursize)
3060 return (char *)prog->tempstringsbuf.data + num;
3063 if (prvm_stringdebug.integer)
3064 VM_Warning(prog, "PRVM_GetString: Invalid temp-string offset (%i >= %i prog->tempstringsbuf.cursize)\n", num, prog->tempstringsbuf.cursize);
3068 else if (num & PRVM_KNOWNSTRINGBASE)
3071 num = num - PRVM_KNOWNSTRINGBASE;
3072 if (num >= 0 && num < prog->numknownstrings)
3074 if (!prog->knownstrings[num])
3076 if (prvm_stringdebug.integer)
3077 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
3080 // refresh the garbage collection on the string - this guards
3081 // against a certain sort of repeated migration to earlier
3082 // points in the scan that could otherwise result in the string
3083 // being freed for being unused
3084 prog->knownstrings_flags[num] = (prog->knownstrings_flags[num] & ~KNOWNSTRINGFLAG_GCPRUNE) | KNOWNSTRINGFLAG_GCMARK;
3085 return prog->knownstrings[num];
3089 if (prvm_stringdebug.integer)
3090 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
3096 // invalid string offset
3097 if (prvm_stringdebug.integer)
3098 VM_Warning(prog, "PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
3103 const char *PRVM_ChangeEngineString(prvm_prog_t *prog, int i, const char *s)
3106 i = i - PRVM_KNOWNSTRINGBASE;
3107 if (i < 0 || i >= prog->numknownstrings)
3108 prog->error_cmd("PRVM_ChangeEngineString: string index %i is out of bounds", i);
3109 else if ((prog->knownstrings_flags[i] & KNOWNSTRINGFLAG_ENGINE) == 0)
3110 prog->error_cmd("PRVM_ChangeEngineString: string index %i is not an engine string", i);
3111 old = prog->knownstrings[i];
3112 prog->knownstrings[i] = s;
3116 static void PRVM_NewKnownString(prvm_prog_t *prog, int i, int flags, const char *s)
3118 if (i >= prog->numknownstrings)
3120 if (i >= prog->maxknownstrings)
3122 const char **oldstrings = prog->knownstrings;
3123 const unsigned char *oldstrings_flags = prog->knownstrings_flags;
3124 const char **oldstrings_origin = prog->knownstrings_origin;
3125 prog->maxknownstrings += 128;
3126 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3127 prog->knownstrings_flags = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
3128 if (prog->leaktest_active)
3129 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3130 if (prog->numknownstrings)
3132 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
3133 memcpy((char **)prog->knownstrings_flags, oldstrings_flags, prog->numknownstrings * sizeof(unsigned char));
3134 if (prog->leaktest_active)
3135 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
3138 prog->numknownstrings++;
3140 prog->firstfreeknownstring = i + 1;
3141 prog->knownstrings[i] = s;
3142 // it's in use right now, spare it until the next gc pass - that said, it is not freeable so this is probably moot
3143 prog->knownstrings_flags[i] = flags;
3144 if (prog->leaktest_active)
3145 prog->knownstrings_origin[i] = NULL;
3148 int PRVM_SetEngineString(prvm_prog_t *prog, const char *s)
3153 if (s >= prog->strings && s <= prog->strings + prog->stringssize)
3154 prog->error_cmd("PRVM_SetEngineString: s in prog->strings area");
3155 // if it's in the tempstrings area, use a reserved range
3156 // (otherwise we'd get millions of useless string offsets cluttering the database)
3157 if (s >= (char *)prog->tempstringsbuf.data && s < (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.maxsize)
3158 return prog->stringssize + (s - (char *)prog->tempstringsbuf.data);
3159 // see if it's a known string address
3160 for (i = 0;i < prog->numknownstrings;i++)
3161 if (prog->knownstrings[i] == s)
3162 return PRVM_KNOWNSTRINGBASE + i;
3163 // new unknown engine string
3164 if (developer_insane.integer)
3165 Con_DPrintf("new engine string %p = \"%s\"\n", (void *)s, s);
3166 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3167 if (!prog->knownstrings[i])
3169 PRVM_NewKnownString(prog, i, KNOWNSTRINGFLAG_GCMARK | KNOWNSTRINGFLAG_ENGINE, s);
3170 return PRVM_KNOWNSTRINGBASE + i;
3173 // temp string handling
3175 // all tempstrings go into this buffer consecutively, and it is reset
3176 // whenever PRVM_ExecuteProgram returns to the engine
3177 // (technically each PRVM_ExecuteProgram call saves the cursize value and
3178 // restores it on return, so multiple recursive calls can share the same
3180 // the buffer size is automatically grown as needed
3182 int PRVM_SetTempString(prvm_prog_t *prog, const char *s)
3188 size = (int)strlen(s) + 1;
3189 if (developer_insane.integer)
3190 Con_DPrintf("PRVM_SetTempString: cursize %i, size %i\n", prog->tempstringsbuf.cursize, size);
3191 if (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
3193 sizebuf_t old = prog->tempstringsbuf;
3194 if (prog->tempstringsbuf.cursize + size >= 1<<28)
3195 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);
3196 prog->tempstringsbuf.maxsize = max(prog->tempstringsbuf.maxsize, 65536);
3197 while (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
3198 prog->tempstringsbuf.maxsize *= 2;
3199 if (prog->tempstringsbuf.maxsize != old.maxsize || prog->tempstringsbuf.data == NULL)
3201 Con_DPrintf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, prog->tempstringsbuf.maxsize/1024);
3202 prog->tempstringsbuf.data = (unsigned char *) Mem_Alloc(prog->progs_mempool, prog->tempstringsbuf.maxsize);
3206 memcpy(prog->tempstringsbuf.data, old.data, old.cursize);
3211 t = (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.cursize;
3213 prog->tempstringsbuf.cursize += size;
3214 return PRVM_SetEngineString(prog, t);
3217 int PRVM_AllocString(prvm_prog_t *prog, size_t bufferlength, char **pointer)
3227 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3228 if (!prog->knownstrings[i])
3230 s = (char *)PRVM_Alloc(bufferlength);
3231 PRVM_NewKnownString(prog, i, KNOWNSTRINGFLAG_GCMARK, s);
3232 if(prog->leaktest_active)
3233 prog->knownstrings_origin[i] = PRVM_AllocationOrigin(prog);
3235 *pointer = (char *)(prog->knownstrings[i]);
3236 return PRVM_KNOWNSTRINGBASE + i;
3239 void PRVM_FreeString(prvm_prog_t *prog, int num)
3242 prog->error_cmd("PRVM_FreeString: attempt to free a NULL string");
3243 else if (num >= 0 && num < prog->stringssize)
3244 prog->error_cmd("PRVM_FreeString: attempt to free a constant string");
3245 else if (num >= PRVM_KNOWNSTRINGBASE && num < PRVM_KNOWNSTRINGBASE + prog->numknownstrings)
3247 num = num - PRVM_KNOWNSTRINGBASE;
3248 if (!prog->knownstrings[num])
3249 prog->error_cmd("PRVM_FreeString: attempt to free a non-existent or already freed string");
3250 if (!prog->knownstrings_flags[num])
3251 prog->error_cmd("PRVM_FreeString: attempt to free a string owned by the engine");
3252 PRVM_Free((char *)prog->knownstrings[num]);
3253 if(prog->leaktest_active)
3254 if(prog->knownstrings_origin[num])
3255 PRVM_Free((char *)prog->knownstrings_origin[num]);
3256 prog->knownstrings[num] = NULL;
3257 prog->knownstrings_flags[num] = 0;
3258 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
3261 prog->error_cmd("PRVM_FreeString: invalid string offset %i", num);
3264 static qboolean PRVM_IsStringReferenced(prvm_prog_t *prog, string_t string)
3268 for (i = 0;i < prog->numglobaldefs;i++)
3270 ddef_t *d = &prog->globaldefs[i];
3271 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3273 if(string == PRVM_GLOBALFIELDSTRING(d->ofs))
3277 for(j = 0; j < prog->num_edicts; ++j)
3279 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3280 if (ed->priv.required->free)
3282 for (i=0; i<prog->numfielddefs; ++i)
3284 ddef_t *d = &prog->fielddefs[i];
3285 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3287 if(string == PRVM_EDICTFIELDSTRING(ed, d->ofs))
3295 static qboolean PRVM_IsEdictRelevant(prvm_prog_t *prog, prvm_edict_t *edict)
3299 if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
3300 return true; // world or clients
3301 if (edict->priv.required->freetime <= prog->inittime)
3302 return true; // created during startup
3303 if (prog == SVVM_prog)
3305 if(PRVM_serveredictfloat(edict, solid)) // can block other stuff, or is a trigger?
3307 if(PRVM_serveredictfloat(edict, modelindex)) // visible ent?
3309 if(PRVM_serveredictfloat(edict, effects)) // particle effect?
3311 if(PRVM_serveredictfunction(edict, think)) // has a think function?
3312 if(PRVM_serveredictfloat(edict, nextthink) > 0) // that actually will eventually run?
3314 if(PRVM_serveredictfloat(edict, takedamage))
3316 if(*prvm_leaktest_ignore_classnames.string)
3318 if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_serveredictstring(edict, classname)))))
3322 else if (prog == CLVM_prog)
3324 // TODO someone add more stuff here
3325 if(PRVM_clientedictfloat(edict, entnum)) // csqc networked
3327 if(PRVM_clientedictfloat(edict, modelindex)) // visible ent?
3329 if(PRVM_clientedictfloat(edict, effects)) // particle effect?
3331 if(PRVM_clientedictfunction(edict, think)) // has a think function?
3332 if(PRVM_clientedictfloat(edict, nextthink) > 0) // that actually will eventually run?
3334 if(*prvm_leaktest_ignore_classnames.string)
3336 if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_clientedictstring(edict, classname)))))
3342 // menu prog does not have classnames
3347 static qboolean PRVM_IsEdictReferenced(prvm_prog_t *prog, prvm_edict_t *edict, int mark)
3350 int edictnum = PRVM_NUM_FOR_EDICT(edict);
3351 const char *targetname = NULL;
3353 if (prog == SVVM_prog && prvm_leaktest_follow_targetname.integer)
3354 targetname = PRVM_GetString(prog, PRVM_serveredictstring(edict, targetname));
3357 if(!*targetname) // ""
3360 for(j = 0; j < prog->num_edicts; ++j)
3362 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3363 if (ed->priv.required->mark < mark)
3369 const char *target = PRVM_GetString(prog, PRVM_serveredictstring(ed, target));
3371 if(!strcmp(target, targetname))
3374 for (i=0; i<prog->numfielddefs; ++i)
3376 ddef_t *d = &prog->fielddefs[i];
3377 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3379 if(edictnum == PRVM_EDICTFIELDEDICT(ed, d->ofs))
3387 static void PRVM_MarkReferencedEdicts(prvm_prog_t *prog)
3393 // Stage 1: world, all entities that are relevant, and all entities that are referenced by globals.
3395 for(j = 0; j < prog->num_edicts; ++j)
3397 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3398 if(ed->priv.required->free)
3400 ed->priv.required->mark = PRVM_IsEdictRelevant(prog, ed) ? stage : 0;
3402 for (i = 0;i < prog->numglobaldefs;i++)
3404 ddef_t *d = &prog->globaldefs[i];
3406 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3408 j = PRVM_GLOBALFIELDEDICT(d->ofs);
3409 if (i < 0 || j >= prog->max_edicts) {
3410 Con_Printf("Invalid entity reference from global %s.\n", PRVM_GetString(prog, d->s_name));
3413 ed = PRVM_EDICT_NUM(j);;
3414 ed->priv.required->mark = stage;
3417 // Future stages: all entities that are referenced by an entity of the previous stage.
3421 for(j = 0; j < prog->num_edicts; ++j)
3423 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3424 if(ed->priv.required->free)
3426 if(ed->priv.required->mark)
3428 if(PRVM_IsEdictReferenced(prog, ed, stage))
3430 ed->priv.required->mark = stage + 1;
3437 Con_DPrintf("leak check used %d stages to find all references\n", stage);
3440 void PRVM_LeakTest(prvm_prog_t *prog)
3443 qboolean leaked = false;
3445 if(!prog->leaktest_active)
3449 for (i = 0; i < prog->numknownstrings; ++i)
3451 if(prog->knownstrings[i])
3452 if(prog->knownstrings_flags[i])
3453 if(prog->knownstrings_origin[i])
3454 if(!PRVM_IsStringReferenced(prog, PRVM_KNOWNSTRINGBASE + i))
3456 Con_Printf("Unreferenced string found!\n Value: %s\n Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
3462 PRVM_MarkReferencedEdicts(prog);
3463 for(j = 0; j < prog->num_edicts; ++j)
3465 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3466 if(ed->priv.required->free)
3468 if(!ed->priv.required->mark)
3469 if(ed->priv.required->allocation_origin)
3471 Con_Printf("Unreferenced edict found!\n Allocated at: %s\n", ed->priv.required->allocation_origin);
3472 PRVM_ED_Print(prog, ed, NULL);
3477 ed->priv.required->mark = 0; // clear marks again when done
3480 for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
3482 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
3484 if(stringbuffer->origin)
3486 Con_Printf("Open string buffer handle found!\n Allocated at: %s\n", stringbuffer->origin);
3491 for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
3493 if(prog->openfiles[i])
3494 if(prog->openfiles_origin[i])
3496 Con_Printf("Open file handle found!\n Allocated at: %s\n", prog->openfiles_origin[i]);
3501 for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
3503 if(prog->opensearches[i])
3504 if(prog->opensearches_origin[i])
3506 Con_Printf("Open search handle found!\n Allocated at: %s\n", prog->opensearches_origin[i]);
3512 Con_Printf("Congratulations. No leaks found.\n");
3515 void PRVM_GarbageCollection(prvm_prog_t *prog)
3517 int limit = prvm_garbagecollection_scan_limit.integer;
3518 prvm_prog_garbagecollection_state_t *gc = &prog->gc;
3519 if (!prvm_garbagecollection_enable.integer)
3522 // we like to limit how much scanning we do so it doesn't put a significant
3523 // burden on the cpu, so each of these are not complete scans, we also like
3524 // to have consistent cpu usage so we do a bit of work on each category of
3525 // leaked object every frame
3531 case PRVM_GC_GLOBALS_MARK:
3532 for (; gc->globals_mark_progress < prog->numglobaldefs && (limit--) > 0; gc->globals_mark_progress++)
3534 ddef_t *d = &prog->globaldefs[gc->globals_mark_progress];
3539 prvm_int_t s = prog->globals.ip[d->ofs];
3540 if (s & PRVM_KNOWNSTRINGBASE)
3542 prvm_int_t num = s - PRVM_KNOWNSTRINGBASE;
3543 if (!prog->knownstrings[num])
3546 Con_DPrintf("PRVM_GarbageCollection: Found bogus strzone reference in global %i (global name: \"%s\"), erasing reference", d->ofs, PRVM_GetString(prog, d->s_name));
3547 prog->globals.ip[d->ofs] = 0;
3550 prog->knownstrings_flags[num] = (prog->knownstrings_flags[num] | KNOWNSTRINGFLAG_GCMARK) & ~KNOWNSTRINGFLAG_GCPRUNE;
3558 if (gc->globals_mark_progress >= prog->numglobaldefs)
3561 case PRVM_GC_FIELDS_MARK:
3562 for (; gc->fields_mark_progress < prog->numfielddefs && limit > 0;)
3564 ddef_t *d = &prog->fielddefs[gc->fields_mark_progress];
3568 //for (gc-> entityindex = 0; entityindex < prog->num_edicts; entityindex++)
3569 for (;gc->fields_mark_progress_entity < prog->num_edicts && (limit--) > 0;gc->fields_mark_progress_entity++)
3571 int entityindex = gc->fields_mark_progress_entity;
3572 prvm_int_t s = prog->edictsfields.ip[entityindex * prog->entityfields + d->ofs];
3573 if (s & PRVM_KNOWNSTRINGBASE)
3575 prvm_int_t num = s - PRVM_KNOWNSTRINGBASE;
3576 if (!prog->knownstrings[num])
3579 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));
3580 prog->edictsfields.ip[entityindex * prog->entityfields + d->ofs] = 0;
3583 prog->knownstrings_flags[num] = (prog->knownstrings_flags[num] | KNOWNSTRINGFLAG_GCMARK) & ~KNOWNSTRINGFLAG_GCPRUNE;
3586 if (gc->fields_mark_progress_entity >= prog->num_edicts)
3588 gc->fields_mark_progress_entity = 0;
3589 gc->fields_mark_progress++;
3593 gc->fields_mark_progress_entity = 0;
3594 gc->fields_mark_progress++;
3598 if (gc->fields_mark_progress >= prog->numfielddefs)
3601 case PRVM_GC_KNOWNSTRINGS_SWEEP:
3602 // free any strzone'd strings that are not marked
3603 if (!prvm_garbagecollection_strings.integer)
3608 for (;gc->knownstrings_sweep_progress < prog->numknownstrings && (limit--) > 0;gc->knownstrings_sweep_progress++)
3610 int num = gc->knownstrings_sweep_progress;
3611 if (prog->knownstrings[num] && (prog->knownstrings_flags[num] & (KNOWNSTRINGFLAG_GCMARK | KNOWNSTRINGFLAG_ENGINE)) == 0)
3613 if (prog->knownstrings_flags[num] & KNOWNSTRINGFLAG_GCPRUNE)
3615 // string has been marked for pruning two passes in a row
3616 if (prvm_garbagecollection_notify.integer)
3617 Con_DPrintf("prvm_garbagecollection_notify: %s: freeing unreferenced string %i: \"%s\"\n", prog->name, num, prog->knownstrings[num]);
3618 Mem_Free((char *)prog->knownstrings[num]);
3619 prog->knownstrings[num] = NULL;
3620 prog->knownstrings_flags[num] = 0;
3621 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
3625 // mark it for pruning next pass
3626 prog->knownstrings_flags[num] |= KNOWNSTRINGFLAG_GCPRUNE;
3630 if (gc->knownstrings_sweep_progress >= prog->numknownstrings)
3635 memset(gc, 0, sizeof(*gc));