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;
1388 // parse the opening brace
1389 if (!COM_ParseToken_Simple(&data, false, false, true))
1391 if (com_token[0] != '{')
1392 prog->error_cmd("PRVM_ED_LoadFromFile: %s: found %s when expecting {", prog->name, com_token);
1394 // CHANGED: this is not conform to PR_LoadFromFile
1395 if(prog->loadintoworld)
1397 prog->loadintoworld = false;
1398 ent = PRVM_EDICT_NUM(0);
1401 ent = PRVM_ED_Alloc(prog);
1404 if (ent != prog->edicts) // hack
1405 memset (ent->fields.fp, 0, prog->entityfields * sizeof(prvm_vec_t));
1407 data = PRVM_ED_ParseEdict (prog, data, ent);
1410 // remove the entity ?
1411 if(!prog->load_edict(prog, ent))
1413 PRVM_ED_Free(prog, ent);
1418 if (PRVM_serverfunction(SV_OnEntityPreSpawnFunction))
1421 PRVM_serverglobalfloat(time) = sv.time;
1422 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1423 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityPreSpawnFunction), "QC function SV_OnEntityPreSpawnFunction is missing");
1426 if(ent->priv.required->free)
1433 // immediately call spawn function, but only if there is a self global and a classname
1435 if(!ent->priv.required->free)
1437 if (!PRVM_alledictstring(ent, classname))
1439 Con_Print("No classname for:\n");
1440 PRVM_ED_Print(prog, ent, NULL);
1441 PRVM_ED_Free (prog, ent);
1445 * This is required for FTE compatibility (FreeCS).
1446 * It copies the key/value pairs themselves into a
1447 * global for QC to parse on its own.
1451 fulldata_ddef = PRVM_ED_FindGlobal(prog, "__fullspawndata");
1453 fulldata = (prvm_eval_t *) &prog->globals.fp[fulldata_ddef->ofs];
1458 fulldata->string = PRVM_AllocString(prog, data - start + 1, &spawndata);
1459 for(in = start; in < data; )
1463 *spawndata++ = '\t';
1471 // look for the spawn function
1472 funcname = PRVM_GetString(prog, PRVM_alledictstring(ent, classname));
1473 func = PRVM_ED_FindFunction (prog, va(vabuf, sizeof(vabuf), "spawnfunc_%s", funcname));
1475 if(!PRVM_allglobalfloat(require_spawnfunc_prefix))
1476 func = PRVM_ED_FindFunction (prog, funcname);
1480 // check for OnEntityNoSpawnFunction
1481 if (PRVM_serverfunction(SV_OnEntityNoSpawnFunction))
1484 PRVM_serverglobalfloat(time) = sv.time;
1485 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1486 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityNoSpawnFunction), "QC function SV_OnEntityNoSpawnFunction is missing");
1490 if (developer.integer > 0) // don't confuse non-developers with errors
1492 Con_Print("No spawn function for:\n");
1493 PRVM_ED_Print(prog, ent, NULL);
1495 PRVM_ED_Free (prog, ent);
1496 continue; // not included in "inhibited" count
1502 PRVM_serverglobalfloat(time) = sv.time;
1503 PRVM_allglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1504 prog->ExecuteProgram(prog, func - prog->functions, "");
1508 if(!ent->priv.required->free)
1509 if (PRVM_serverfunction(SV_OnEntityPostSpawnFunction))
1512 PRVM_serverglobalfloat(time) = sv.time;
1513 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1514 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityPostSpawnFunction), "QC function SV_OnEntityPostSpawnFunction is missing");
1518 if (ent->priv.required->free)
1522 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);
1524 prvm_reuseedicts_always_allow = 0;
1527 static void PRVM_FindOffsets(prvm_prog_t *prog)
1529 // field and global searches use -1 for NULL
1530 memset(&prog->fieldoffsets, -1, sizeof(prog->fieldoffsets));
1531 memset(&prog->globaloffsets, -1, sizeof(prog->globaloffsets));
1532 // function searches use 0 for NULL
1533 memset(&prog->funcoffsets, 0, sizeof(prog->funcoffsets));
1534 #define PRVM_DECLARE_serverglobalfloat(x)
1535 #define PRVM_DECLARE_serverglobalvector(x)
1536 #define PRVM_DECLARE_serverglobalstring(x)
1537 #define PRVM_DECLARE_serverglobaledict(x)
1538 #define PRVM_DECLARE_serverglobalfunction(x)
1539 #define PRVM_DECLARE_clientglobalfloat(x)
1540 #define PRVM_DECLARE_clientglobalvector(x)
1541 #define PRVM_DECLARE_clientglobalstring(x)
1542 #define PRVM_DECLARE_clientglobaledict(x)
1543 #define PRVM_DECLARE_clientglobalfunction(x)
1544 #define PRVM_DECLARE_menuglobalfloat(x)
1545 #define PRVM_DECLARE_menuglobalvector(x)
1546 #define PRVM_DECLARE_menuglobalstring(x)
1547 #define PRVM_DECLARE_menuglobaledict(x)
1548 #define PRVM_DECLARE_menuglobalfunction(x)
1549 #define PRVM_DECLARE_serverfieldfloat(x)
1550 #define PRVM_DECLARE_serverfieldvector(x)
1551 #define PRVM_DECLARE_serverfieldstring(x)
1552 #define PRVM_DECLARE_serverfieldedict(x)
1553 #define PRVM_DECLARE_serverfieldfunction(x)
1554 #define PRVM_DECLARE_clientfieldfloat(x)
1555 #define PRVM_DECLARE_clientfieldvector(x)
1556 #define PRVM_DECLARE_clientfieldstring(x)
1557 #define PRVM_DECLARE_clientfieldedict(x)
1558 #define PRVM_DECLARE_clientfieldfunction(x)
1559 #define PRVM_DECLARE_menufieldfloat(x)
1560 #define PRVM_DECLARE_menufieldvector(x)
1561 #define PRVM_DECLARE_menufieldstring(x)
1562 #define PRVM_DECLARE_menufieldedict(x)
1563 #define PRVM_DECLARE_menufieldfunction(x)
1564 #define PRVM_DECLARE_serverfunction(x)
1565 #define PRVM_DECLARE_clientfunction(x)
1566 #define PRVM_DECLARE_menufunction(x)
1567 #define PRVM_DECLARE_field(x) prog->fieldoffsets.x = PRVM_ED_FindFieldOffset(prog, #x);
1568 #define PRVM_DECLARE_global(x) prog->globaloffsets.x = PRVM_ED_FindGlobalOffset(prog, #x);
1569 #define PRVM_DECLARE_function(x) prog->funcoffsets.x = PRVM_ED_FindFunctionOffset(prog, #x);
1570 #include "prvm_offsets.h"
1571 #undef PRVM_DECLARE_serverglobalfloat
1572 #undef PRVM_DECLARE_serverglobalvector
1573 #undef PRVM_DECLARE_serverglobalstring
1574 #undef PRVM_DECLARE_serverglobaledict
1575 #undef PRVM_DECLARE_serverglobalfunction
1576 #undef PRVM_DECLARE_clientglobalfloat
1577 #undef PRVM_DECLARE_clientglobalvector
1578 #undef PRVM_DECLARE_clientglobalstring
1579 #undef PRVM_DECLARE_clientglobaledict
1580 #undef PRVM_DECLARE_clientglobalfunction
1581 #undef PRVM_DECLARE_menuglobalfloat
1582 #undef PRVM_DECLARE_menuglobalvector
1583 #undef PRVM_DECLARE_menuglobalstring
1584 #undef PRVM_DECLARE_menuglobaledict
1585 #undef PRVM_DECLARE_menuglobalfunction
1586 #undef PRVM_DECLARE_serverfieldfloat
1587 #undef PRVM_DECLARE_serverfieldvector
1588 #undef PRVM_DECLARE_serverfieldstring
1589 #undef PRVM_DECLARE_serverfieldedict
1590 #undef PRVM_DECLARE_serverfieldfunction
1591 #undef PRVM_DECLARE_clientfieldfloat
1592 #undef PRVM_DECLARE_clientfieldvector
1593 #undef PRVM_DECLARE_clientfieldstring
1594 #undef PRVM_DECLARE_clientfieldedict
1595 #undef PRVM_DECLARE_clientfieldfunction
1596 #undef PRVM_DECLARE_menufieldfloat
1597 #undef PRVM_DECLARE_menufieldvector
1598 #undef PRVM_DECLARE_menufieldstring
1599 #undef PRVM_DECLARE_menufieldedict
1600 #undef PRVM_DECLARE_menufieldfunction
1601 #undef PRVM_DECLARE_serverfunction
1602 #undef PRVM_DECLARE_clientfunction
1603 #undef PRVM_DECLARE_menufunction
1604 #undef PRVM_DECLARE_field
1605 #undef PRVM_DECLARE_global
1606 #undef PRVM_DECLARE_function
1611 typedef struct dpfield_s
1618 #define DPFIELDS (sizeof(dpfields) / sizeof(dpfield_t))
1620 dpfield_t dpfields[] =
1631 #define PO_HASHSIZE 16384
1632 typedef struct po_string_s
1635 struct po_string_s *nextonhashchain;
1640 po_string_t *hashtable[PO_HASHSIZE];
1643 static void PRVM_PO_UnparseString(char *out, const char *in, size_t outsize)
1652 case '\a': if(outsize >= 2) { *out++ = '\\'; *out++ = 'a'; outsize -= 2; } break;
1653 case '\b': if(outsize >= 2) { *out++ = '\\'; *out++ = 'b'; outsize -= 2; } break;
1654 case '\t': if(outsize >= 2) { *out++ = '\\'; *out++ = 't'; outsize -= 2; } break;
1655 case '\r': if(outsize >= 2) { *out++ = '\\'; *out++ = 'r'; outsize -= 2; } break;
1656 case '\n': if(outsize >= 2) { *out++ = '\\'; *out++ = 'n'; outsize -= 2; } break;
1657 case '\\': if(outsize >= 2) { *out++ = '\\'; *out++ = '\\'; outsize -= 2; } break;
1658 case '"': if(outsize >= 2) { *out++ = '\\'; *out++ = '"'; outsize -= 2; } break;
1660 if(*in >= 0 && *in <= 0x1F)
1665 *out++ = '0' + ((*in & 0700) >> 6);
1666 *out++ = '0' + ((*in & 0070) >> 3);
1667 *out++ = '0' + (*in & 0007) ;
1684 static void PRVM_PO_ParseString(char *out, const char *in, size_t outsize)
1697 case 'a': if(outsize > 0) { *out++ = '\a'; --outsize; } break;
1698 case 'b': if(outsize > 0) { *out++ = '\b'; --outsize; } break;
1699 case 't': if(outsize > 0) { *out++ = '\t'; --outsize; } break;
1700 case 'r': if(outsize > 0) { *out++ = '\r'; --outsize; } break;
1701 case 'n': if(outsize > 0) { *out++ = '\n'; --outsize; } break;
1702 case '\\': if(outsize > 0) { *out++ = '\\'; --outsize; } break;
1703 case '"': if(outsize > 0) { *out++ = '"'; --outsize; } break;
1704 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
1708 if(*in >= '0' && *in <= '7')
1711 *out = (*out << 3) | (*in - '0');
1714 if(*in >= '0' && *in <= '7')
1717 *out = (*out << 3) | (*in - '0');
1728 if(outsize > 0) { *out++ = *in; --outsize; }
1743 static po_t *PRVM_PO_Load(const char *filename, const char *filename2, mempool_t *pool)
1748 char inbuf[MAX_INPUTLINE];
1749 char decodedbuf[MAX_INPUTLINE];
1752 po_string_t thisstr;
1755 for (i = 0; i < 2; ++i)
1757 const char *buf = (const char *)
1758 FS_LoadFile((i > 0 ? filename : filename2), pool, true, NULL);
1759 // first read filename2, then read filename
1760 // so that progs.dat.de.po wins over common.de.po
1761 // and within file, last item wins
1768 po = (po_t *)Mem_Alloc(pool, sizeof(*po));
1769 memset(po, 0, sizeof(*po));
1772 memset(&thisstr, 0, sizeof(thisstr)); // hush compiler warning
1780 p = strchr(p, '\n');
1786 if(*p == '\r' || *p == '\n')
1791 if(!strncmp(p, "msgid \"", 7))
1796 else if(!strncmp(p, "msgstr \"", 8))
1803 p = strchr(p, '\n');
1813 q = strchr(p, '\n');
1820 if((size_t)(q - p) >= (size_t) sizeof(inbuf))
1822 strlcpy(inbuf, p, q - p); // not - 1, because this adds a NUL
1823 PRVM_PO_ParseString(decodedbuf + decodedpos, inbuf, sizeof(decodedbuf) - decodedpos);
1824 decodedpos += strlen(decodedbuf + decodedpos);
1834 Mem_Free(thisstr.key);
1835 thisstr.key = (char *)Mem_Alloc(pool, decodedpos + 1);
1836 memcpy(thisstr.key, decodedbuf, decodedpos + 1);
1838 else if(decodedpos > 0 && thisstr.key) // skip empty translation results
1840 thisstr.value = (char *)Mem_Alloc(pool, decodedpos + 1);
1841 memcpy(thisstr.value, decodedbuf, decodedpos + 1);
1842 hashindex = CRC_Block((const unsigned char *) thisstr.key, strlen(thisstr.key)) % PO_HASHSIZE;
1843 thisstr.nextonhashchain = po->hashtable[hashindex];
1844 po->hashtable[hashindex] = (po_string_t *)Mem_Alloc(pool, sizeof(thisstr));
1845 memcpy(po->hashtable[hashindex], &thisstr, sizeof(thisstr));
1846 memset(&thisstr, 0, sizeof(thisstr));
1850 Mem_Free((char *) buf);
1855 static const char *PRVM_PO_Lookup(po_t *po, const char *str)
1857 int hashindex = CRC_Block((const unsigned char *) str, strlen(str)) % PO_HASHSIZE;
1858 po_string_t *p = po->hashtable[hashindex];
1861 if(!strcmp(str, p->key))
1863 p = p->nextonhashchain;
1867 static void PRVM_PO_Destroy(po_t *po)
1870 for(i = 0; i < PO_HASHSIZE; ++i)
1872 po_string_t *p = po->hashtable[i];
1876 p = p->nextonhashchain;
1885 void PRVM_LeakTest(prvm_prog_t *prog);
1886 void PRVM_Prog_Reset(prvm_prog_t *prog)
1890 if(prog->tempstringsbuf.cursize)
1891 Mem_Free(prog->tempstringsbuf.data);
1892 prog->tempstringsbuf.cursize = 0;
1893 PRVM_LeakTest(prog);
1894 prog->reset_cmd(prog);
1895 Mem_FreePool(&prog->progs_mempool);
1897 PRVM_PO_Destroy((po_t *) prog->po);
1899 memset(prog,0,sizeof(prvm_prog_t));
1900 prog->break_statement = -1;
1901 prog->watch_global_type = ev_void;
1902 prog->watch_field_type = ev_void;
1910 static void PRVM_LoadLNO( prvm_prog_t *prog, const char *progname ) {
1911 fs_offset_t filesize;
1913 unsigned int *header;
1916 FS_StripExtension( progname, filename, sizeof( filename ) );
1917 strlcat( filename, ".lno", sizeof( filename ) );
1919 lno = FS_LoadFile( filename, tempmempool, false, &filesize );
1925 <Spike> SafeWrite (h, &lnotype, sizeof(int));
1926 <Spike> SafeWrite (h, &version, sizeof(int));
1927 <Spike> SafeWrite (h, &numglobaldefs, sizeof(int));
1928 <Spike> SafeWrite (h, &numpr_globals, sizeof(int));
1929 <Spike> SafeWrite (h, &numfielddefs, sizeof(int));
1930 <Spike> SafeWrite (h, &numstatements, sizeof(int));
1931 <Spike> SafeWrite (h, statement_linenums, numstatements*sizeof(int));
1933 if ((unsigned int)filesize < (6 + prog->progs_numstatements) * sizeof(int))
1939 header = (unsigned int *) lno;
1940 if( header[ 0 ] == *(unsigned int *) "LNOF" &&
1941 LittleLong( header[ 1 ] ) == 1 &&
1942 (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs_numglobaldefs &&
1943 (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs_numglobals &&
1944 (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs_numfielddefs &&
1945 (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs_numstatements )
1947 prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof( int ) );
1948 memcpy( prog->statement_linenums, header + 6, prog->progs_numstatements * sizeof( int ) );
1950 /* gmqcc suports columnums */
1951 if ((unsigned int)filesize > ((6 + 2 * prog->progs_numstatements) * sizeof( int )))
1953 prog->statement_columnnums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof( int ) );
1954 memcpy( prog->statement_columnnums, header + 6 + prog->progs_numstatements, prog->progs_numstatements * sizeof( int ) );
1965 static void PRVM_UpdateBreakpoints(prvm_prog_t *prog);
1966 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)
1969 dprograms_t *dprograms;
1970 dstatement_t *instatements;
1971 ddef_t *infielddefs;
1972 ddef_t *inglobaldefs;
1974 dfunction_t *infunctions;
1976 fs_offset_t filesize;
1977 int requiredglobalspace;
1994 prog->error_cmd("PRVM_LoadProgs: there is already a %s program loaded!", prog->name );
1996 Host_LockSession(); // all progs can use the session cvar
1997 Crypto_LoadKeys(); // all progs might use the keys at init time
2001 dprograms = (dprograms_t *) data;
2005 dprograms = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
2006 if (dprograms == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
2007 prog->error_cmd("PRVM_LoadProgs: couldn't load %s for %s", filename, prog->name);
2008 // TODO bounds check header fields (e.g. numstatements), they must never go behind end of file
2010 prog->profiletime = Sys_DirtyTime();
2011 prog->starttime = host.realtime;
2013 Con_DPrintf("%s programs occupy %iK.\n", prog->name, (int)(filesize/1024));
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 // Inittime is at least the time when this function finished. However,
2540 // later events may bump it.
2541 prog->inittime = host.realtime;
2545 static void PRVM_Fields_f(cmd_state_t *cmd)
2548 int i, j, ednum, used, usedamount;
2550 char tempstring[MAX_INPUTLINE], tempstring2[260];
2560 Con_Print("no progs loaded\n");
2565 if(Cmd_Argc(cmd) != 2)
2567 Con_Print("prvm_fields <program name>\n");
2571 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2574 counts = (int *)Mem_Alloc(tempmempool, prog->numfielddefs * sizeof(int));
2575 for (ednum = 0;ednum < prog->max_edicts;ednum++)
2577 ed = PRVM_EDICT_NUM(ednum);
2578 if (ed->priv.required->free)
2580 for (i = 1;i < prog->numfielddefs;i++)
2582 d = &prog->fielddefs[i];
2583 name = PRVM_GetString(prog, d->s_name);
2584 if (name[strlen(name)-2] == '_')
2585 continue; // skip _x, _y, _z vars
2586 val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
2587 // if the value is still all 0, skip the field
2588 for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
2590 if (val->ivector[j])
2601 for (i = 0;i < prog->numfielddefs;i++)
2603 d = &prog->fielddefs[i];
2604 name = PRVM_GetString(prog, d->s_name);
2605 if (name[strlen(name)-2] == '_')
2606 continue; // skip _x, _y, _z vars
2607 switch(d->type & ~DEF_SAVEGLOBAL)
2610 strlcat(tempstring, "string ", sizeof(tempstring));
2613 strlcat(tempstring, "entity ", sizeof(tempstring));
2616 strlcat(tempstring, "function ", sizeof(tempstring));
2619 strlcat(tempstring, "field ", sizeof(tempstring));
2622 strlcat(tempstring, "void ", sizeof(tempstring));
2625 strlcat(tempstring, "float ", sizeof(tempstring));
2628 strlcat(tempstring, "vector ", sizeof(tempstring));
2631 strlcat(tempstring, "pointer ", sizeof(tempstring));
2634 dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
2635 strlcat(tempstring, tempstring2, sizeof(tempstring));
2638 if (strlen(name) > sizeof(tempstring2)-4)
2640 memcpy (tempstring2, name, sizeof(tempstring2)-4);
2641 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
2642 tempstring2[sizeof(tempstring2)-1] = 0;
2645 strlcat(tempstring, name, sizeof(tempstring));
2646 for (j = (int)strlen(name);j < 25;j++)
2647 strlcat(tempstring, " ", sizeof(tempstring));
2648 dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
2649 strlcat(tempstring, tempstring2, sizeof(tempstring));
2650 strlcat(tempstring, "\n", sizeof(tempstring));
2651 if (strlen(tempstring) >= sizeof(tempstring)/2)
2653 Con_Print(tempstring);
2659 usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
2663 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);
2666 static void PRVM_Globals_f(cmd_state_t *cmd)
2670 const char *wildcard;
2676 Con_Print("no progs loaded\n");
2679 if(Cmd_Argc (cmd) < 2 || Cmd_Argc(cmd) > 3)
2681 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
2685 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2688 if( Cmd_Argc(cmd) == 3)
2689 wildcard = Cmd_Argv(cmd, 2);
2693 Con_Printf("%s :", prog->name);
2695 for (i = 0;i < prog->numglobaldefs;i++)
2698 if( !matchpattern( PRVM_GetString(prog, prog->globaldefs[i].s_name), wildcard, 1) )
2703 Con_Printf("%s\n", PRVM_GetString(prog, prog->globaldefs[i].s_name));
2705 Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->numglobals, numculled, prog->numglobals * 4);
2713 static void PRVM_Global_f(cmd_state_t *cmd)
2717 char valuebuf[MAX_INPUTLINE];
2718 if( Cmd_Argc(cmd) != 3 ) {
2719 Con_Printf( "prvm_global <program name> <global name>\n" );
2723 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2726 global = PRVM_ED_FindGlobal( prog, Cmd_Argv(cmd, 2) );
2728 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(cmd, 2), Cmd_Argv(cmd, 1) );
2730 Con_Printf( "%s: %s\n", Cmd_Argv(cmd, 2), PRVM_ValueString( prog, (etype_t)global->type, PRVM_GLOBALFIELDVALUE(global->ofs), valuebuf, sizeof(valuebuf) ) );
2738 static void PRVM_GlobalSet_f(cmd_state_t *cmd)
2742 if( Cmd_Argc(cmd) != 4 ) {
2743 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2747 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2750 global = PRVM_ED_FindGlobal( prog, Cmd_Argv(cmd, 2) );
2752 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(cmd, 2), Cmd_Argv(cmd, 1) );
2754 PRVM_ED_ParseEpair( prog, NULL, global, Cmd_Argv(cmd, 3), true );
2758 ======================
2759 Break- and Watchpoints
2760 ======================
2764 char break_statement[256];
2765 char watch_global[256];
2767 char watch_field[256];
2770 static debug_data_t debug_data[PRVM_PROG_MAX];
2772 void PRVM_Breakpoint(prvm_prog_t *prog, int stack_index, const char *text)
2775 Con_Printf("PRVM_Breakpoint: %s\n", text);
2776 PRVM_PrintState(prog, stack_index);
2777 if (prvm_breakpointdump.integer)
2778 SV_Savegame_to(prog, va(vabuf, sizeof(vabuf), "breakpoint-%s.dmp", prog->name));
2781 void PRVM_Watchpoint(prvm_prog_t *prog, int stack_index, const char *text, etype_t type, prvm_eval_t *o, prvm_eval_t *n)
2783 size_t sz = sizeof(prvm_vec_t) * ((type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2784 if (memcmp(o, n, sz))
2787 char valuebuf_o[128];
2788 char valuebuf_n[128];
2789 PRVM_UglyValueString(prog, type, o, valuebuf_o, sizeof(valuebuf_o));
2790 PRVM_UglyValueString(prog, type, n, valuebuf_n, sizeof(valuebuf_n));
2791 dpsnprintf(buf, sizeof(buf), "%s: %s -> %s", text, valuebuf_o, valuebuf_n);
2792 PRVM_Breakpoint(prog, stack_index, buf);
2797 static void PRVM_UpdateBreakpoints(prvm_prog_t *prog)
2799 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2802 if (debug->break_statement[0])
2804 if (debug->break_statement[0] >= '0' && debug->break_statement[0] <= '9')
2806 prog->break_statement = atoi(debug->break_statement);
2807 prog->break_stack_index = 0;
2812 func = PRVM_ED_FindFunction (prog, debug->break_statement);
2815 Con_Printf("%s progs: no function or statement named %s to break on!\n", prog->name, debug->break_statement);
2816 prog->break_statement = -1;
2820 prog->break_statement = func->first_statement;
2821 prog->break_stack_index = 1;
2824 if (prog->break_statement >= -1)
2825 Con_Printf("%s progs: breakpoint is at statement %d\n", prog->name, prog->break_statement);
2828 prog->break_statement = -1;
2830 if (debug->watch_global[0])
2832 ddef_t *global = PRVM_ED_FindGlobal( prog, debug->watch_global );
2835 Con_Printf( "%s progs: no global named '%s' to watch!\n", prog->name, debug->watch_global );
2836 prog->watch_global_type = ev_void;
2840 size_t sz = sizeof(prvm_vec_t) * ((global->type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2841 prog->watch_global = global->ofs;
2842 prog->watch_global_type = (etype_t)global->type;
2843 memcpy(&prog->watch_global_value, PRVM_GLOBALFIELDVALUE(prog->watch_global), sz);
2845 if (prog->watch_global_type != ev_void)
2846 Con_Printf("%s progs: global watchpoint is at global index %d\n", prog->name, prog->watch_global);
2849 prog->watch_global_type = ev_void;
2851 if (debug->watch_field[0])
2853 ddef_t *field = PRVM_ED_FindField( prog, debug->watch_field );
2856 Con_Printf( "%s progs: no field named '%s' to watch!\n", prog->name, debug->watch_field );
2857 prog->watch_field_type = ev_void;
2861 size_t sz = sizeof(prvm_vec_t) * ((field->type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2862 prog->watch_edict = debug->watch_edict;
2863 prog->watch_field = field->ofs;
2864 prog->watch_field_type = (etype_t)field->type;
2865 if (prog->watch_edict < prog->num_edicts)
2866 memcpy(&prog->watch_edictfield_value, PRVM_EDICTFIELDVALUE(PRVM_EDICT_NUM(prog->watch_edict), prog->watch_field), sz);
2868 memset(&prog->watch_edictfield_value, 0, sz);
2870 if (prog->watch_edict != ev_void)
2871 Con_Printf("%s progs: edict field watchpoint is at edict %d field index %d\n", prog->name, prog->watch_edict, prog->watch_field);
2874 prog->watch_field_type = ev_void;
2877 static void PRVM_Breakpoint_f(cmd_state_t *cmd)
2881 if( Cmd_Argc(cmd) == 2 ) {
2882 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2885 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2886 debug->break_statement[0] = 0;
2888 PRVM_UpdateBreakpoints(prog);
2891 if( Cmd_Argc(cmd) != 3 ) {
2892 Con_Printf( "prvm_breakpoint <program name> <function name | statement>\n" );
2896 if (!(prog = PRVM_ProgFromString(Cmd_Argv(cmd, 1))))
2900 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2901 strlcpy(debug->break_statement, Cmd_Argv(cmd, 2), sizeof(debug->break_statement));
2903 PRVM_UpdateBreakpoints(prog);
2906 static void PRVM_GlobalWatchpoint_f(cmd_state_t *cmd)
2910 if( Cmd_Argc(cmd) == 2 ) {
2911 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2914 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2915 debug->watch_global[0] = 0;
2917 PRVM_UpdateBreakpoints(prog);
2920 if( Cmd_Argc(cmd) != 3 ) {
2921 Con_Printf( "prvm_globalwatchpoint <program name> <global name>\n" );
2925 if (!(prog = PRVM_ProgFromString(Cmd_Argv(cmd, 1))))
2929 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2930 strlcpy(debug->watch_global, Cmd_Argv(cmd, 2), sizeof(debug->watch_global));
2932 PRVM_UpdateBreakpoints(prog);
2935 static void PRVM_EdictWatchpoint_f(cmd_state_t *cmd)
2939 if( Cmd_Argc(cmd) == 2 ) {
2940 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2943 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2944 debug->watch_field[0] = 0;
2946 PRVM_UpdateBreakpoints(prog);
2949 if( Cmd_Argc(cmd) != 4 ) {
2950 Con_Printf( "prvm_edictwatchpoint <program name> <edict number> <field name>\n" );
2954 if (!(prog = PRVM_ProgFromString(Cmd_Argv(cmd, 1))))
2958 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2959 debug->watch_edict = atoi(Cmd_Argv(cmd, 2));
2960 strlcpy(debug->watch_field, Cmd_Argv(cmd, 3), sizeof(debug->watch_field));
2962 PRVM_UpdateBreakpoints(prog);
2970 void PRVM_Init (void)
2972 Cmd_AddCommand(CMD_SHARED, "prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2973 Cmd_AddCommand(CMD_SHARED, "prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2974 Cmd_AddCommand(CMD_SHARED, "prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2975 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)");
2976 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");
2977 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)");
2978 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)");
2979 Cmd_AddCommand(CMD_SHARED, "prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2980 Cmd_AddCommand(CMD_SHARED, "prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2981 Cmd_AddCommand(CMD_SHARED, "prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2982 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)");
2983 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");
2984 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");
2985 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)");
2986 Cmd_AddCommand(CMD_SHARED, "cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2987 Cmd_AddCommand(CMD_SHARED, "menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2988 Cmd_AddCommand(CMD_SHARED, "sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2989 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");
2990 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");
2991 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");
2993 Cvar_RegisterVariable (&prvm_language);
2994 Cvar_RegisterVariable (&prvm_traceqc);
2995 Cvar_RegisterVariable (&prvm_statementprofiling);
2996 Cvar_RegisterVariable (&prvm_timeprofiling);
2997 Cvar_RegisterVariable (&prvm_coverage);
2998 Cvar_RegisterVariable (&prvm_backtraceforwarnings);
2999 Cvar_RegisterVariable (&prvm_leaktest);
3000 Cvar_RegisterVariable (&prvm_leaktest_follow_targetname);
3001 Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames);
3002 Cvar_RegisterVariable (&prvm_errordump);
3003 Cvar_RegisterVariable (&prvm_breakpointdump);
3004 Cvar_RegisterVariable (&prvm_reuseedicts_startuptime);
3005 Cvar_RegisterVariable (&prvm_reuseedicts_neverinsameframe);
3006 Cvar_RegisterVariable (&prvm_garbagecollection_enable);
3007 Cvar_RegisterVariable (&prvm_garbagecollection_notify);
3008 Cvar_RegisterVariable (&prvm_garbagecollection_scan_limit);
3009 Cvar_RegisterVariable (&prvm_garbagecollection_strings);
3010 Cvar_RegisterVariable (&prvm_stringdebug);
3012 // COMMANDLINEOPTION: PRVM: -norunaway disables the runaway loop check (it might be impossible to exit DarkPlaces if used!)
3013 prvm_runawaycheck = !COM_CheckParm("-norunaway");
3023 void PRVM_Prog_Init(prvm_prog_t *prog, cmd_state_t *cmd)
3025 PRVM_Prog_Reset(prog);
3026 prog->leaktest_active = prvm_leaktest.integer != 0;
3027 prog->console_cmd = cmd;
3030 // LadyHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
3031 unsigned int PRVM_EDICT_NUM_ERROR(prvm_prog_t *prog, unsigned int n, const char *filename, int fileline)
3033 prog->error_cmd("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", prog->name, n, filename, fileline);
3037 #define PRVM_KNOWNSTRINGBASE 0x40000000
3039 const char *PRVM_GetString(prvm_prog_t *prog, int num)
3044 if (prvm_stringdebug.integer)
3045 VM_Warning(prog, "PRVM_GetString: Invalid string offset (%i < 0)\n", num);
3048 else if (num < prog->stringssize)
3050 // constant string from progs.dat
3051 return prog->strings + num;
3053 else if (num <= prog->stringssize + prog->tempstringsbuf.maxsize)
3055 // tempstring returned by engine to QC (becomes invalid after returning to engine)
3056 num -= prog->stringssize;
3057 if (num < prog->tempstringsbuf.cursize)
3058 return (char *)prog->tempstringsbuf.data + num;
3061 if (prvm_stringdebug.integer)
3062 VM_Warning(prog, "PRVM_GetString: Invalid temp-string offset (%i >= %i prog->tempstringsbuf.cursize)\n", num, prog->tempstringsbuf.cursize);
3066 else if (num & PRVM_KNOWNSTRINGBASE)
3069 num = num - PRVM_KNOWNSTRINGBASE;
3070 if (num >= 0 && num < prog->numknownstrings)
3072 if (!prog->knownstrings[num])
3074 if (prvm_stringdebug.integer)
3075 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
3078 // refresh the garbage collection on the string - this guards
3079 // against a certain sort of repeated migration to earlier
3080 // points in the scan that could otherwise result in the string
3081 // being freed for being unused
3082 prog->knownstrings_flags[num] = (prog->knownstrings_flags[num] & ~KNOWNSTRINGFLAG_GCPRUNE) | KNOWNSTRINGFLAG_GCMARK;
3083 return prog->knownstrings[num];
3087 if (prvm_stringdebug.integer)
3088 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
3094 // invalid string offset
3095 if (prvm_stringdebug.integer)
3096 VM_Warning(prog, "PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
3101 const char *PRVM_ChangeEngineString(prvm_prog_t *prog, int i, const char *s)
3104 i = i - PRVM_KNOWNSTRINGBASE;
3105 if (i < 0 || i >= prog->numknownstrings)
3106 prog->error_cmd("PRVM_ChangeEngineString: string index %i is out of bounds", i);
3107 else if ((prog->knownstrings_flags[i] & KNOWNSTRINGFLAG_ENGINE) == 0)
3108 prog->error_cmd("PRVM_ChangeEngineString: string index %i is not an engine string", i);
3109 old = prog->knownstrings[i];
3110 prog->knownstrings[i] = s;
3114 static void PRVM_NewKnownString(prvm_prog_t *prog, int i, int flags, const char *s)
3116 if (i >= prog->numknownstrings)
3118 if (i >= prog->maxknownstrings)
3120 const char **oldstrings = prog->knownstrings;
3121 const unsigned char *oldstrings_flags = prog->knownstrings_flags;
3122 const char **oldstrings_origin = prog->knownstrings_origin;
3123 prog->maxknownstrings += 128;
3124 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3125 prog->knownstrings_flags = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
3126 if (prog->leaktest_active)
3127 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3128 if (prog->numknownstrings)
3130 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
3131 memcpy((char **)prog->knownstrings_flags, oldstrings_flags, prog->numknownstrings * sizeof(unsigned char));
3132 if (prog->leaktest_active)
3133 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
3136 prog->numknownstrings++;
3138 prog->firstfreeknownstring = i + 1;
3139 prog->knownstrings[i] = s;
3140 // it's in use right now, spare it until the next gc pass - that said, it is not freeable so this is probably moot
3141 prog->knownstrings_flags[i] = flags;
3142 if (prog->leaktest_active)
3143 prog->knownstrings_origin[i] = NULL;
3146 int PRVM_SetEngineString(prvm_prog_t *prog, const char *s)
3151 if (s >= prog->strings && s <= prog->strings + prog->stringssize)
3152 prog->error_cmd("PRVM_SetEngineString: s in prog->strings area");
3153 // if it's in the tempstrings area, use a reserved range
3154 // (otherwise we'd get millions of useless string offsets cluttering the database)
3155 if (s >= (char *)prog->tempstringsbuf.data && s < (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.maxsize)
3156 return prog->stringssize + (s - (char *)prog->tempstringsbuf.data);
3157 // see if it's a known string address
3158 for (i = 0;i < prog->numknownstrings;i++)
3159 if (prog->knownstrings[i] == s)
3160 return PRVM_KNOWNSTRINGBASE + i;
3161 // new unknown engine string
3162 if (developer_insane.integer)
3163 Con_DPrintf("new engine string %p = \"%s\"\n", (void *)s, s);
3164 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3165 if (!prog->knownstrings[i])
3167 PRVM_NewKnownString(prog, i, KNOWNSTRINGFLAG_GCMARK | KNOWNSTRINGFLAG_ENGINE, s);
3168 return PRVM_KNOWNSTRINGBASE + i;
3171 // temp string handling
3173 // all tempstrings go into this buffer consecutively, and it is reset
3174 // whenever PRVM_ExecuteProgram returns to the engine
3175 // (technically each PRVM_ExecuteProgram call saves the cursize value and
3176 // restores it on return, so multiple recursive calls can share the same
3178 // the buffer size is automatically grown as needed
3180 int PRVM_SetTempString(prvm_prog_t *prog, const char *s)
3186 size = (int)strlen(s) + 1;
3187 if (developer_insane.integer)
3188 Con_DPrintf("PRVM_SetTempString: cursize %i, size %i\n", prog->tempstringsbuf.cursize, size);
3189 if (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
3191 sizebuf_t old = prog->tempstringsbuf;
3192 if (prog->tempstringsbuf.cursize + size >= 1<<28)
3193 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);
3194 prog->tempstringsbuf.maxsize = max(prog->tempstringsbuf.maxsize, 65536);
3195 while (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
3196 prog->tempstringsbuf.maxsize *= 2;
3197 if (prog->tempstringsbuf.maxsize != old.maxsize || prog->tempstringsbuf.data == NULL)
3199 Con_DPrintf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, prog->tempstringsbuf.maxsize/1024);
3200 prog->tempstringsbuf.data = (unsigned char *) Mem_Alloc(prog->progs_mempool, prog->tempstringsbuf.maxsize);
3204 memcpy(prog->tempstringsbuf.data, old.data, old.cursize);
3209 t = (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.cursize;
3211 prog->tempstringsbuf.cursize += size;
3212 return PRVM_SetEngineString(prog, t);
3215 int PRVM_AllocString(prvm_prog_t *prog, size_t bufferlength, char **pointer)
3225 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3226 if (!prog->knownstrings[i])
3228 s = (char *)PRVM_Alloc(bufferlength);
3229 PRVM_NewKnownString(prog, i, KNOWNSTRINGFLAG_GCMARK, s);
3230 if(prog->leaktest_active)
3231 prog->knownstrings_origin[i] = PRVM_AllocationOrigin(prog);
3233 *pointer = (char *)(prog->knownstrings[i]);
3234 return PRVM_KNOWNSTRINGBASE + i;
3237 void PRVM_FreeString(prvm_prog_t *prog, int num)
3240 prog->error_cmd("PRVM_FreeString: attempt to free a NULL string");
3241 else if (num >= 0 && num < prog->stringssize)
3242 prog->error_cmd("PRVM_FreeString: attempt to free a constant string");
3243 else if (num >= PRVM_KNOWNSTRINGBASE && num < PRVM_KNOWNSTRINGBASE + prog->numknownstrings)
3245 num = num - PRVM_KNOWNSTRINGBASE;
3246 if (!prog->knownstrings[num])
3247 prog->error_cmd("PRVM_FreeString: attempt to free a non-existent or already freed string");
3248 if (!prog->knownstrings_flags[num])
3249 prog->error_cmd("PRVM_FreeString: attempt to free a string owned by the engine");
3250 PRVM_Free((char *)prog->knownstrings[num]);
3251 if(prog->leaktest_active)
3252 if(prog->knownstrings_origin[num])
3253 PRVM_Free((char *)prog->knownstrings_origin[num]);
3254 prog->knownstrings[num] = NULL;
3255 prog->knownstrings_flags[num] = 0;
3256 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
3259 prog->error_cmd("PRVM_FreeString: invalid string offset %i", num);
3262 static qboolean PRVM_IsStringReferenced(prvm_prog_t *prog, string_t string)
3266 for (i = 0;i < prog->numglobaldefs;i++)
3268 ddef_t *d = &prog->globaldefs[i];
3269 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3271 if(string == PRVM_GLOBALFIELDSTRING(d->ofs))
3275 for(j = 0; j < prog->num_edicts; ++j)
3277 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3278 if (ed->priv.required->free)
3280 for (i=0; i<prog->numfielddefs; ++i)
3282 ddef_t *d = &prog->fielddefs[i];
3283 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3285 if(string == PRVM_EDICTFIELDSTRING(ed, d->ofs))
3293 static qboolean PRVM_IsEdictRelevant(prvm_prog_t *prog, prvm_edict_t *edict)
3297 if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
3298 return true; // world or clients
3299 if (edict->priv.required->freetime <= prog->inittime)
3300 return true; // created during startup
3301 if (prog == SVVM_prog)
3303 if(PRVM_serveredictfloat(edict, solid)) // can block other stuff, or is a trigger?
3305 if(PRVM_serveredictfloat(edict, modelindex)) // visible ent?
3307 if(PRVM_serveredictfloat(edict, effects)) // particle effect?
3309 if(PRVM_serveredictfunction(edict, think)) // has a think function?
3310 if(PRVM_serveredictfloat(edict, nextthink) > 0) // that actually will eventually run?
3312 if(PRVM_serveredictfloat(edict, takedamage))
3314 if(*prvm_leaktest_ignore_classnames.string)
3316 if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_serveredictstring(edict, classname)))))
3320 else if (prog == CLVM_prog)
3322 // TODO someone add more stuff here
3323 if(PRVM_clientedictfloat(edict, entnum)) // csqc networked
3325 if(PRVM_clientedictfloat(edict, modelindex)) // visible ent?
3327 if(PRVM_clientedictfloat(edict, effects)) // particle effect?
3329 if(PRVM_clientedictfunction(edict, think)) // has a think function?
3330 if(PRVM_clientedictfloat(edict, nextthink) > 0) // that actually will eventually run?
3332 if(*prvm_leaktest_ignore_classnames.string)
3334 if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_clientedictstring(edict, classname)))))
3340 // menu prog does not have classnames
3345 static qboolean PRVM_IsEdictReferenced(prvm_prog_t *prog, prvm_edict_t *edict, int mark)
3348 int edictnum = PRVM_NUM_FOR_EDICT(edict);
3349 const char *targetname = NULL;
3351 if (prog == SVVM_prog && prvm_leaktest_follow_targetname.integer)
3352 targetname = PRVM_GetString(prog, PRVM_serveredictstring(edict, targetname));
3355 if(!*targetname) // ""
3358 for(j = 0; j < prog->num_edicts; ++j)
3360 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3361 if (ed->priv.required->mark < mark)
3367 const char *target = PRVM_GetString(prog, PRVM_serveredictstring(ed, target));
3369 if(!strcmp(target, targetname))
3372 for (i=0; i<prog->numfielddefs; ++i)
3374 ddef_t *d = &prog->fielddefs[i];
3375 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3377 if(edictnum == PRVM_EDICTFIELDEDICT(ed, d->ofs))
3385 static void PRVM_MarkReferencedEdicts(prvm_prog_t *prog)
3391 // Stage 1: world, all entities that are relevant, and all entities that are referenced by globals.
3393 for(j = 0; j < prog->num_edicts; ++j)
3395 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3396 if(ed->priv.required->free)
3398 ed->priv.required->mark = PRVM_IsEdictRelevant(prog, ed) ? stage : 0;
3400 for (i = 0;i < prog->numglobaldefs;i++)
3402 ddef_t *d = &prog->globaldefs[i];
3404 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3406 j = PRVM_GLOBALFIELDEDICT(d->ofs);
3407 if (i < 0 || j >= prog->max_edicts) {
3408 Con_Printf("Invalid entity reference from global %s.\n", PRVM_GetString(prog, d->s_name));
3411 ed = PRVM_EDICT_NUM(j);;
3412 ed->priv.required->mark = stage;
3415 // Future stages: all entities that are referenced by an entity of the previous stage.
3419 for(j = 0; j < prog->num_edicts; ++j)
3421 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3422 if(ed->priv.required->free)
3424 if(ed->priv.required->mark)
3426 if(PRVM_IsEdictReferenced(prog, ed, stage))
3428 ed->priv.required->mark = stage + 1;
3435 Con_DPrintf("leak check used %d stages to find all references\n", stage);
3438 void PRVM_LeakTest(prvm_prog_t *prog)
3441 qboolean leaked = false;
3443 if(!prog->leaktest_active)
3447 for (i = 0; i < prog->numknownstrings; ++i)
3449 if(prog->knownstrings[i])
3450 if(prog->knownstrings_flags[i])
3451 if(prog->knownstrings_origin[i])
3452 if(!PRVM_IsStringReferenced(prog, PRVM_KNOWNSTRINGBASE + i))
3454 Con_Printf("Unreferenced string found!\n Value: %s\n Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
3460 PRVM_MarkReferencedEdicts(prog);
3461 for(j = 0; j < prog->num_edicts; ++j)
3463 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3464 if(ed->priv.required->free)
3466 if(!ed->priv.required->mark)
3467 if(ed->priv.required->allocation_origin)
3469 Con_Printf("Unreferenced edict found!\n Allocated at: %s\n", ed->priv.required->allocation_origin);
3470 PRVM_ED_Print(prog, ed, NULL);
3475 ed->priv.required->mark = 0; // clear marks again when done
3478 for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
3480 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
3482 if(stringbuffer->origin)
3484 Con_Printf("Open string buffer handle found!\n Allocated at: %s\n", stringbuffer->origin);
3489 for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
3491 if(prog->openfiles[i])
3492 if(prog->openfiles_origin[i])
3494 Con_Printf("Open file handle found!\n Allocated at: %s\n", prog->openfiles_origin[i]);
3499 for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
3501 if(prog->opensearches[i])
3502 if(prog->opensearches_origin[i])
3504 Con_Printf("Open search handle found!\n Allocated at: %s\n", prog->opensearches_origin[i]);
3510 Con_Printf("Congratulations. No leaks found.\n");
3513 void PRVM_GarbageCollection(prvm_prog_t *prog)
3515 int limit = prvm_garbagecollection_scan_limit.integer;
3516 prvm_prog_garbagecollection_state_t *gc = &prog->gc;
3517 if (!prvm_garbagecollection_enable.integer)
3520 // we like to limit how much scanning we do so it doesn't put a significant
3521 // burden on the cpu, so each of these are not complete scans, we also like
3522 // to have consistent cpu usage so we do a bit of work on each category of
3523 // leaked object every frame
3529 case PRVM_GC_GLOBALS_MARK:
3530 for (; gc->globals_mark_progress < prog->numglobaldefs && (limit--) > 0; gc->globals_mark_progress++)
3532 ddef_t *d = &prog->globaldefs[gc->globals_mark_progress];
3537 prvm_int_t s = prog->globals.ip[d->ofs];
3538 if (s & PRVM_KNOWNSTRINGBASE)
3540 prvm_int_t num = s - PRVM_KNOWNSTRINGBASE;
3541 if (!prog->knownstrings[num])
3544 Con_DPrintf("PRVM_GarbageCollection: Found bogus strzone reference in global %i (global name: \"%s\"), erasing reference", d->ofs, PRVM_GetString(prog, d->s_name));
3545 prog->globals.ip[d->ofs] = 0;
3548 prog->knownstrings_flags[num] = (prog->knownstrings_flags[num] | KNOWNSTRINGFLAG_GCMARK) & ~KNOWNSTRINGFLAG_GCPRUNE;
3556 if (gc->globals_mark_progress >= prog->numglobaldefs)
3559 case PRVM_GC_FIELDS_MARK:
3560 for (; gc->fields_mark_progress < prog->numfielddefs && limit > 0;)
3562 ddef_t *d = &prog->fielddefs[gc->fields_mark_progress];
3566 //for (gc-> entityindex = 0; entityindex < prog->num_edicts; entityindex++)
3567 for (;gc->fields_mark_progress_entity < prog->num_edicts && (limit--) > 0;gc->fields_mark_progress_entity++)
3569 int entityindex = gc->fields_mark_progress_entity;
3570 prvm_int_t s = prog->edictsfields.ip[entityindex * prog->entityfields + d->ofs];
3571 if (s & PRVM_KNOWNSTRINGBASE)
3573 prvm_int_t num = s - PRVM_KNOWNSTRINGBASE;
3574 if (!prog->knownstrings[num])
3577 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));
3578 prog->edictsfields.ip[entityindex * prog->entityfields + d->ofs] = 0;
3581 prog->knownstrings_flags[num] = (prog->knownstrings_flags[num] | KNOWNSTRINGFLAG_GCMARK) & ~KNOWNSTRINGFLAG_GCPRUNE;
3584 if (gc->fields_mark_progress_entity >= prog->num_edicts)
3586 gc->fields_mark_progress_entity = 0;
3587 gc->fields_mark_progress++;
3591 gc->fields_mark_progress_entity = 0;
3592 gc->fields_mark_progress++;
3596 if (gc->fields_mark_progress >= prog->numfielddefs)
3599 case PRVM_GC_KNOWNSTRINGS_SWEEP:
3600 // free any strzone'd strings that are not marked
3601 if (!prvm_garbagecollection_strings.integer)
3606 for (;gc->knownstrings_sweep_progress < prog->numknownstrings && (limit--) > 0;gc->knownstrings_sweep_progress++)
3608 int num = gc->knownstrings_sweep_progress;
3609 if (prog->knownstrings[num] && (prog->knownstrings_flags[num] & (KNOWNSTRINGFLAG_GCMARK | KNOWNSTRINGFLAG_ENGINE)) == 0)
3611 if (prog->knownstrings_flags[num] & KNOWNSTRINGFLAG_GCPRUNE)
3613 // string has been marked for pruning two passes in a row
3614 if (prvm_garbagecollection_notify.integer)
3615 Con_DPrintf("prvm_garbagecollection_notify: %s: freeing unreferenced string %i: \"%s\"\n", prog->name, num, prog->knownstrings[num]);
3616 Mem_Free((char *)prog->knownstrings[num]);
3617 prog->knownstrings[num] = NULL;
3618 prog->knownstrings_flags[num] = 0;
3619 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
3623 // mark it for pruning next pass
3624 prog->knownstrings_flags[num] |= KNOWNSTRINGFLAG_GCPRUNE;
3628 if (gc->knownstrings_sweep_progress >= prog->numknownstrings)
3633 memset(gc, 0, sizeof(*gc));