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 requiredglobalspace = 0;
2016 for (i = 0;i < numrequiredglobals;i++)
2017 requiredglobalspace += required_global[i].type == ev_vector ? 3 : 1;
2019 prog->filecrc = CRC_Block((unsigned char *)dprograms, filesize);
2021 // byte swap the header
2022 prog->progs_version = LittleLong(dprograms->version);
2023 prog->progs_crc = LittleLong(dprograms->crc);
2024 if (prog->progs_version != PROG_VERSION)
2025 prog->error_cmd("%s: %s has wrong version number (%i should be %i)", prog->name, filename, prog->progs_version, PROG_VERSION);
2026 instatements = (dstatement_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_statements));
2027 prog->progs_numstatements = LittleLong(dprograms->numstatements);
2028 inglobaldefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globaldefs));
2029 prog->progs_numglobaldefs = LittleLong(dprograms->numglobaldefs);
2030 infielddefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_fielddefs));
2031 prog->progs_numfielddefs = LittleLong(dprograms->numfielddefs);
2032 infunctions = (dfunction_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_functions));
2033 prog->progs_numfunctions = LittleLong(dprograms->numfunctions);
2034 instrings = (char *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_strings));
2035 prog->progs_numstrings = LittleLong(dprograms->numstrings);
2036 inglobals = (int *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globals));
2037 prog->progs_numglobals = LittleLong(dprograms->numglobals);
2038 prog->progs_entityfields = LittleLong(dprograms->entityfields);
2040 prog->numstatements = prog->progs_numstatements;
2041 prog->numglobaldefs = prog->progs_numglobaldefs;
2042 prog->numfielddefs = prog->progs_numfielddefs;
2043 prog->numfunctions = prog->progs_numfunctions;
2044 prog->numstrings = prog->progs_numstrings;
2045 prog->numglobals = prog->progs_numglobals;
2046 prog->entityfields = prog->progs_entityfields;
2048 if (LittleLong(dprograms->ofs_strings) + prog->progs_numstrings > (int)filesize)
2049 prog->error_cmd("%s: %s strings go past end of file", prog->name, filename);
2050 prog->strings = (char *)Mem_Alloc(prog->progs_mempool, prog->progs_numstrings);
2051 memcpy(prog->strings, instrings, prog->progs_numstrings);
2052 prog->stringssize = prog->progs_numstrings;
2054 prog->numknownstrings = 0;
2055 prog->maxknownstrings = 0;
2056 prog->knownstrings = NULL;
2057 prog->knownstrings_flags = NULL;
2059 Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
2061 // we need to expand the globaldefs and fielddefs to include engine defs
2062 prog->globaldefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobaldefs + numrequiredglobals) * sizeof(ddef_t));
2063 prog->globals.fp = (prvm_vec_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobals + requiredglobalspace + 2) * sizeof(prvm_vec_t));
2064 // + 2 is because of an otherwise occurring overrun in RETURN instruction
2065 // when trying to return the last or second-last global
2066 // (RETURN always returns a vector, there is no RETURN_F instruction)
2067 prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numfielddefs + numrequiredfields) * sizeof(ddef_t));
2068 // we need to convert the statements to our memory format
2069 prog->statements = (mstatement_t *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(mstatement_t));
2070 // allocate space for profiling statement usage
2071 prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(*prog->statement_profile));
2072 prog->explicit_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(*prog->statement_profile));
2073 // functions need to be converted to the memory format
2074 prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs_numfunctions);
2076 for (i = 0;i < prog->progs_numfunctions;i++)
2078 prog->functions[i].first_statement = LittleLong(infunctions[i].first_statement);
2079 prog->functions[i].parm_start = LittleLong(infunctions[i].parm_start);
2080 prog->functions[i].s_name = LittleLong(infunctions[i].s_name);
2081 prog->functions[i].s_file = LittleLong(infunctions[i].s_file);
2082 prog->functions[i].numparms = LittleLong(infunctions[i].numparms);
2083 prog->functions[i].locals = LittleLong(infunctions[i].locals);
2084 memcpy(prog->functions[i].parm_size, infunctions[i].parm_size, sizeof(infunctions[i].parm_size));
2085 if(prog->functions[i].first_statement >= prog->numstatements)
2086 prog->error_cmd("PRVM_LoadProgs: out of bounds function statement (function %d) in %s", i, prog->name);
2087 // TODO bounds check parm_start, s_name, s_file, numparms, locals, parm_size
2090 // copy the globaldefs to the new globaldefs list
2091 for (i=0 ; i<prog->numglobaldefs ; i++)
2093 prog->globaldefs[i].type = LittleShort(inglobaldefs[i].type);
2094 prog->globaldefs[i].ofs = LittleShort(inglobaldefs[i].ofs);
2095 prog->globaldefs[i].s_name = LittleLong(inglobaldefs[i].s_name);
2096 // TODO bounds check ofs, s_name
2099 // append the required globals
2100 for (i = 0;i < numrequiredglobals;i++)
2102 prog->globaldefs[prog->numglobaldefs].type = required_global[i].type;
2103 prog->globaldefs[prog->numglobaldefs].ofs = prog->numglobals;
2104 prog->globaldefs[prog->numglobaldefs].s_name = PRVM_SetEngineString(prog, required_global[i].name);
2105 if (prog->globaldefs[prog->numglobaldefs].type == ev_vector)
2106 prog->numglobals += 3;
2109 prog->numglobaldefs++;
2112 // copy the progs fields to the new fields list
2113 for (i = 0;i < prog->numfielddefs;i++)
2115 prog->fielddefs[i].type = LittleShort(infielddefs[i].type);
2116 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
2117 prog->error_cmd("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", prog->name);
2118 prog->fielddefs[i].ofs = LittleShort(infielddefs[i].ofs);
2119 prog->fielddefs[i].s_name = LittleLong(infielddefs[i].s_name);
2120 // TODO bounds check ofs, s_name
2123 // append the required fields
2124 for (i = 0;i < numrequiredfields;i++)
2126 prog->fielddefs[prog->numfielddefs].type = required_field[i].type;
2127 prog->fielddefs[prog->numfielddefs].ofs = prog->entityfields;
2128 prog->fielddefs[prog->numfielddefs].s_name = PRVM_SetEngineString(prog, required_field[i].name);
2129 if (prog->fielddefs[prog->numfielddefs].type == ev_vector)
2130 prog->entityfields += 3;
2132 prog->entityfields++;
2133 prog->numfielddefs++;
2136 // LadyHavoc: TODO: reorder globals to match engine struct
2137 // LadyHavoc: TODO: reorder fields to match engine struct
2138 #define remapglobal(index) (index)
2139 #define remapfield(index) (index)
2142 // FIXME: LadyHavoc: this uses a crude way to identify integer constants, rather than checking for matching globaldefs and checking their type
2143 for (i = 0;i < prog->progs_numglobals;i++)
2145 u.i = LittleLong(inglobals[i]);
2146 // most globals are 0, we only need to deal with the ones that are not
2149 d = u.i & 0xFF800000;
2150 if ((d == 0xFF800000) || (d == 0))
2152 // Looks like an integer (expand to int64)
2153 prog->globals.ip[remapglobal(i)] = u.i;
2157 // Looks like a float (expand to double)
2158 prog->globals.fp[remapglobal(i)] = u.f;
2163 // LadyHavoc: TODO: support 32bit progs statement formats
2164 // copy, remap globals in statements, bounds check
2165 for (i = 0;i < prog->progs_numstatements;i++)
2167 op = (opcode_t)LittleShort(instatements[i].op);
2168 a = (unsigned short)LittleShort(instatements[i].a);
2169 b = (unsigned short)LittleShort(instatements[i].b);
2170 c = (unsigned short)LittleShort(instatements[i].c);
2176 if (a >= prog->progs_numglobals || b + i < 0 || b + i >= prog->progs_numstatements)
2177 prog->error_cmd("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", i, prog->name);
2178 prog->statements[i].op = op;
2179 prog->statements[i].operand[0] = remapglobal(a);
2180 prog->statements[i].operand[1] = -1;
2181 prog->statements[i].operand[2] = -1;
2182 prog->statements[i].jumpabsolute = i + b;
2186 if (a + i < 0 || a + i >= prog->progs_numstatements)
2187 prog->error_cmd("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, prog->name);
2188 prog->statements[i].op = op;
2189 prog->statements[i].operand[0] = -1;
2190 prog->statements[i].operand[1] = -1;
2191 prog->statements[i].operand[2] = -1;
2192 prog->statements[i].jumpabsolute = i + a;
2195 Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", (int)op, i, prog->name);
2197 // global global global
2232 if (a >= prog->progs_numglobals || b >= prog->progs_numglobals || c >= prog->progs_numglobals)
2233 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
2234 prog->statements[i].op = op;
2235 prog->statements[i].operand[0] = remapglobal(a);
2236 prog->statements[i].operand[1] = remapglobal(b);
2237 prog->statements[i].operand[2] = remapglobal(c);
2238 prog->statements[i].jumpabsolute = -1;
2240 // global none global
2246 if (a >= prog->progs_numglobals || c >= prog->progs_numglobals)
2247 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2248 prog->statements[i].op = op;
2249 prog->statements[i].operand[0] = remapglobal(a);
2250 prog->statements[i].operand[1] = -1;
2251 prog->statements[i].operand[2] = remapglobal(c);
2252 prog->statements[i].jumpabsolute = -1;
2268 if (a >= prog->progs_numglobals || b >= prog->progs_numglobals)
2269 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2270 prog->statements[i].op = op;
2271 prog->statements[i].operand[0] = remapglobal(a);
2272 prog->statements[i].operand[1] = remapglobal(b);
2273 prog->statements[i].operand[2] = -1;
2274 prog->statements[i].jumpabsolute = -1;
2278 if ( a < prog->progs_numglobals)
2279 if ( prog->globals.ip[remapglobal(a)] >= 0 )
2280 if ( prog->globals.ip[remapglobal(a)] < prog->progs_numfunctions )
2281 if ( prog->functions[prog->globals.ip[remapglobal(a)]].first_statement == -642 )
2282 ++prog->numexplicitcoveragestatements;
2293 if ( a >= prog->progs_numglobals)
2294 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2295 prog->statements[i].op = op;
2296 prog->statements[i].operand[0] = remapglobal(a);
2297 prog->statements[i].operand[1] = -1;
2298 prog->statements[i].operand[2] = -1;
2299 prog->statements[i].jumpabsolute = -1;
2303 if(prog->numstatements < 1)
2305 prog->error_cmd("PRVM_LoadProgs: empty program in %s", prog->name);
2307 else switch(prog->statements[prog->numstatements - 1].op)
2314 prog->error_cmd("PRVM_LoadProgs: program may fall off the edge (does not end with RETURN, GOTO or DONE) in %s", prog->name);
2318 // we're done with the file now
2320 Mem_Free(dprograms);
2323 // check required functions
2324 for(i=0 ; i < numrequiredfunc ; i++)
2325 if(PRVM_ED_FindFunction(prog, required_func[i]) == 0)
2326 prog->error_cmd("%s: %s not found in %s",prog->name, required_func[i], filename);
2328 PRVM_LoadLNO(prog, filename);
2330 PRVM_Init_Exec(prog);
2332 if(*prvm_language.string)
2333 // in CSQC we really shouldn't be able to change how stuff works... sorry for now
2334 // later idea: include a list of authorized .po file checksums with the csprogs
2336 qboolean deftrans = prog == CLVM_prog;
2337 const char *realfilename = (prog != CLVM_prog ? filename : csqc_progname.string);
2338 if(deftrans) // once we have dotranslate_ strings, ALWAYS use the opt-in method!
2340 for (i=0 ; i<prog->numglobaldefs ; i++)
2343 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2344 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2345 if(name && !strncmp(name, "dotranslate_", 12))
2352 if(!strcmp(prvm_language.string, "dump"))
2354 qfile_t *f = FS_OpenRealFile(va(vabuf, sizeof(vabuf), "%s.pot", realfilename), "w", false);
2355 Con_Printf("Dumping to %s.pot\n", realfilename);
2358 for (i=0 ; i<prog->numglobaldefs ; i++)
2361 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2362 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2363 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2365 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2366 const char *value = PRVM_GetString(prog, val->string);
2369 char buf[MAX_INPUTLINE];
2370 PRVM_PO_UnparseString(buf, value, sizeof(buf));
2371 FS_Printf(f, "msgid \"%s\"\nmsgstr \"\"\n\n", buf);
2380 po_t *po = PRVM_PO_Load(
2381 va(vabuf, sizeof(vabuf), "%s.%s.po", realfilename, prvm_language.string),
2382 va(vabuf2, sizeof(vabuf2), "common.%s.po", prvm_language.string),
2383 prog->progs_mempool);
2386 for (i=0 ; i<prog->numglobaldefs ; i++)
2389 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2390 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2391 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2393 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2394 const char *value = PRVM_GetString(prog, val->string);
2397 value = PRVM_PO_Lookup(po, value);
2399 val->string = PRVM_SetEngineString(prog, value);
2407 for (cvar = prog->console_cmd->cvars->vars; cvar; cvar = cvar->next)
2408 cvar->globaldefindex[prog - prvm_prog_list] = -1;
2410 for (i=0 ; i<prog->numglobaldefs ; i++)
2413 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2414 //Con_Printf("found var %s\n", name);
2416 && !strncmp(name, "autocvar_", 9)
2417 && !(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
2420 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2421 cvar = Cvar_FindVar(prog->console_cmd->cvars, name + 9, prog->console_cmd->cvars_flagsmask);
2422 //Con_Printf("PRVM_LoadProgs: autocvar global %s in %s, processing...\n", name, prog->name);
2429 Con_DPrintf("PRVM_LoadProgs: no cvar for autocvar global %s in %s, creating...\n", name, prog->name);
2430 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2433 if((float)((int)(val->_float)) == val->_float)
2434 dpsnprintf(buf, sizeof(buf), "%i", (int)(val->_float));
2439 for (int precision = 7; precision <= 9; ++precision) {
2440 dpsnprintf(buf, sizeof(buf), "%.*g", precision, f);
2441 if ((float)atof(buf) == f) {
2449 for (i = 0; i < 3; ++i)
2453 for (int precision = 7; precision <= 9; ++precision) {
2454 dpsnprintf(buf, sizeof(buf), "%.*g", precision, f);
2455 if ((float)atof(buf) == f) {
2456 prec[i] = precision;
2461 dpsnprintf(buf, sizeof(buf), "%.*g %.*g %.*g", prec[0], val->vector[0], prec[1], val->vector[1], prec[2], val->vector[2]);
2465 value = PRVM_GetString(prog, val->string);
2468 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2471 cvar = Cvar_Get(prog->console_cmd->cvars, name + 9, value, prog->console_cmd->cvars_flagsmask, NULL);
2472 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2474 val->string = PRVM_SetEngineString(prog, cvar->string);
2475 cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2478 prog->error_cmd("PRVM_LoadProgs: could not create cvar for autocvar global %s in %s", name, prog->name);
2479 cvar->globaldefindex[prog - prvm_prog_list] = i;
2481 else if((cvar->flags & CVAR_PRIVATE) == 0)
2483 // MUST BE SYNCED WITH cvar.c Cvar_Set
2486 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2489 val->_float = cvar->value;
2493 VectorClear(val->vector);
2494 for (j = 0;j < 3;j++)
2496 while (*s && ISWHITESPACE(*s))
2500 val->vector[j] = atof(s);
2501 while (!ISWHITESPACE(*s))
2508 val->string = PRVM_SetEngineString(prog, cvar->string);
2509 cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2512 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2515 cvar->globaldefindex[prog - prvm_prog_list] = i;
2518 Con_Printf("PRVM_LoadProgs: private cvar for autocvar global %s in %s\n", name, prog->name);
2524 prog->loaded = true;
2526 PRVM_UpdateBreakpoints(prog);
2528 // set flags & ddef_ts in prog
2532 PRVM_FindOffsets(prog);
2534 prog->init_cmd(prog);
2537 PRVM_MEM_Alloc(prog);
2539 Con_Printf("%s: program loaded (crc %i, size %iK)\n", prog->name, prog->filecrc, (int)(filesize/1024));
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));