2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26 prvm_prog_t prvm_prog_list[PRVM_PROG_MAX];
28 int prvm_type_size[8] = {1,sizeof(string_t)/4,1,3,1,1,sizeof(func_t)/4,sizeof(void *)/4};
30 prvm_eval_t prvm_badvalue; // used only for error returns
32 cvar_t prvm_language = {CVAR_CLIENT | CVAR_SERVER | CVAR_SAVE, "prvm_language", "", "when set, loads PROGSFILE.LANGUAGENAME.po and common.LANGUAGENAME.po for string translations; when set to dump, PROGSFILE.pot is written from the strings in the progs"};
33 // LadyHavoc: prints every opcode as it executes - warning: this is significant spew
34 cvar_t prvm_traceqc = {CVAR_CLIENT | CVAR_SERVER, "prvm_traceqc", "0", "prints every QuakeC statement as it is executed (only for really thorough debugging!)"};
35 // LadyHavoc: counts usage of each QuakeC statement
36 cvar_t prvm_statementprofiling = {CVAR_CLIENT | CVAR_SERVER, "prvm_statementprofiling", "0", "counts how many times each QuakeC statement has been executed, these counts are displayed in prvm_printfunction output (if enabled)"};
37 cvar_t prvm_timeprofiling = {CVAR_CLIENT | CVAR_SERVER, "prvm_timeprofiling", "0", "counts how long each function has been executed, these counts are displayed in prvm_profile output (if enabled)"};
38 cvar_t prvm_coverage = {CVAR_CLIENT | CVAR_SERVER, "prvm_coverage", "0", "report and count coverage events (1: per-function, 2: coverage() builtin, 4: per-statement)"};
39 cvar_t prvm_backtraceforwarnings = {CVAR_CLIENT | CVAR_SERVER, "prvm_backtraceforwarnings", "0", "print a backtrace for warnings too"};
40 cvar_t prvm_leaktest = {CVAR_CLIENT | CVAR_SERVER, "prvm_leaktest", "0", "try to detect memory leaks in strings or entities"};
41 cvar_t prvm_leaktest_follow_targetname = {CVAR_CLIENT | CVAR_SERVER, "prvm_leaktest_follow_targetname", "0", "if set, target/targetname links are considered when leak testing; this should normally not be required, as entities created during startup - e.g. info_notnull - are never considered leaky"};
42 cvar_t prvm_leaktest_ignore_classnames = {CVAR_CLIENT | CVAR_SERVER, "prvm_leaktest_ignore_classnames", "", "classnames of entities to NOT leak check because they are found by find(world, classname, ...) but are actually spawned by QC code (NOT map entities)"};
43 cvar_t prvm_errordump = {CVAR_CLIENT | CVAR_SERVER, "prvm_errordump", "0", "write a savegame on crash to crash-server.dmp"};
44 cvar_t prvm_breakpointdump = {CVAR_CLIENT | CVAR_SERVER, "prvm_breakpointdump", "0", "write a savegame on breakpoint to breakpoint-server.dmp"};
45 cvar_t prvm_reuseedicts_startuptime = {CVAR_CLIENT | CVAR_SERVER, "prvm_reuseedicts_startuptime", "2", "allows immediate re-use of freed entity slots during start of new level (value in seconds)"};
46 cvar_t prvm_reuseedicts_neverinsameframe = {CVAR_CLIENT | CVAR_SERVER, "prvm_reuseedicts_neverinsameframe", "1", "never allows re-use of freed entity slots during same frame"};
47 cvar_t prvm_garbagecollection_enable = {CVAR_CLIENT | CVAR_SERVER, "prvm_garbagecollection_enable", "1", "automatically scan for and free resources that are not referenced by the code being executed in the VM"};
48 cvar_t prvm_garbagecollection_notify = {CVAR_CLIENT | CVAR_SERVER, "prvm_garbagecollection_notify", "0", "print out a notification for each resource freed by garbage collection"};
49 cvar_t prvm_garbagecollection_scan_limit = {CVAR_CLIENT | CVAR_SERVER, "prvm_garbagecollection_scan_limit", "10000", "scan this many fields or resources per frame to free up unreferenced resources"};
50 cvar_t prvm_garbagecollection_strings = {CVAR_CLIENT | CVAR_SERVER, "prvm_garbagecollection_strings", "1", "automatically call strunzone() on strings that are not referenced"};
51 cvar_t prvm_stringdebug = {CVAR_CLIENT | CVAR_SERVER, "prvm_stringdebug", "0", "Print debug and warning messages related to strings"};
53 static double prvm_reuseedicts_always_allow = 0;
54 qboolean prvm_runawaycheck = true;
56 //============================================================================
64 static void PRVM_MEM_Alloc(prvm_prog_t *prog)
68 // reserve space for the null entity aka world
69 // check bound of max_edicts
70 prog->max_edicts = bound(1 + prog->reserved_edicts, prog->max_edicts, prog->limit_edicts);
71 prog->num_edicts = bound(1 + prog->reserved_edicts, prog->num_edicts, prog->max_edicts);
73 // edictprivate_size has to be min as big prvm_edict_private_t
74 prog->edictprivate_size = max(prog->edictprivate_size,(int)sizeof(prvm_edict_private_t));
77 prog->edicts = (prvm_edict_t *)Mem_Alloc(prog->progs_mempool,prog->limit_edicts * sizeof(prvm_edict_t));
79 // alloc edict private space
80 prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size);
83 prog->entityfieldsarea = prog->entityfields * prog->max_edicts;
84 prog->edictsfields.fp = (prvm_vec_t *)Mem_Alloc(prog->progs_mempool, prog->entityfieldsarea * sizeof(prvm_vec_t));
87 for(i = 0; i < prog->max_edicts; i++)
89 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
90 prog->edicts[i].fields.fp = prog->edictsfields.fp + i * prog->entityfields;
96 PRVM_MEM_IncreaseEdicts
99 void PRVM_MEM_IncreaseEdicts(prvm_prog_t *prog)
103 if(prog->max_edicts >= prog->limit_edicts)
106 prog->begin_increase_edicts(prog);
109 prog->max_edicts = min(prog->max_edicts + 256, prog->limit_edicts);
111 prog->entityfieldsarea = prog->entityfields * prog->max_edicts;
112 prog->edictsfields.fp = (prvm_vec_t*)Mem_Realloc(prog->progs_mempool, (void *)prog->edictsfields.fp, prog->entityfieldsarea * sizeof(prvm_vec_t));
113 prog->edictprivate = (void *)Mem_Realloc(prog->progs_mempool, (void *)prog->edictprivate, prog->max_edicts * prog->edictprivate_size);
115 //set e and v pointers
116 for(i = 0; i < prog->max_edicts; i++)
118 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
119 prog->edicts[i].fields.fp = prog->edictsfields.fp + i * prog->entityfields;
122 prog->end_increase_edicts(prog);
125 //============================================================================
128 int PRVM_ED_FindFieldOffset(prvm_prog_t *prog, const char *field)
131 d = PRVM_ED_FindField(prog, field);
137 int PRVM_ED_FindGlobalOffset(prvm_prog_t *prog, const char *global)
140 d = PRVM_ED_FindGlobal(prog, global);
146 func_t PRVM_ED_FindFunctionOffset(prvm_prog_t *prog, const char *function)
149 f = PRVM_ED_FindFunction(prog, function);
152 return (func_t)(f - prog->functions);
160 prvm_prog_t *PRVM_ProgFromString(const char *str)
162 if (!strcmp(str, "server"))
164 if (!strcmp(str, "client"))
167 if (!strcmp(str, "menu"))
175 PRVM_FriendlyProgFromString
178 prvm_prog_t *PRVM_FriendlyProgFromString(const char *str)
180 prvm_prog_t *prog = PRVM_ProgFromString(str);
183 Con_Printf("%s: unknown program name\n", str);
188 Con_Printf("%s: program is not loaded\n", str);
198 Sets everything to NULL.
200 Nota bene: this also marks the entity as allocated if it has been previously
201 freed and sets the allocation origin.
204 void PRVM_ED_ClearEdict(prvm_prog_t *prog, prvm_edict_t *e)
206 memset(e->fields.fp, 0, prog->entityfields * sizeof(prvm_vec_t));
207 e->priv.required->free = false;
208 e->priv.required->freetime = host.realtime;
209 if(e->priv.required->allocation_origin)
210 Mem_Free((char *)e->priv.required->allocation_origin);
211 e->priv.required->allocation_origin = PRVM_AllocationOrigin(prog);
213 // AK: Let the init_edict function determine if something needs to be initialized
214 prog->init_edict(prog, e);
217 const char *PRVM_AllocationOrigin(prvm_prog_t *prog)
220 if(prog->leaktest_active)
221 if(prog->depth > 0) // actually in QC code and not just parsing the entities block of a map/savegame
223 buf = (char *)PRVM_Alloc(256);
224 PRVM_ShortStackTrace(prog, buf, 256);
233 Returns if this particular edict could get allocated by PRVM_ED_Alloc
236 qboolean PRVM_ED_CanAlloc(prvm_prog_t *prog, prvm_edict_t *e)
238 if(!e->priv.required->free)
240 if(prvm_reuseedicts_always_allow == host.realtime)
242 if(host.realtime <= e->priv.required->freetime + 0.1 && prvm_reuseedicts_neverinsameframe.integer)
243 return false; // never allow reuse in same frame (causes networking trouble)
244 if(e->priv.required->freetime < prog->starttime + prvm_reuseedicts_startuptime.value)
246 if(host.realtime > e->priv.required->freetime + 1)
248 return false; // entity slot still blocked because the entity was freed less than one second ago
255 Either finds a free edict, or allocates a new one.
256 Try to avoid reusing an entity that was recently freed, because it
257 can cause the client to think the entity morphed into something else
258 instead of being removed and recreated, which can cause interpolated
259 angles and bad trails.
262 prvm_edict_t *PRVM_ED_Alloc(prvm_prog_t *prog)
267 // the client qc dont need maxclients
268 // thus it doesnt need to use svs.maxclients
269 // AK: changed i=svs.maxclients+1
270 // AK: changed so the edict 0 wont spawn -> used as reserved/world entity
271 // although the menu/client has no world
272 for (i = prog->reserved_edicts + 1;i < prog->num_edicts;i++)
274 e = PRVM_EDICT_NUM(i);
275 if(PRVM_ED_CanAlloc(prog, e))
277 PRVM_ED_ClearEdict (prog, e);
282 if (i == prog->limit_edicts)
283 prog->error_cmd("%s: PRVM_ED_Alloc: no free edicts", prog->name);
286 if (prog->num_edicts >= prog->max_edicts)
287 PRVM_MEM_IncreaseEdicts(prog);
289 e = PRVM_EDICT_NUM(i);
291 PRVM_ED_ClearEdict(prog, e);
299 Marks the edict as free
300 FIXME: walk all entities and NULL out references to this entity
303 void PRVM_ED_Free(prvm_prog_t *prog, prvm_edict_t *ed)
305 // dont delete the null entity (world) or reserved edicts
306 if (ed - prog->edicts <= prog->reserved_edicts)
309 prog->free_edict(prog, ed);
311 ed->priv.required->free = true;
312 ed->priv.required->freetime = host.realtime;
313 if(ed->priv.required->allocation_origin)
315 Mem_Free((char *)ed->priv.required->allocation_origin);
316 ed->priv.required->allocation_origin = NULL;
320 //===========================================================================
327 static ddef_t *PRVM_ED_GlobalAtOfs (prvm_prog_t *prog, int ofs)
332 for (i = 0;i < prog->numglobaldefs;i++)
334 def = &prog->globaldefs[i];
346 ddef_t *PRVM_ED_FieldAtOfs (prvm_prog_t *prog, int ofs)
351 for (i = 0;i < prog->numfielddefs;i++)
353 def = &prog->fielddefs[i];
365 ddef_t *PRVM_ED_FindField (prvm_prog_t *prog, const char *name)
370 for (i = 0;i < prog->numfielddefs;i++)
372 def = &prog->fielddefs[i];
373 if (!strcmp(PRVM_GetString(prog, def->s_name), name))
384 ddef_t *PRVM_ED_FindGlobal (prvm_prog_t *prog, const char *name)
389 for (i = 0;i < prog->numglobaldefs;i++)
391 def = &prog->globaldefs[i];
392 if (!strcmp(PRVM_GetString(prog, def->s_name), name))
404 mfunction_t *PRVM_ED_FindFunction (prvm_prog_t *prog, const char *name)
409 for (i = 0;i < prog->numfunctions;i++)
411 func = &prog->functions[i];
412 if (!strcmp(PRVM_GetString(prog, func->s_name), name))
423 Returns a string describing *data in a type specific manner
426 static char *PRVM_ValueString (prvm_prog_t *prog, etype_t type, prvm_eval_t *val, char *line, size_t linelength)
432 type = (etype_t)((int) type & ~DEF_SAVEGLOBAL);
437 strlcpy (line, PRVM_GetString (prog, val->string), linelength);
441 if (n < 0 || n >= prog->max_edicts)
442 dpsnprintf (line, linelength, "entity %i (invalid!)", n);
444 dpsnprintf (line, linelength, "entity %i", n);
447 if ((unsigned int)val->function < (unsigned int)prog->progs_numfunctions)
449 f = prog->functions + val->function;
450 dpsnprintf (line, linelength, "%s()", PRVM_GetString(prog, f->s_name));
453 dpsnprintf (line, linelength, "function%lli() (invalid!)", val->function);
456 def = PRVM_ED_FieldAtOfs ( prog, val->_int );
458 dpsnprintf (line, linelength, ".%s", PRVM_GetString(prog, def->s_name));
460 dpsnprintf (line, linelength, "field%lli (invalid!)", val->_int );
463 dpsnprintf (line, linelength, "void");
466 // LadyHavoc: changed from %5.1f to %10.4f
467 dpsnprintf (line, linelength, FLOAT_LOSSLESS_FORMAT, val->_float);
470 // LadyHavoc: changed from %5.1f to %10.4f
471 dpsnprintf (line, linelength, "'" VECTOR_LOSSLESS_FORMAT "'", val->vector[0], val->vector[1], val->vector[2]);
474 dpsnprintf (line, linelength, "pointer");
477 dpsnprintf (line, linelength, "bad type %i", (int) type);
488 Returns a string describing *data in a type specific manner
489 Easier to parse than PR_ValueString
492 char *PRVM_UglyValueString (prvm_prog_t *prog, etype_t type, prvm_eval_t *val, char *line, size_t linelength)
499 type = (etype_t)((int)type & ~DEF_SAVEGLOBAL);
504 // Parse the string a bit to turn special characters
505 // (like newline, specifically) into escape codes,
506 // this fixes saving games from various mods
507 s = PRVM_GetString (prog, val->string);
508 for (i = 0;i < (int)linelength - 2 && *s;)
538 dpsnprintf (line, linelength, "%i", i);
541 if ((unsigned int)val->function < (unsigned int)prog->progs_numfunctions)
543 f = prog->functions + val->function;
544 strlcpy (line, PRVM_GetString (prog, f->s_name), linelength);
547 dpsnprintf (line, linelength, "bad function %lli (invalid!)", val->function);
550 def = PRVM_ED_FieldAtOfs ( prog, val->_int );
552 dpsnprintf (line, linelength, ".%s", PRVM_GetString(prog, def->s_name));
554 dpsnprintf (line, linelength, "field%lli (invalid!)", val->_int );
557 dpsnprintf (line, linelength, "void");
560 dpsnprintf (line, linelength, FLOAT_LOSSLESS_FORMAT, val->_float);
563 dpsnprintf (line, linelength, VECTOR_LOSSLESS_FORMAT, val->vector[0], val->vector[1], val->vector[2]);
566 dpsnprintf (line, linelength, "bad type %i", type);
577 Returns a string with a description and the contents of a global,
578 padded to 20 field width
581 char *PRVM_GlobalString (prvm_prog_t *prog, int ofs, char *line, size_t linelength)
587 char valuebuf[MAX_INPUTLINE];
589 val = (prvm_eval_t *)&prog->globals.fp[ofs];
590 def = PRVM_ED_GlobalAtOfs(prog, ofs);
592 dpsnprintf (line, linelength, "GLOBAL%i", ofs);
595 s = PRVM_ValueString (prog, (etype_t)def->type, val, valuebuf, sizeof(valuebuf));
596 dpsnprintf (line, linelength, "%s (=%s)", PRVM_GetString(prog, def->s_name), s);
600 //for ( ; i<20 ; i++)
601 // strcat (line," ");
607 char *PRVM_GlobalStringNoContents (prvm_prog_t *prog, int ofs, char *line, size_t linelength)
612 def = PRVM_ED_GlobalAtOfs(prog, ofs);
614 dpsnprintf (line, linelength, "GLOBAL%i", ofs);
616 dpsnprintf (line, linelength, "%s", PRVM_GetString(prog, def->s_name));
619 //for ( ; i<20 ; i++)
620 // strcat (line," ");
634 // LadyHavoc: optimized this to print out much more quickly (tempstring)
635 // LadyHavoc: changed to print out every 4096 characters (incase there are a lot of fields to print)
636 void PRVM_ED_Print(prvm_prog_t *prog, prvm_edict_t *ed, const char *wildcard_fieldname)
644 char tempstring[MAX_INPUTLINE], tempstring2[260]; // temporary string buffers
645 char valuebuf[MAX_INPUTLINE];
647 if (ed->priv.required->free)
649 Con_Printf("%s: FREE\n",prog->name);
654 dpsnprintf(tempstring, sizeof(tempstring), "\n%s EDICT %i:\n", prog->name, PRVM_NUM_FOR_EDICT(ed));
655 for (i = 1;i < prog->numfielddefs;i++)
657 d = &prog->fielddefs[i];
658 name = PRVM_GetString(prog, d->s_name);
659 if(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
660 continue; // skip _x, _y, _z vars
662 // Check Field Name Wildcard
663 if(wildcard_fieldname)
664 if( !matchpattern(name, wildcard_fieldname, 1) )
665 // Didn't match; skip
668 val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
670 // if the value is still all 0, skip the field
671 type = d->type & ~DEF_SAVEGLOBAL;
673 for (j=0 ; j<prvm_type_size[type] ; j++)
676 if (j == prvm_type_size[type])
679 if (strlen(name) > sizeof(tempstring2)-4)
681 memcpy (tempstring2, name, sizeof(tempstring2)-4);
682 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
683 tempstring2[sizeof(tempstring2)-1] = 0;
686 strlcat(tempstring, name, sizeof(tempstring));
687 for (l = strlen(name);l < 14;l++)
688 strlcat(tempstring, " ", sizeof(tempstring));
689 strlcat(tempstring, " ", sizeof(tempstring));
691 name = PRVM_ValueString(prog, (etype_t)d->type, val, valuebuf, sizeof(valuebuf));
692 if (strlen(name) > sizeof(tempstring2)-4)
694 memcpy (tempstring2, name, sizeof(tempstring2)-4);
695 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
696 tempstring2[sizeof(tempstring2)-1] = 0;
699 strlcat(tempstring, name, sizeof(tempstring));
700 strlcat(tempstring, "\n", sizeof(tempstring));
701 if (strlen(tempstring) >= sizeof(tempstring)/2)
703 Con_Print(tempstring);
708 Con_Print(tempstring);
718 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)
1370 int parsed, inhibited, spawned, died;
1371 const char *funcname;
1380 prvm_reuseedicts_always_allow = host.realtime;
1385 // parse the opening brace
1386 if (!COM_ParseToken_Simple(&data, false, false, true))
1388 if (com_token[0] != '{')
1389 prog->error_cmd("PRVM_ED_LoadFromFile: %s: found %s when expecting {", prog->name, com_token);
1391 // CHANGED: this is not conform to PR_LoadFromFile
1392 if(prog->loadintoworld)
1394 prog->loadintoworld = false;
1395 ent = PRVM_EDICT_NUM(0);
1398 ent = PRVM_ED_Alloc(prog);
1401 if (ent != prog->edicts) // hack
1402 memset (ent->fields.fp, 0, prog->entityfields * sizeof(prvm_vec_t));
1404 data = PRVM_ED_ParseEdict (prog, data, ent);
1407 // remove the entity ?
1408 if(!prog->load_edict(prog, ent))
1410 PRVM_ED_Free(prog, ent);
1415 if (PRVM_serverfunction(SV_OnEntityPreSpawnFunction))
1418 PRVM_serverglobalfloat(time) = sv.time;
1419 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1420 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityPreSpawnFunction), "QC function SV_OnEntityPreSpawnFunction is missing");
1423 if(ent->priv.required->free)
1430 // immediately call spawn function, but only if there is a self global and a classname
1432 if(!ent->priv.required->free)
1434 if (!PRVM_alledictstring(ent, classname))
1436 Con_Print("No classname for:\n");
1437 PRVM_ED_Print(prog, ent, NULL);
1438 PRVM_ED_Free (prog, ent);
1442 // look for the spawn function
1443 funcname = PRVM_GetString(prog, PRVM_alledictstring(ent, classname));
1444 func = PRVM_ED_FindFunction (prog, va(vabuf, sizeof(vabuf), "spawnfunc_%s", funcname));
1446 if(!PRVM_allglobalfloat(require_spawnfunc_prefix))
1447 func = PRVM_ED_FindFunction (prog, funcname);
1451 // check for OnEntityNoSpawnFunction
1452 if (PRVM_serverfunction(SV_OnEntityNoSpawnFunction))
1455 PRVM_serverglobalfloat(time) = sv.time;
1456 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1457 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityNoSpawnFunction), "QC function SV_OnEntityNoSpawnFunction is missing");
1461 if (developer.integer > 0) // don't confuse non-developers with errors
1463 Con_Print("No spawn function for:\n");
1464 PRVM_ED_Print(prog, ent, NULL);
1466 PRVM_ED_Free (prog, ent);
1467 continue; // not included in "inhibited" count
1473 PRVM_serverglobalfloat(time) = sv.time;
1474 PRVM_allglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1475 prog->ExecuteProgram(prog, func - prog->functions, "");
1479 if(!ent->priv.required->free)
1480 if (PRVM_serverfunction(SV_OnEntityPostSpawnFunction))
1483 PRVM_serverglobalfloat(time) = sv.time;
1484 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1485 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityPostSpawnFunction), "QC function SV_OnEntityPostSpawnFunction is missing");
1489 if (ent->priv.required->free)
1493 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);
1495 prvm_reuseedicts_always_allow = 0;
1498 static void PRVM_FindOffsets(prvm_prog_t *prog)
1500 // field and global searches use -1 for NULL
1501 memset(&prog->fieldoffsets, -1, sizeof(prog->fieldoffsets));
1502 memset(&prog->globaloffsets, -1, sizeof(prog->globaloffsets));
1503 // function searches use 0 for NULL
1504 memset(&prog->funcoffsets, 0, sizeof(prog->funcoffsets));
1505 #define PRVM_DECLARE_serverglobalfloat(x)
1506 #define PRVM_DECLARE_serverglobalvector(x)
1507 #define PRVM_DECLARE_serverglobalstring(x)
1508 #define PRVM_DECLARE_serverglobaledict(x)
1509 #define PRVM_DECLARE_serverglobalfunction(x)
1510 #define PRVM_DECLARE_clientglobalfloat(x)
1511 #define PRVM_DECLARE_clientglobalvector(x)
1512 #define PRVM_DECLARE_clientglobalstring(x)
1513 #define PRVM_DECLARE_clientglobaledict(x)
1514 #define PRVM_DECLARE_clientglobalfunction(x)
1515 #define PRVM_DECLARE_menuglobalfloat(x)
1516 #define PRVM_DECLARE_menuglobalvector(x)
1517 #define PRVM_DECLARE_menuglobalstring(x)
1518 #define PRVM_DECLARE_menuglobaledict(x)
1519 #define PRVM_DECLARE_menuglobalfunction(x)
1520 #define PRVM_DECLARE_serverfieldfloat(x)
1521 #define PRVM_DECLARE_serverfieldvector(x)
1522 #define PRVM_DECLARE_serverfieldstring(x)
1523 #define PRVM_DECLARE_serverfieldedict(x)
1524 #define PRVM_DECLARE_serverfieldfunction(x)
1525 #define PRVM_DECLARE_clientfieldfloat(x)
1526 #define PRVM_DECLARE_clientfieldvector(x)
1527 #define PRVM_DECLARE_clientfieldstring(x)
1528 #define PRVM_DECLARE_clientfieldedict(x)
1529 #define PRVM_DECLARE_clientfieldfunction(x)
1530 #define PRVM_DECLARE_menufieldfloat(x)
1531 #define PRVM_DECLARE_menufieldvector(x)
1532 #define PRVM_DECLARE_menufieldstring(x)
1533 #define PRVM_DECLARE_menufieldedict(x)
1534 #define PRVM_DECLARE_menufieldfunction(x)
1535 #define PRVM_DECLARE_serverfunction(x)
1536 #define PRVM_DECLARE_clientfunction(x)
1537 #define PRVM_DECLARE_menufunction(x)
1538 #define PRVM_DECLARE_field(x) prog->fieldoffsets.x = PRVM_ED_FindFieldOffset(prog, #x);
1539 #define PRVM_DECLARE_global(x) prog->globaloffsets.x = PRVM_ED_FindGlobalOffset(prog, #x);
1540 #define PRVM_DECLARE_function(x) prog->funcoffsets.x = PRVM_ED_FindFunctionOffset(prog, #x);
1541 #include "prvm_offsets.h"
1542 #undef PRVM_DECLARE_serverglobalfloat
1543 #undef PRVM_DECLARE_serverglobalvector
1544 #undef PRVM_DECLARE_serverglobalstring
1545 #undef PRVM_DECLARE_serverglobaledict
1546 #undef PRVM_DECLARE_serverglobalfunction
1547 #undef PRVM_DECLARE_clientglobalfloat
1548 #undef PRVM_DECLARE_clientglobalvector
1549 #undef PRVM_DECLARE_clientglobalstring
1550 #undef PRVM_DECLARE_clientglobaledict
1551 #undef PRVM_DECLARE_clientglobalfunction
1552 #undef PRVM_DECLARE_menuglobalfloat
1553 #undef PRVM_DECLARE_menuglobalvector
1554 #undef PRVM_DECLARE_menuglobalstring
1555 #undef PRVM_DECLARE_menuglobaledict
1556 #undef PRVM_DECLARE_menuglobalfunction
1557 #undef PRVM_DECLARE_serverfieldfloat
1558 #undef PRVM_DECLARE_serverfieldvector
1559 #undef PRVM_DECLARE_serverfieldstring
1560 #undef PRVM_DECLARE_serverfieldedict
1561 #undef PRVM_DECLARE_serverfieldfunction
1562 #undef PRVM_DECLARE_clientfieldfloat
1563 #undef PRVM_DECLARE_clientfieldvector
1564 #undef PRVM_DECLARE_clientfieldstring
1565 #undef PRVM_DECLARE_clientfieldedict
1566 #undef PRVM_DECLARE_clientfieldfunction
1567 #undef PRVM_DECLARE_menufieldfloat
1568 #undef PRVM_DECLARE_menufieldvector
1569 #undef PRVM_DECLARE_menufieldstring
1570 #undef PRVM_DECLARE_menufieldedict
1571 #undef PRVM_DECLARE_menufieldfunction
1572 #undef PRVM_DECLARE_serverfunction
1573 #undef PRVM_DECLARE_clientfunction
1574 #undef PRVM_DECLARE_menufunction
1575 #undef PRVM_DECLARE_field
1576 #undef PRVM_DECLARE_global
1577 #undef PRVM_DECLARE_function
1582 typedef struct dpfield_s
1589 #define DPFIELDS (sizeof(dpfields) / sizeof(dpfield_t))
1591 dpfield_t dpfields[] =
1602 #define PO_HASHSIZE 16384
1603 typedef struct po_string_s
1606 struct po_string_s *nextonhashchain;
1611 po_string_t *hashtable[PO_HASHSIZE];
1614 static void PRVM_PO_UnparseString(char *out, const char *in, size_t outsize)
1623 case '\a': if(outsize >= 2) { *out++ = '\\'; *out++ = 'a'; outsize -= 2; } break;
1624 case '\b': if(outsize >= 2) { *out++ = '\\'; *out++ = 'b'; outsize -= 2; } break;
1625 case '\t': if(outsize >= 2) { *out++ = '\\'; *out++ = 't'; outsize -= 2; } break;
1626 case '\r': if(outsize >= 2) { *out++ = '\\'; *out++ = 'r'; outsize -= 2; } break;
1627 case '\n': if(outsize >= 2) { *out++ = '\\'; *out++ = 'n'; outsize -= 2; } break;
1628 case '\\': if(outsize >= 2) { *out++ = '\\'; *out++ = '\\'; outsize -= 2; } break;
1629 case '"': if(outsize >= 2) { *out++ = '\\'; *out++ = '"'; outsize -= 2; } break;
1631 if(*in >= 0 && *in <= 0x1F)
1636 *out++ = '0' + ((*in & 0700) >> 6);
1637 *out++ = '0' + ((*in & 0070) >> 3);
1638 *out++ = '0' + (*in & 0007) ;
1655 static void PRVM_PO_ParseString(char *out, const char *in, size_t outsize)
1668 case 'a': if(outsize > 0) { *out++ = '\a'; --outsize; } break;
1669 case 'b': if(outsize > 0) { *out++ = '\b'; --outsize; } break;
1670 case 't': if(outsize > 0) { *out++ = '\t'; --outsize; } break;
1671 case 'r': if(outsize > 0) { *out++ = '\r'; --outsize; } break;
1672 case 'n': if(outsize > 0) { *out++ = '\n'; --outsize; } break;
1673 case '\\': if(outsize > 0) { *out++ = '\\'; --outsize; } break;
1674 case '"': if(outsize > 0) { *out++ = '"'; --outsize; } break;
1675 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
1679 if(*in >= '0' && *in <= '7')
1682 *out = (*out << 3) | (*in - '0');
1685 if(*in >= '0' && *in <= '7')
1688 *out = (*out << 3) | (*in - '0');
1699 if(outsize > 0) { *out++ = *in; --outsize; }
1714 static po_t *PRVM_PO_Load(const char *filename, const char *filename2, mempool_t *pool)
1719 char inbuf[MAX_INPUTLINE];
1720 char decodedbuf[MAX_INPUTLINE];
1723 po_string_t thisstr;
1726 for (i = 0; i < 2; ++i)
1728 const char *buf = (const char *)
1729 FS_LoadFile((i > 0 ? filename : filename2), pool, true, NULL);
1730 // first read filename2, then read filename
1731 // so that progs.dat.de.po wins over common.de.po
1732 // and within file, last item wins
1739 po = (po_t *)Mem_Alloc(pool, sizeof(*po));
1740 memset(po, 0, sizeof(*po));
1743 memset(&thisstr, 0, sizeof(thisstr)); // hush compiler warning
1751 p = strchr(p, '\n');
1757 if(*p == '\r' || *p == '\n')
1762 if(!strncmp(p, "msgid \"", 7))
1767 else if(!strncmp(p, "msgstr \"", 8))
1774 p = strchr(p, '\n');
1784 q = strchr(p, '\n');
1791 if((size_t)(q - p) >= (size_t) sizeof(inbuf))
1793 strlcpy(inbuf, p, q - p); // not - 1, because this adds a NUL
1794 PRVM_PO_ParseString(decodedbuf + decodedpos, inbuf, sizeof(decodedbuf) - decodedpos);
1795 decodedpos += strlen(decodedbuf + decodedpos);
1805 Mem_Free(thisstr.key);
1806 thisstr.key = (char *)Mem_Alloc(pool, decodedpos + 1);
1807 memcpy(thisstr.key, decodedbuf, decodedpos + 1);
1809 else if(decodedpos > 0 && thisstr.key) // skip empty translation results
1811 thisstr.value = (char *)Mem_Alloc(pool, decodedpos + 1);
1812 memcpy(thisstr.value, decodedbuf, decodedpos + 1);
1813 hashindex = CRC_Block((const unsigned char *) thisstr.key, strlen(thisstr.key)) % PO_HASHSIZE;
1814 thisstr.nextonhashchain = po->hashtable[hashindex];
1815 po->hashtable[hashindex] = (po_string_t *)Mem_Alloc(pool, sizeof(thisstr));
1816 memcpy(po->hashtable[hashindex], &thisstr, sizeof(thisstr));
1817 memset(&thisstr, 0, sizeof(thisstr));
1821 Mem_Free((char *) buf);
1826 static const char *PRVM_PO_Lookup(po_t *po, const char *str)
1828 int hashindex = CRC_Block((const unsigned char *) str, strlen(str)) % PO_HASHSIZE;
1829 po_string_t *p = po->hashtable[hashindex];
1832 if(!strcmp(str, p->key))
1834 p = p->nextonhashchain;
1838 static void PRVM_PO_Destroy(po_t *po)
1841 for(i = 0; i < PO_HASHSIZE; ++i)
1843 po_string_t *p = po->hashtable[i];
1847 p = p->nextonhashchain;
1856 void PRVM_LeakTest(prvm_prog_t *prog);
1857 void PRVM_Prog_Reset(prvm_prog_t *prog)
1861 PRVM_LeakTest(prog);
1862 prog->reset_cmd(prog);
1863 Mem_FreePool(&prog->progs_mempool);
1865 PRVM_PO_Destroy((po_t *) prog->po);
1867 memset(prog,0,sizeof(prvm_prog_t));
1868 prog->break_statement = -1;
1869 prog->watch_global_type = ev_void;
1870 prog->watch_field_type = ev_void;
1878 static void PRVM_LoadLNO( prvm_prog_t *prog, const char *progname ) {
1879 fs_offset_t filesize;
1881 unsigned int *header;
1884 FS_StripExtension( progname, filename, sizeof( filename ) );
1885 strlcat( filename, ".lno", sizeof( filename ) );
1887 lno = FS_LoadFile( filename, tempmempool, false, &filesize );
1893 <Spike> SafeWrite (h, &lnotype, sizeof(int));
1894 <Spike> SafeWrite (h, &version, sizeof(int));
1895 <Spike> SafeWrite (h, &numglobaldefs, sizeof(int));
1896 <Spike> SafeWrite (h, &numpr_globals, sizeof(int));
1897 <Spike> SafeWrite (h, &numfielddefs, sizeof(int));
1898 <Spike> SafeWrite (h, &numstatements, sizeof(int));
1899 <Spike> SafeWrite (h, statement_linenums, numstatements*sizeof(int));
1901 if ((unsigned int)filesize < (6 + prog->progs_numstatements) * sizeof(int))
1907 header = (unsigned int *) lno;
1908 if( header[ 0 ] == *(unsigned int *) "LNOF" &&
1909 LittleLong( header[ 1 ] ) == 1 &&
1910 (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs_numglobaldefs &&
1911 (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs_numglobals &&
1912 (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs_numfielddefs &&
1913 (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs_numstatements )
1915 prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof( int ) );
1916 memcpy( prog->statement_linenums, header + 6, prog->progs_numstatements * sizeof( int ) );
1918 /* gmqcc suports columnums */
1919 if ((unsigned int)filesize > ((6 + 2 * prog->progs_numstatements) * sizeof( int )))
1921 prog->statement_columnnums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof( int ) );
1922 memcpy( prog->statement_columnnums, header + 6 + prog->progs_numstatements, prog->progs_numstatements * sizeof( int ) );
1933 static void PRVM_UpdateBreakpoints(prvm_prog_t *prog);
1934 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)
1937 dprograms_t *dprograms;
1938 dstatement_t *instatements;
1939 ddef_t *infielddefs;
1940 ddef_t *inglobaldefs;
1942 dfunction_t *infunctions;
1944 fs_offset_t filesize;
1945 int requiredglobalspace;
1962 prog->error_cmd("PRVM_LoadProgs: there is already a %s program loaded!", prog->name );
1964 Host_LockSession(); // all progs can use the session cvar
1965 Crypto_LoadKeys(); // all progs might use the keys at init time
1969 dprograms = (dprograms_t *) data;
1973 dprograms = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
1974 if (dprograms == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
1975 prog->error_cmd("PRVM_LoadProgs: couldn't load %s for %s", filename, prog->name);
1976 // TODO bounds check header fields (e.g. numstatements), they must never go behind end of file
1978 prog->profiletime = Sys_DirtyTime();
1979 prog->starttime = host.realtime;
1981 Con_DPrintf("%s programs occupy %iK.\n", prog->name, (int)(filesize/1024));
1983 requiredglobalspace = 0;
1984 for (i = 0;i < numrequiredglobals;i++)
1985 requiredglobalspace += required_global[i].type == ev_vector ? 3 : 1;
1987 prog->filecrc = CRC_Block((unsigned char *)dprograms, filesize);
1989 // byte swap the header
1990 prog->progs_version = LittleLong(dprograms->version);
1991 prog->progs_crc = LittleLong(dprograms->crc);
1992 if (prog->progs_version != PROG_VERSION)
1993 prog->error_cmd("%s: %s has wrong version number (%i should be %i)", prog->name, filename, prog->progs_version, PROG_VERSION);
1994 instatements = (dstatement_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_statements));
1995 prog->progs_numstatements = LittleLong(dprograms->numstatements);
1996 inglobaldefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globaldefs));
1997 prog->progs_numglobaldefs = LittleLong(dprograms->numglobaldefs);
1998 infielddefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_fielddefs));
1999 prog->progs_numfielddefs = LittleLong(dprograms->numfielddefs);
2000 infunctions = (dfunction_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_functions));
2001 prog->progs_numfunctions = LittleLong(dprograms->numfunctions);
2002 instrings = (char *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_strings));
2003 prog->progs_numstrings = LittleLong(dprograms->numstrings);
2004 inglobals = (int *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globals));
2005 prog->progs_numglobals = LittleLong(dprograms->numglobals);
2006 prog->progs_entityfields = LittleLong(dprograms->entityfields);
2008 prog->numstatements = prog->progs_numstatements;
2009 prog->numglobaldefs = prog->progs_numglobaldefs;
2010 prog->numfielddefs = prog->progs_numfielddefs;
2011 prog->numfunctions = prog->progs_numfunctions;
2012 prog->numstrings = prog->progs_numstrings;
2013 prog->numglobals = prog->progs_numglobals;
2014 prog->entityfields = prog->progs_entityfields;
2016 if (LittleLong(dprograms->ofs_strings) + prog->progs_numstrings > (int)filesize)
2017 prog->error_cmd("%s: %s strings go past end of file", prog->name, filename);
2018 prog->strings = (char *)Mem_Alloc(prog->progs_mempool, prog->progs_numstrings);
2019 memcpy(prog->strings, instrings, prog->progs_numstrings);
2020 prog->stringssize = prog->progs_numstrings;
2022 prog->numknownstrings = 0;
2023 prog->maxknownstrings = 0;
2024 prog->knownstrings = NULL;
2025 prog->knownstrings_flags = NULL;
2027 Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
2029 // we need to expand the globaldefs and fielddefs to include engine defs
2030 prog->globaldefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobaldefs + numrequiredglobals) * sizeof(ddef_t));
2031 prog->globals.fp = (prvm_vec_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobals + requiredglobalspace + 2) * sizeof(prvm_vec_t));
2032 // + 2 is because of an otherwise occurring overrun in RETURN instruction
2033 // when trying to return the last or second-last global
2034 // (RETURN always returns a vector, there is no RETURN_F instruction)
2035 prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numfielddefs + numrequiredfields) * sizeof(ddef_t));
2036 // we need to convert the statements to our memory format
2037 prog->statements = (mstatement_t *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(mstatement_t));
2038 // allocate space for profiling statement usage
2039 prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(*prog->statement_profile));
2040 prog->explicit_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(*prog->statement_profile));
2041 // functions need to be converted to the memory format
2042 prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs_numfunctions);
2044 for (i = 0;i < prog->progs_numfunctions;i++)
2046 prog->functions[i].first_statement = LittleLong(infunctions[i].first_statement);
2047 prog->functions[i].parm_start = LittleLong(infunctions[i].parm_start);
2048 prog->functions[i].s_name = LittleLong(infunctions[i].s_name);
2049 prog->functions[i].s_file = LittleLong(infunctions[i].s_file);
2050 prog->functions[i].numparms = LittleLong(infunctions[i].numparms);
2051 prog->functions[i].locals = LittleLong(infunctions[i].locals);
2052 memcpy(prog->functions[i].parm_size, infunctions[i].parm_size, sizeof(infunctions[i].parm_size));
2053 if(prog->functions[i].first_statement >= prog->numstatements)
2054 prog->error_cmd("PRVM_LoadProgs: out of bounds function statement (function %d) in %s", i, prog->name);
2055 // TODO bounds check parm_start, s_name, s_file, numparms, locals, parm_size
2058 // copy the globaldefs to the new globaldefs list
2059 for (i=0 ; i<prog->numglobaldefs ; i++)
2061 prog->globaldefs[i].type = LittleShort(inglobaldefs[i].type);
2062 prog->globaldefs[i].ofs = LittleShort(inglobaldefs[i].ofs);
2063 prog->globaldefs[i].s_name = LittleLong(inglobaldefs[i].s_name);
2064 // TODO bounds check ofs, s_name
2067 // append the required globals
2068 for (i = 0;i < numrequiredglobals;i++)
2070 prog->globaldefs[prog->numglobaldefs].type = required_global[i].type;
2071 prog->globaldefs[prog->numglobaldefs].ofs = prog->numglobals;
2072 prog->globaldefs[prog->numglobaldefs].s_name = PRVM_SetEngineString(prog, required_global[i].name);
2073 if (prog->globaldefs[prog->numglobaldefs].type == ev_vector)
2074 prog->numglobals += 3;
2077 prog->numglobaldefs++;
2080 // copy the progs fields to the new fields list
2081 for (i = 0;i < prog->numfielddefs;i++)
2083 prog->fielddefs[i].type = LittleShort(infielddefs[i].type);
2084 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
2085 prog->error_cmd("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", prog->name);
2086 prog->fielddefs[i].ofs = LittleShort(infielddefs[i].ofs);
2087 prog->fielddefs[i].s_name = LittleLong(infielddefs[i].s_name);
2088 // TODO bounds check ofs, s_name
2091 // append the required fields
2092 for (i = 0;i < numrequiredfields;i++)
2094 prog->fielddefs[prog->numfielddefs].type = required_field[i].type;
2095 prog->fielddefs[prog->numfielddefs].ofs = prog->entityfields;
2096 prog->fielddefs[prog->numfielddefs].s_name = PRVM_SetEngineString(prog, required_field[i].name);
2097 if (prog->fielddefs[prog->numfielddefs].type == ev_vector)
2098 prog->entityfields += 3;
2100 prog->entityfields++;
2101 prog->numfielddefs++;
2104 // LadyHavoc: TODO: reorder globals to match engine struct
2105 // LadyHavoc: TODO: reorder fields to match engine struct
2106 #define remapglobal(index) (index)
2107 #define remapfield(index) (index)
2110 // FIXME: LadyHavoc: this uses a crude way to identify integer constants, rather than checking for matching globaldefs and checking their type
2111 for (i = 0;i < prog->progs_numglobals;i++)
2113 u.i = LittleLong(inglobals[i]);
2114 // most globals are 0, we only need to deal with the ones that are not
2117 d = u.i & 0xFF800000;
2118 if ((d == 0xFF800000) || (d == 0))
2120 // Looks like an integer (expand to int64)
2121 prog->globals.ip[remapglobal(i)] = u.i;
2125 // Looks like a float (expand to double)
2126 prog->globals.fp[remapglobal(i)] = u.f;
2131 // LadyHavoc: TODO: support 32bit progs statement formats
2132 // copy, remap globals in statements, bounds check
2133 for (i = 0;i < prog->progs_numstatements;i++)
2135 op = (opcode_t)LittleShort(instatements[i].op);
2136 a = (unsigned short)LittleShort(instatements[i].a);
2137 b = (unsigned short)LittleShort(instatements[i].b);
2138 c = (unsigned short)LittleShort(instatements[i].c);
2144 if (a >= prog->progs_numglobals || b + i < 0 || b + i >= prog->progs_numstatements)
2145 prog->error_cmd("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", i, prog->name);
2146 prog->statements[i].op = op;
2147 prog->statements[i].operand[0] = remapglobal(a);
2148 prog->statements[i].operand[1] = -1;
2149 prog->statements[i].operand[2] = -1;
2150 prog->statements[i].jumpabsolute = i + b;
2154 if (a + i < 0 || a + i >= prog->progs_numstatements)
2155 prog->error_cmd("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, prog->name);
2156 prog->statements[i].op = op;
2157 prog->statements[i].operand[0] = -1;
2158 prog->statements[i].operand[1] = -1;
2159 prog->statements[i].operand[2] = -1;
2160 prog->statements[i].jumpabsolute = i + a;
2163 Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", (int)op, i, prog->name);
2165 // global global global
2200 if (a >= prog->progs_numglobals || b >= prog->progs_numglobals || c >= prog->progs_numglobals)
2201 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
2202 prog->statements[i].op = op;
2203 prog->statements[i].operand[0] = remapglobal(a);
2204 prog->statements[i].operand[1] = remapglobal(b);
2205 prog->statements[i].operand[2] = remapglobal(c);
2206 prog->statements[i].jumpabsolute = -1;
2208 // global none global
2214 if (a >= prog->progs_numglobals || c >= prog->progs_numglobals)
2215 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2216 prog->statements[i].op = op;
2217 prog->statements[i].operand[0] = remapglobal(a);
2218 prog->statements[i].operand[1] = -1;
2219 prog->statements[i].operand[2] = remapglobal(c);
2220 prog->statements[i].jumpabsolute = -1;
2236 if (a >= prog->progs_numglobals || b >= prog->progs_numglobals)
2237 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2238 prog->statements[i].op = op;
2239 prog->statements[i].operand[0] = remapglobal(a);
2240 prog->statements[i].operand[1] = remapglobal(b);
2241 prog->statements[i].operand[2] = -1;
2242 prog->statements[i].jumpabsolute = -1;
2246 if ( a < prog->progs_numglobals)
2247 if ( prog->globals.ip[remapglobal(a)] >= 0 )
2248 if ( prog->globals.ip[remapglobal(a)] < prog->progs_numfunctions )
2249 if ( prog->functions[prog->globals.ip[remapglobal(a)]].first_statement == -642 )
2250 ++prog->numexplicitcoveragestatements;
2261 if ( a >= prog->progs_numglobals)
2262 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2263 prog->statements[i].op = op;
2264 prog->statements[i].operand[0] = remapglobal(a);
2265 prog->statements[i].operand[1] = -1;
2266 prog->statements[i].operand[2] = -1;
2267 prog->statements[i].jumpabsolute = -1;
2271 if(prog->numstatements < 1)
2273 prog->error_cmd("PRVM_LoadProgs: empty program in %s", prog->name);
2275 else switch(prog->statements[prog->numstatements - 1].op)
2282 prog->error_cmd("PRVM_LoadProgs: program may fall off the edge (does not end with RETURN, GOTO or DONE) in %s", prog->name);
2286 // we're done with the file now
2288 Mem_Free(dprograms);
2291 // check required functions
2292 for(i=0 ; i < numrequiredfunc ; i++)
2293 if(PRVM_ED_FindFunction(prog, required_func[i]) == 0)
2294 prog->error_cmd("%s: %s not found in %s",prog->name, required_func[i], filename);
2296 PRVM_LoadLNO(prog, filename);
2298 PRVM_Init_Exec(prog);
2300 if(*prvm_language.string)
2301 // in CSQC we really shouldn't be able to change how stuff works... sorry for now
2302 // later idea: include a list of authorized .po file checksums with the csprogs
2304 qboolean deftrans = prog == CLVM_prog;
2305 const char *realfilename = (prog != CLVM_prog ? filename : csqc_progname.string);
2306 if(deftrans) // once we have dotranslate_ strings, ALWAYS use the opt-in method!
2308 for (i=0 ; i<prog->numglobaldefs ; i++)
2311 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2312 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2313 if(name && !strncmp(name, "dotranslate_", 12))
2320 if(!strcmp(prvm_language.string, "dump"))
2322 qfile_t *f = FS_OpenRealFile(va(vabuf, sizeof(vabuf), "%s.pot", realfilename), "w", false);
2323 Con_Printf("Dumping to %s.pot\n", realfilename);
2326 for (i=0 ; i<prog->numglobaldefs ; i++)
2329 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2330 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2331 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2333 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2334 const char *value = PRVM_GetString(prog, val->string);
2337 char buf[MAX_INPUTLINE];
2338 PRVM_PO_UnparseString(buf, value, sizeof(buf));
2339 FS_Printf(f, "msgid \"%s\"\nmsgstr \"\"\n\n", buf);
2348 po_t *po = PRVM_PO_Load(
2349 va(vabuf, sizeof(vabuf), "%s.%s.po", realfilename, prvm_language.string),
2350 va(vabuf2, sizeof(vabuf2), "common.%s.po", prvm_language.string),
2351 prog->progs_mempool);
2354 for (i=0 ; i<prog->numglobaldefs ; i++)
2357 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2358 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2359 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2361 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2362 const char *value = PRVM_GetString(prog, val->string);
2365 value = PRVM_PO_Lookup(po, value);
2367 val->string = PRVM_SetEngineString(prog, value);
2375 for (cvar = prog->console_cmd->cvars->vars; cvar; cvar = cvar->next)
2376 cvar->globaldefindex[prog - prvm_prog_list] = -1;
2378 for (i=0 ; i<prog->numglobaldefs ; i++)
2381 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2382 //Con_Printf("found var %s\n", name);
2384 && !strncmp(name, "autocvar_", 9)
2385 && !(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
2388 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2389 cvar = Cvar_FindVar(prog->console_cmd->cvars, name + 9, prog->console_cmd->cvars_flagsmask);
2390 //Con_Printf("PRVM_LoadProgs: autocvar global %s in %s, processing...\n", name, prog->name);
2397 Con_DPrintf("PRVM_LoadProgs: no cvar for autocvar global %s in %s, creating...\n", name, prog->name);
2398 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2401 if((float)((int)(val->_float)) == val->_float)
2402 dpsnprintf(buf, sizeof(buf), "%i", (int)(val->_float));
2407 for (int precision = 7; precision <= 9; ++precision) {
2408 dpsnprintf(buf, sizeof(buf), "%.*g", precision, f);
2409 if ((float)atof(buf) == f) {
2417 for (i = 0; i < 3; ++i)
2421 for (int precision = 7; precision <= 9; ++precision) {
2422 dpsnprintf(buf, sizeof(buf), "%.*g", precision, f);
2423 if ((float)atof(buf) == f) {
2424 prec[i] = precision;
2429 dpsnprintf(buf, sizeof(buf), "%.*g %.*g %.*g", prec[0], val->vector[0], prec[1], val->vector[1], prec[2], val->vector[2]);
2433 value = PRVM_GetString(prog, val->string);
2436 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2439 cvar = Cvar_Get(prog->console_cmd->cvars, name + 9, value, prog->console_cmd->cvars_flagsmask, NULL);
2440 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2442 val->string = PRVM_SetEngineString(prog, cvar->string);
2443 cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2446 prog->error_cmd("PRVM_LoadProgs: could not create cvar for autocvar global %s in %s", name, prog->name);
2447 cvar->globaldefindex[prog - prvm_prog_list] = i;
2449 else if((cvar->flags & CVAR_PRIVATE) == 0)
2451 // MUST BE SYNCED WITH cvar.c Cvar_Set
2454 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2457 val->_float = cvar->value;
2461 VectorClear(val->vector);
2462 for (j = 0;j < 3;j++)
2464 while (*s && ISWHITESPACE(*s))
2468 val->vector[j] = atof(s);
2469 while (!ISWHITESPACE(*s))
2476 val->string = PRVM_SetEngineString(prog, cvar->string);
2477 cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2480 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2483 cvar->globaldefindex[prog - prvm_prog_list] = i;
2486 Con_Printf("PRVM_LoadProgs: private cvar for autocvar global %s in %s\n", name, prog->name);
2492 prog->loaded = true;
2494 PRVM_UpdateBreakpoints(prog);
2496 // set flags & ddef_ts in prog
2500 PRVM_FindOffsets(prog);
2502 prog->init_cmd(prog);
2505 PRVM_MEM_Alloc(prog);
2507 // Inittime is at least the time when this function finished. However,
2508 // later events may bump it.
2509 prog->inittime = host.realtime;
2513 static void PRVM_Fields_f(cmd_state_t *cmd)
2516 int i, j, ednum, used, usedamount;
2518 char tempstring[MAX_INPUTLINE], tempstring2[260];
2528 Con_Print("no progs loaded\n");
2533 if(Cmd_Argc(cmd) != 2)
2535 Con_Print("prvm_fields <program name>\n");
2539 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2542 counts = (int *)Mem_Alloc(tempmempool, prog->numfielddefs * sizeof(int));
2543 for (ednum = 0;ednum < prog->max_edicts;ednum++)
2545 ed = PRVM_EDICT_NUM(ednum);
2546 if (ed->priv.required->free)
2548 for (i = 1;i < prog->numfielddefs;i++)
2550 d = &prog->fielddefs[i];
2551 name = PRVM_GetString(prog, d->s_name);
2552 if (name[strlen(name)-2] == '_')
2553 continue; // skip _x, _y, _z vars
2554 val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
2555 // if the value is still all 0, skip the field
2556 for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
2558 if (val->ivector[j])
2569 for (i = 0;i < prog->numfielddefs;i++)
2571 d = &prog->fielddefs[i];
2572 name = PRVM_GetString(prog, d->s_name);
2573 if (name[strlen(name)-2] == '_')
2574 continue; // skip _x, _y, _z vars
2575 switch(d->type & ~DEF_SAVEGLOBAL)
2578 strlcat(tempstring, "string ", sizeof(tempstring));
2581 strlcat(tempstring, "entity ", sizeof(tempstring));
2584 strlcat(tempstring, "function ", sizeof(tempstring));
2587 strlcat(tempstring, "field ", sizeof(tempstring));
2590 strlcat(tempstring, "void ", sizeof(tempstring));
2593 strlcat(tempstring, "float ", sizeof(tempstring));
2596 strlcat(tempstring, "vector ", sizeof(tempstring));
2599 strlcat(tempstring, "pointer ", sizeof(tempstring));
2602 dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
2603 strlcat(tempstring, tempstring2, sizeof(tempstring));
2606 if (strlen(name) > sizeof(tempstring2)-4)
2608 memcpy (tempstring2, name, sizeof(tempstring2)-4);
2609 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
2610 tempstring2[sizeof(tempstring2)-1] = 0;
2613 strlcat(tempstring, name, sizeof(tempstring));
2614 for (j = (int)strlen(name);j < 25;j++)
2615 strlcat(tempstring, " ", sizeof(tempstring));
2616 dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
2617 strlcat(tempstring, tempstring2, sizeof(tempstring));
2618 strlcat(tempstring, "\n", sizeof(tempstring));
2619 if (strlen(tempstring) >= sizeof(tempstring)/2)
2621 Con_Print(tempstring);
2627 usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
2631 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);
2634 static void PRVM_Globals_f(cmd_state_t *cmd)
2638 const char *wildcard;
2644 Con_Print("no progs loaded\n");
2647 if(Cmd_Argc (cmd) < 2 || Cmd_Argc(cmd) > 3)
2649 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
2653 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2656 if( Cmd_Argc(cmd) == 3)
2657 wildcard = Cmd_Argv(cmd, 2);
2661 Con_Printf("%s :", prog->name);
2663 for (i = 0;i < prog->numglobaldefs;i++)
2666 if( !matchpattern( PRVM_GetString(prog, prog->globaldefs[i].s_name), wildcard, 1) )
2671 Con_Printf("%s\n", PRVM_GetString(prog, prog->globaldefs[i].s_name));
2673 Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->numglobals, numculled, prog->numglobals * 4);
2681 static void PRVM_Global_f(cmd_state_t *cmd)
2685 char valuebuf[MAX_INPUTLINE];
2686 if( Cmd_Argc(cmd) != 3 ) {
2687 Con_Printf( "prvm_global <program name> <global name>\n" );
2691 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2694 global = PRVM_ED_FindGlobal( prog, Cmd_Argv(cmd, 2) );
2696 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(cmd, 2), Cmd_Argv(cmd, 1) );
2698 Con_Printf( "%s: %s\n", Cmd_Argv(cmd, 2), PRVM_ValueString( prog, (etype_t)global->type, PRVM_GLOBALFIELDVALUE(global->ofs), valuebuf, sizeof(valuebuf) ) );
2706 static void PRVM_GlobalSet_f(cmd_state_t *cmd)
2710 if( Cmd_Argc(cmd) != 4 ) {
2711 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2715 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2718 global = PRVM_ED_FindGlobal( prog, Cmd_Argv(cmd, 2) );
2720 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(cmd, 2), Cmd_Argv(cmd, 1) );
2722 PRVM_ED_ParseEpair( prog, NULL, global, Cmd_Argv(cmd, 3), true );
2726 ======================
2727 Break- and Watchpoints
2728 ======================
2732 char break_statement[256];
2733 char watch_global[256];
2735 char watch_field[256];
2738 static debug_data_t debug_data[PRVM_PROG_MAX];
2740 void PRVM_Breakpoint(prvm_prog_t *prog, int stack_index, const char *text)
2743 Con_Printf("PRVM_Breakpoint: %s\n", text);
2744 PRVM_PrintState(prog, stack_index);
2745 if (prvm_breakpointdump.integer)
2746 SV_Savegame_to(prog, va(vabuf, sizeof(vabuf), "breakpoint-%s.dmp", prog->name));
2749 void PRVM_Watchpoint(prvm_prog_t *prog, int stack_index, const char *text, etype_t type, prvm_eval_t *o, prvm_eval_t *n)
2751 size_t sz = sizeof(prvm_vec_t) * ((type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2752 if (memcmp(o, n, sz))
2755 char valuebuf_o[128];
2756 char valuebuf_n[128];
2757 PRVM_UglyValueString(prog, type, o, valuebuf_o, sizeof(valuebuf_o));
2758 PRVM_UglyValueString(prog, type, n, valuebuf_n, sizeof(valuebuf_n));
2759 dpsnprintf(buf, sizeof(buf), "%s: %s -> %s", text, valuebuf_o, valuebuf_n);
2760 PRVM_Breakpoint(prog, stack_index, buf);
2765 static void PRVM_UpdateBreakpoints(prvm_prog_t *prog)
2767 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2770 if (debug->break_statement[0])
2772 if (debug->break_statement[0] >= '0' && debug->break_statement[0] <= '9')
2774 prog->break_statement = atoi(debug->break_statement);
2775 prog->break_stack_index = 0;
2780 func = PRVM_ED_FindFunction (prog, debug->break_statement);
2783 Con_Printf("%s progs: no function or statement named %s to break on!\n", prog->name, debug->break_statement);
2784 prog->break_statement = -1;
2788 prog->break_statement = func->first_statement;
2789 prog->break_stack_index = 1;
2792 if (prog->break_statement >= -1)
2793 Con_Printf("%s progs: breakpoint is at statement %d\n", prog->name, prog->break_statement);
2796 prog->break_statement = -1;
2798 if (debug->watch_global[0])
2800 ddef_t *global = PRVM_ED_FindGlobal( prog, debug->watch_global );
2803 Con_Printf( "%s progs: no global named '%s' to watch!\n", prog->name, debug->watch_global );
2804 prog->watch_global_type = ev_void;
2808 size_t sz = sizeof(prvm_vec_t) * ((global->type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2809 prog->watch_global = global->ofs;
2810 prog->watch_global_type = (etype_t)global->type;
2811 memcpy(&prog->watch_global_value, PRVM_GLOBALFIELDVALUE(prog->watch_global), sz);
2813 if (prog->watch_global_type != ev_void)
2814 Con_Printf("%s progs: global watchpoint is at global index %d\n", prog->name, prog->watch_global);
2817 prog->watch_global_type = ev_void;
2819 if (debug->watch_field[0])
2821 ddef_t *field = PRVM_ED_FindField( prog, debug->watch_field );
2824 Con_Printf( "%s progs: no field named '%s' to watch!\n", prog->name, debug->watch_field );
2825 prog->watch_field_type = ev_void;
2829 size_t sz = sizeof(prvm_vec_t) * ((field->type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2830 prog->watch_edict = debug->watch_edict;
2831 prog->watch_field = field->ofs;
2832 prog->watch_field_type = (etype_t)field->type;
2833 if (prog->watch_edict < prog->num_edicts)
2834 memcpy(&prog->watch_edictfield_value, PRVM_EDICTFIELDVALUE(PRVM_EDICT_NUM(prog->watch_edict), prog->watch_field), sz);
2836 memset(&prog->watch_edictfield_value, 0, sz);
2838 if (prog->watch_edict != ev_void)
2839 Con_Printf("%s progs: edict field watchpoint is at edict %d field index %d\n", prog->name, prog->watch_edict, prog->watch_field);
2842 prog->watch_field_type = ev_void;
2845 static void PRVM_Breakpoint_f(cmd_state_t *cmd)
2849 if( Cmd_Argc(cmd) == 2 ) {
2850 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2853 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2854 debug->break_statement[0] = 0;
2856 PRVM_UpdateBreakpoints(prog);
2859 if( Cmd_Argc(cmd) != 3 ) {
2860 Con_Printf( "prvm_breakpoint <program name> <function name | statement>\n" );
2864 if (!(prog = PRVM_ProgFromString(Cmd_Argv(cmd, 1))))
2868 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2869 strlcpy(debug->break_statement, Cmd_Argv(cmd, 2), sizeof(debug->break_statement));
2871 PRVM_UpdateBreakpoints(prog);
2874 static void PRVM_GlobalWatchpoint_f(cmd_state_t *cmd)
2878 if( Cmd_Argc(cmd) == 2 ) {
2879 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2882 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2883 debug->watch_global[0] = 0;
2885 PRVM_UpdateBreakpoints(prog);
2888 if( Cmd_Argc(cmd) != 3 ) {
2889 Con_Printf( "prvm_globalwatchpoint <program name> <global name>\n" );
2893 if (!(prog = PRVM_ProgFromString(Cmd_Argv(cmd, 1))))
2897 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2898 strlcpy(debug->watch_global, Cmd_Argv(cmd, 2), sizeof(debug->watch_global));
2900 PRVM_UpdateBreakpoints(prog);
2903 static void PRVM_EdictWatchpoint_f(cmd_state_t *cmd)
2907 if( Cmd_Argc(cmd) == 2 ) {
2908 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2911 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2912 debug->watch_field[0] = 0;
2914 PRVM_UpdateBreakpoints(prog);
2917 if( Cmd_Argc(cmd) != 4 ) {
2918 Con_Printf( "prvm_edictwatchpoint <program name> <edict number> <field name>\n" );
2922 if (!(prog = PRVM_ProgFromString(Cmd_Argv(cmd, 1))))
2926 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2927 debug->watch_edict = atoi(Cmd_Argv(cmd, 2));
2928 strlcpy(debug->watch_field, Cmd_Argv(cmd, 3), sizeof(debug->watch_field));
2930 PRVM_UpdateBreakpoints(prog);
2938 void PRVM_Init (void)
2940 Cmd_AddCommand(CMD_SHARED, "prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2941 Cmd_AddCommand(CMD_SHARED, "prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2942 Cmd_AddCommand(CMD_SHARED, "prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2943 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)");
2944 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");
2945 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)");
2946 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)");
2947 Cmd_AddCommand(CMD_SHARED, "prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2948 Cmd_AddCommand(CMD_SHARED, "prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2949 Cmd_AddCommand(CMD_SHARED, "prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2950 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)");
2951 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");
2952 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");
2953 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)");
2954 Cmd_AddCommand(CMD_SHARED, "cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2955 Cmd_AddCommand(CMD_SHARED, "menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2956 Cmd_AddCommand(CMD_SHARED, "sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2957 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");
2958 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");
2959 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");
2961 Cvar_RegisterVariable (&prvm_language);
2962 Cvar_RegisterVariable (&prvm_traceqc);
2963 Cvar_RegisterVariable (&prvm_statementprofiling);
2964 Cvar_RegisterVariable (&prvm_timeprofiling);
2965 Cvar_RegisterVariable (&prvm_coverage);
2966 Cvar_RegisterVariable (&prvm_backtraceforwarnings);
2967 Cvar_RegisterVariable (&prvm_leaktest);
2968 Cvar_RegisterVariable (&prvm_leaktest_follow_targetname);
2969 Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames);
2970 Cvar_RegisterVariable (&prvm_errordump);
2971 Cvar_RegisterVariable (&prvm_breakpointdump);
2972 Cvar_RegisterVariable (&prvm_reuseedicts_startuptime);
2973 Cvar_RegisterVariable (&prvm_reuseedicts_neverinsameframe);
2974 Cvar_RegisterVariable (&prvm_garbagecollection_enable);
2975 Cvar_RegisterVariable (&prvm_garbagecollection_notify);
2976 Cvar_RegisterVariable (&prvm_garbagecollection_scan_limit);
2977 Cvar_RegisterVariable (&prvm_garbagecollection_strings);
2978 Cvar_RegisterVariable (&prvm_stringdebug);
2980 // COMMANDLINEOPTION: PRVM: -norunaway disables the runaway loop check (it might be impossible to exit DarkPlaces if used!)
2981 prvm_runawaycheck = !COM_CheckParm("-norunaway");
2991 void PRVM_Prog_Init(prvm_prog_t *prog, cmd_state_t *cmd)
2993 PRVM_Prog_Reset(prog);
2994 prog->leaktest_active = prvm_leaktest.integer != 0;
2995 prog->console_cmd = cmd;
2998 // LadyHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
2999 unsigned int PRVM_EDICT_NUM_ERROR(prvm_prog_t *prog, unsigned int n, const char *filename, int fileline)
3001 prog->error_cmd("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", prog->name, n, filename, fileline);
3005 #define PRVM_KNOWNSTRINGBASE 0x40000000
3007 const char *PRVM_GetString(prvm_prog_t *prog, int num)
3012 if (prvm_stringdebug.integer)
3013 VM_Warning(prog, "PRVM_GetString: Invalid string offset (%i < 0)\n", num);
3016 else if (num < prog->stringssize)
3018 // constant string from progs.dat
3019 return prog->strings + num;
3021 else if (num <= prog->stringssize + prog->tempstringsbuf.maxsize)
3023 // tempstring returned by engine to QC (becomes invalid after returning to engine)
3024 num -= prog->stringssize;
3025 if (num < prog->tempstringsbuf.cursize)
3026 return (char *)prog->tempstringsbuf.data + num;
3029 if (prvm_stringdebug.integer)
3030 VM_Warning(prog, "PRVM_GetString: Invalid temp-string offset (%i >= %i prog->tempstringsbuf.cursize)\n", num, prog->tempstringsbuf.cursize);
3034 else if (num & PRVM_KNOWNSTRINGBASE)
3037 num = num - PRVM_KNOWNSTRINGBASE;
3038 if (num >= 0 && num < prog->numknownstrings)
3040 if (!prog->knownstrings[num])
3042 if (prvm_stringdebug.integer)
3043 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
3046 // refresh the garbage collection on the string - this guards
3047 // against a certain sort of repeated migration to earlier
3048 // points in the scan that could otherwise result in the string
3049 // being freed for being unused
3050 prog->knownstrings_flags[num] = (prog->knownstrings_flags[num] & ~KNOWNSTRINGFLAG_GCPRUNE) | KNOWNSTRINGFLAG_GCMARK;
3051 return prog->knownstrings[num];
3055 if (prvm_stringdebug.integer)
3056 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
3062 // invalid string offset
3063 if (prvm_stringdebug.integer)
3064 VM_Warning(prog, "PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
3069 const char *PRVM_ChangeEngineString(prvm_prog_t *prog, int i, const char *s)
3072 i = i - PRVM_KNOWNSTRINGBASE;
3073 if (i < 0 || i >= prog->numknownstrings)
3074 prog->error_cmd("PRVM_ChangeEngineString: string index %i is out of bounds", i);
3075 else if ((prog->knownstrings_flags[i] & KNOWNSTRINGFLAG_ENGINE) == 0)
3076 prog->error_cmd("PRVM_ChangeEngineString: string index %i is not an engine string", i);
3077 old = prog->knownstrings[i];
3078 prog->knownstrings[i] = s;
3082 static void PRVM_NewKnownString(prvm_prog_t *prog, int i, int flags, const char *s)
3084 if (i >= prog->numknownstrings)
3086 if (i >= prog->maxknownstrings)
3088 const char **oldstrings = prog->knownstrings;
3089 const unsigned char *oldstrings_flags = prog->knownstrings_flags;
3090 const char **oldstrings_origin = prog->knownstrings_origin;
3091 prog->maxknownstrings += 128;
3092 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3093 prog->knownstrings_flags = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
3094 if (prog->leaktest_active)
3095 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3096 if (prog->numknownstrings)
3098 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
3099 memcpy((char **)prog->knownstrings_flags, oldstrings_flags, prog->numknownstrings * sizeof(unsigned char));
3100 if (prog->leaktest_active)
3101 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
3104 prog->numknownstrings++;
3106 prog->firstfreeknownstring = i + 1;
3107 prog->knownstrings[i] = s;
3108 // it's in use right now, spare it until the next gc pass - that said, it is not freeable so this is probably moot
3109 prog->knownstrings_flags[i] = flags;
3110 if (prog->leaktest_active)
3111 prog->knownstrings_origin[i] = NULL;
3114 int PRVM_SetEngineString(prvm_prog_t *prog, const char *s)
3119 if (s >= prog->strings && s <= prog->strings + prog->stringssize)
3120 prog->error_cmd("PRVM_SetEngineString: s in prog->strings area");
3121 // if it's in the tempstrings area, use a reserved range
3122 // (otherwise we'd get millions of useless string offsets cluttering the database)
3123 if (s >= (char *)prog->tempstringsbuf.data && s < (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.maxsize)
3124 return prog->stringssize + (s - (char *)prog->tempstringsbuf.data);
3125 // see if it's a known string address
3126 for (i = 0;i < prog->numknownstrings;i++)
3127 if (prog->knownstrings[i] == s)
3128 return PRVM_KNOWNSTRINGBASE + i;
3129 // new unknown engine string
3130 if (developer_insane.integer)
3131 Con_DPrintf("new engine string %p = \"%s\"\n", (void *)s, s);
3132 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3133 if (!prog->knownstrings[i])
3135 PRVM_NewKnownString(prog, i, KNOWNSTRINGFLAG_GCMARK | KNOWNSTRINGFLAG_ENGINE, s);
3136 return PRVM_KNOWNSTRINGBASE + i;
3139 // temp string handling
3141 // all tempstrings go into this buffer consecutively, and it is reset
3142 // whenever PRVM_ExecuteProgram returns to the engine
3143 // (technically each PRVM_ExecuteProgram call saves the cursize value and
3144 // restores it on return, so multiple recursive calls can share the same
3146 // the buffer size is automatically grown as needed
3148 int PRVM_SetTempString(prvm_prog_t *prog, const char *s)
3154 size = (int)strlen(s) + 1;
3155 if (developer_insane.integer)
3156 Con_DPrintf("PRVM_SetTempString: cursize %i, size %i\n", prog->tempstringsbuf.cursize, size);
3157 if (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
3159 sizebuf_t old = prog->tempstringsbuf;
3160 if (prog->tempstringsbuf.cursize + size >= 1<<28)
3161 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);
3162 prog->tempstringsbuf.maxsize = max(prog->tempstringsbuf.maxsize, 65536);
3163 while (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
3164 prog->tempstringsbuf.maxsize *= 2;
3165 if (prog->tempstringsbuf.maxsize != old.maxsize || prog->tempstringsbuf.data == NULL)
3167 Con_DPrintf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, prog->tempstringsbuf.maxsize/1024);
3168 prog->tempstringsbuf.data = (unsigned char *) Mem_Alloc(prog->progs_mempool, prog->tempstringsbuf.maxsize);
3172 memcpy(prog->tempstringsbuf.data, old.data, old.cursize);
3177 t = (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.cursize;
3179 prog->tempstringsbuf.cursize += size;
3180 return PRVM_SetEngineString(prog, t);
3183 int PRVM_AllocString(prvm_prog_t *prog, size_t bufferlength, char **pointer)
3193 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3194 if (!prog->knownstrings[i])
3196 s = (char *)PRVM_Alloc(bufferlength);
3197 PRVM_NewKnownString(prog, i, KNOWNSTRINGFLAG_GCMARK, s);
3198 if(prog->leaktest_active)
3199 prog->knownstrings_origin[i] = PRVM_AllocationOrigin(prog);
3201 *pointer = (char *)(prog->knownstrings[i]);
3202 return PRVM_KNOWNSTRINGBASE + i;
3205 void PRVM_FreeString(prvm_prog_t *prog, int num)
3208 prog->error_cmd("PRVM_FreeString: attempt to free a NULL string");
3209 else if (num >= 0 && num < prog->stringssize)
3210 prog->error_cmd("PRVM_FreeString: attempt to free a constant string");
3211 else if (num >= PRVM_KNOWNSTRINGBASE && num < PRVM_KNOWNSTRINGBASE + prog->numknownstrings)
3213 num = num - PRVM_KNOWNSTRINGBASE;
3214 if (!prog->knownstrings[num])
3215 prog->error_cmd("PRVM_FreeString: attempt to free a non-existent or already freed string");
3216 if (!prog->knownstrings_flags[num])
3217 prog->error_cmd("PRVM_FreeString: attempt to free a string owned by the engine");
3218 PRVM_Free((char *)prog->knownstrings[num]);
3219 if(prog->leaktest_active)
3220 if(prog->knownstrings_origin[num])
3221 PRVM_Free((char *)prog->knownstrings_origin[num]);
3222 prog->knownstrings[num] = NULL;
3223 prog->knownstrings_flags[num] = 0;
3224 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
3227 prog->error_cmd("PRVM_FreeString: invalid string offset %i", num);
3230 static qboolean PRVM_IsStringReferenced(prvm_prog_t *prog, string_t string)
3234 for (i = 0;i < prog->numglobaldefs;i++)
3236 ddef_t *d = &prog->globaldefs[i];
3237 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3239 if(string == PRVM_GLOBALFIELDSTRING(d->ofs))
3243 for(j = 0; j < prog->num_edicts; ++j)
3245 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3246 if (ed->priv.required->free)
3248 for (i=0; i<prog->numfielddefs; ++i)
3250 ddef_t *d = &prog->fielddefs[i];
3251 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3253 if(string == PRVM_EDICTFIELDSTRING(ed, d->ofs))
3261 static qboolean PRVM_IsEdictRelevant(prvm_prog_t *prog, prvm_edict_t *edict)
3265 if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
3266 return true; // world or clients
3267 if (edict->priv.required->freetime <= prog->inittime)
3268 return true; // created during startup
3269 if (prog == SVVM_prog)
3271 if(PRVM_serveredictfloat(edict, solid)) // can block other stuff, or is a trigger?
3273 if(PRVM_serveredictfloat(edict, modelindex)) // visible ent?
3275 if(PRVM_serveredictfloat(edict, effects)) // particle effect?
3277 if(PRVM_serveredictfunction(edict, think)) // has a think function?
3278 if(PRVM_serveredictfloat(edict, nextthink) > 0) // that actually will eventually run?
3280 if(PRVM_serveredictfloat(edict, takedamage))
3282 if(*prvm_leaktest_ignore_classnames.string)
3284 if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_serveredictstring(edict, classname)))))
3288 else if (prog == CLVM_prog)
3290 // TODO someone add more stuff here
3291 if(PRVM_clientedictfloat(edict, entnum)) // csqc networked
3293 if(PRVM_clientedictfloat(edict, modelindex)) // visible ent?
3295 if(PRVM_clientedictfloat(edict, effects)) // particle effect?
3297 if(PRVM_clientedictfunction(edict, think)) // has a think function?
3298 if(PRVM_clientedictfloat(edict, nextthink) > 0) // that actually will eventually run?
3300 if(*prvm_leaktest_ignore_classnames.string)
3302 if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_clientedictstring(edict, classname)))))
3308 // menu prog does not have classnames
3313 static qboolean PRVM_IsEdictReferenced(prvm_prog_t *prog, prvm_edict_t *edict, int mark)
3316 int edictnum = PRVM_NUM_FOR_EDICT(edict);
3317 const char *targetname = NULL;
3319 if (prog == SVVM_prog && prvm_leaktest_follow_targetname.integer)
3320 targetname = PRVM_GetString(prog, PRVM_serveredictstring(edict, targetname));
3323 if(!*targetname) // ""
3326 for(j = 0; j < prog->num_edicts; ++j)
3328 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3329 if (ed->priv.required->mark < mark)
3335 const char *target = PRVM_GetString(prog, PRVM_serveredictstring(ed, target));
3337 if(!strcmp(target, targetname))
3340 for (i=0; i<prog->numfielddefs; ++i)
3342 ddef_t *d = &prog->fielddefs[i];
3343 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3345 if(edictnum == PRVM_EDICTFIELDEDICT(ed, d->ofs))
3353 static void PRVM_MarkReferencedEdicts(prvm_prog_t *prog)
3359 // Stage 1: world, all entities that are relevant, and all entities that are referenced by globals.
3361 for(j = 0; j < prog->num_edicts; ++j)
3363 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3364 if(ed->priv.required->free)
3366 ed->priv.required->mark = PRVM_IsEdictRelevant(prog, ed) ? stage : 0;
3368 for (i = 0;i < prog->numglobaldefs;i++)
3370 ddef_t *d = &prog->globaldefs[i];
3372 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3374 j = PRVM_GLOBALFIELDEDICT(d->ofs);
3375 if (i < 0 || j >= prog->max_edicts) {
3376 Con_Printf("Invalid entity reference from global %s.\n", PRVM_GetString(prog, d->s_name));
3379 ed = PRVM_EDICT_NUM(j);;
3380 ed->priv.required->mark = stage;
3383 // Future stages: all entities that are referenced by an entity of the previous stage.
3387 for(j = 0; j < prog->num_edicts; ++j)
3389 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3390 if(ed->priv.required->free)
3392 if(ed->priv.required->mark)
3394 if(PRVM_IsEdictReferenced(prog, ed, stage))
3396 ed->priv.required->mark = stage + 1;
3403 Con_DPrintf("leak check used %d stages to find all references\n", stage);
3406 void PRVM_LeakTest(prvm_prog_t *prog)
3409 qboolean leaked = false;
3411 if(!prog->leaktest_active)
3415 for (i = 0; i < prog->numknownstrings; ++i)
3417 if(prog->knownstrings[i])
3418 if(prog->knownstrings_flags[i])
3419 if(prog->knownstrings_origin[i])
3420 if(!PRVM_IsStringReferenced(prog, PRVM_KNOWNSTRINGBASE + i))
3422 Con_Printf("Unreferenced string found!\n Value: %s\n Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
3428 PRVM_MarkReferencedEdicts(prog);
3429 for(j = 0; j < prog->num_edicts; ++j)
3431 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3432 if(ed->priv.required->free)
3434 if(!ed->priv.required->mark)
3435 if(ed->priv.required->allocation_origin)
3437 Con_Printf("Unreferenced edict found!\n Allocated at: %s\n", ed->priv.required->allocation_origin);
3438 PRVM_ED_Print(prog, ed, NULL);
3443 ed->priv.required->mark = 0; // clear marks again when done
3446 for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
3448 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
3450 if(stringbuffer->origin)
3452 Con_Printf("Open string buffer handle found!\n Allocated at: %s\n", stringbuffer->origin);
3457 for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
3459 if(prog->openfiles[i])
3460 if(prog->openfiles_origin[i])
3462 Con_Printf("Open file handle found!\n Allocated at: %s\n", prog->openfiles_origin[i]);
3467 for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
3469 if(prog->opensearches[i])
3470 if(prog->opensearches_origin[i])
3472 Con_Printf("Open search handle found!\n Allocated at: %s\n", prog->opensearches_origin[i]);
3478 Con_Printf("Congratulations. No leaks found.\n");
3481 void PRVM_GarbageCollection(prvm_prog_t *prog)
3483 int limit = prvm_garbagecollection_scan_limit.integer;
3484 prvm_prog_garbagecollection_state_t *gc = &prog->gc;
3485 if (!prvm_garbagecollection_enable.integer)
3488 // we like to limit how much scanning we do so it doesn't put a significant
3489 // burden on the cpu, so each of these are not complete scans, we also like
3490 // to have consistent cpu usage so we do a bit of work on each category of
3491 // leaked object every frame
3497 case PRVM_GC_GLOBALS_MARK:
3498 for (; gc->globals_mark_progress < prog->numglobaldefs && (limit--) > 0; gc->globals_mark_progress++)
3500 ddef_t *d = &prog->globaldefs[gc->globals_mark_progress];
3505 prvm_int_t s = prog->globals.ip[d->ofs];
3506 if (s & PRVM_KNOWNSTRINGBASE)
3508 prvm_int_t num = s - PRVM_KNOWNSTRINGBASE;
3509 if (!prog->knownstrings[num])
3512 Con_DPrintf("PRVM_GarbageCollection: Found bogus strzone reference in global %i (global name: \"%s\"), erasing reference", d->ofs, PRVM_GetString(prog, d->s_name));
3513 prog->globals.ip[d->ofs] = 0;
3516 prog->knownstrings_flags[num] = (prog->knownstrings_flags[num] | KNOWNSTRINGFLAG_GCMARK) & ~KNOWNSTRINGFLAG_GCPRUNE;
3524 if (gc->globals_mark_progress >= prog->numglobaldefs)
3527 case PRVM_GC_FIELDS_MARK:
3528 for (; gc->fields_mark_progress < prog->numfielddefs && limit > 0;)
3530 ddef_t *d = &prog->fielddefs[gc->fields_mark_progress];
3534 //for (gc-> entityindex = 0; entityindex < prog->num_edicts; entityindex++)
3535 for (;gc->fields_mark_progress_entity < prog->num_edicts && (limit--) > 0;gc->fields_mark_progress_entity++)
3537 int entityindex = gc->fields_mark_progress_entity;
3538 prvm_int_t s = prog->edictsfields.ip[entityindex * prog->entityfields + d->ofs];
3539 if (s & PRVM_KNOWNSTRINGBASE)
3541 prvm_int_t num = s - PRVM_KNOWNSTRINGBASE;
3542 if (!prog->knownstrings[num])
3545 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));
3546 prog->edictsfields.ip[entityindex * prog->entityfields + d->ofs] = 0;
3549 prog->knownstrings_flags[num] = (prog->knownstrings_flags[num] | KNOWNSTRINGFLAG_GCMARK) & ~KNOWNSTRINGFLAG_GCPRUNE;
3552 if (gc->fields_mark_progress_entity >= prog->num_edicts)
3554 gc->fields_mark_progress_entity = 0;
3555 gc->fields_mark_progress++;
3559 gc->fields_mark_progress_entity = 0;
3560 gc->fields_mark_progress++;
3564 if (gc->fields_mark_progress >= prog->numfielddefs)
3567 case PRVM_GC_KNOWNSTRINGS_SWEEP:
3568 // free any strzone'd strings that are not marked
3569 if (!prvm_garbagecollection_strings.integer)
3574 for (;gc->knownstrings_sweep_progress < prog->numknownstrings && (limit--) > 0;gc->knownstrings_sweep_progress++)
3576 int num = gc->knownstrings_sweep_progress;
3577 if (prog->knownstrings[num] && (prog->knownstrings_flags[num] & (KNOWNSTRINGFLAG_GCMARK | KNOWNSTRINGFLAG_ENGINE)) == 0)
3579 if (prog->knownstrings_flags[num] & KNOWNSTRINGFLAG_GCPRUNE)
3581 // string has been marked for pruning two passes in a row
3582 if (prvm_garbagecollection_notify.integer)
3583 Con_DPrintf("prvm_garbagecollection_notify: %s: freeing unreferenced string %i: \"%s\"\n", prog->name, num, prog->knownstrings[num]);
3584 Mem_Free((char *)prog->knownstrings[num]);
3585 prog->knownstrings[num] = NULL;
3586 prog->knownstrings_flags[num] = 0;
3587 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
3591 // mark it for pruning next pass
3592 prog->knownstrings_flags[num] |= KNOWNSTRINGFLAG_GCPRUNE;
3596 if (gc->knownstrings_sweep_progress >= prog->numknownstrings)
3601 memset(gc, 0, sizeof(*gc));