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"};
52 static double prvm_reuseedicts_always_allow = 0;
53 qboolean prvm_runawaycheck = true;
55 //============================================================================
63 static void PRVM_MEM_Alloc(prvm_prog_t *prog)
67 // reserve space for the null entity aka world
68 // check bound of max_edicts
69 prog->max_edicts = bound(1 + prog->reserved_edicts, prog->max_edicts, prog->limit_edicts);
70 prog->num_edicts = bound(1 + prog->reserved_edicts, prog->num_edicts, prog->max_edicts);
72 // edictprivate_size has to be min as big prvm_edict_private_t
73 prog->edictprivate_size = max(prog->edictprivate_size,(int)sizeof(prvm_edict_private_t));
76 prog->edicts = (prvm_edict_t *)Mem_Alloc(prog->progs_mempool,prog->limit_edicts * sizeof(prvm_edict_t));
78 // alloc edict private space
79 prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size);
82 prog->entityfieldsarea = prog->entityfields * prog->max_edicts;
83 prog->edictsfields.fp = (prvm_vec_t *)Mem_Alloc(prog->progs_mempool, prog->entityfieldsarea * sizeof(prvm_vec_t));
86 for(i = 0; i < prog->max_edicts; i++)
88 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
89 prog->edicts[i].fields.fp = prog->edictsfields.fp + i * prog->entityfields;
95 PRVM_MEM_IncreaseEdicts
98 void PRVM_MEM_IncreaseEdicts(prvm_prog_t *prog)
102 if(prog->max_edicts >= prog->limit_edicts)
105 prog->begin_increase_edicts(prog);
108 prog->max_edicts = min(prog->max_edicts + 256, prog->limit_edicts);
110 prog->entityfieldsarea = prog->entityfields * prog->max_edicts;
111 prog->edictsfields.fp = (prvm_vec_t*)Mem_Realloc(prog->progs_mempool, (void *)prog->edictsfields.fp, prog->entityfieldsarea * sizeof(prvm_vec_t));
112 prog->edictprivate = (void *)Mem_Realloc(prog->progs_mempool, (void *)prog->edictprivate, prog->max_edicts * prog->edictprivate_size);
114 //set e and v pointers
115 for(i = 0; i < prog->max_edicts; i++)
117 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
118 prog->edicts[i].fields.fp = prog->edictsfields.fp + i * prog->entityfields;
121 prog->end_increase_edicts(prog);
124 //============================================================================
127 int PRVM_ED_FindFieldOffset(prvm_prog_t *prog, const char *field)
130 d = PRVM_ED_FindField(prog, field);
136 int PRVM_ED_FindGlobalOffset(prvm_prog_t *prog, const char *global)
139 d = PRVM_ED_FindGlobal(prog, global);
145 func_t PRVM_ED_FindFunctionOffset(prvm_prog_t *prog, const char *function)
148 f = PRVM_ED_FindFunction(prog, function);
151 return (func_t)(f - prog->functions);
159 prvm_prog_t *PRVM_ProgFromString(const char *str)
161 if (!strcmp(str, "server"))
163 if (!strcmp(str, "client"))
166 if (!strcmp(str, "menu"))
174 PRVM_FriendlyProgFromString
177 prvm_prog_t *PRVM_FriendlyProgFromString(const char *str)
179 prvm_prog_t *prog = PRVM_ProgFromString(str);
182 Con_Printf("%s: unknown program name\n", str);
187 Con_Printf("%s: program is not loaded\n", str);
197 Sets everything to NULL.
199 Nota bene: this also marks the entity as allocated if it has been previously
200 freed and sets the allocation origin.
203 void PRVM_ED_ClearEdict(prvm_prog_t *prog, prvm_edict_t *e)
205 memset(e->fields.fp, 0, prog->entityfields * sizeof(prvm_vec_t));
206 e->priv.required->free = false;
207 e->priv.required->freetime = realtime;
208 if(e->priv.required->allocation_origin)
209 Mem_Free((char *)e->priv.required->allocation_origin);
210 e->priv.required->allocation_origin = PRVM_AllocationOrigin(prog);
212 // AK: Let the init_edict function determine if something needs to be initialized
213 prog->init_edict(prog, e);
216 const char *PRVM_AllocationOrigin(prvm_prog_t *prog)
219 if(prog->leaktest_active)
220 if(prog->depth > 0) // actually in QC code and not just parsing the entities block of a map/savegame
222 buf = (char *)PRVM_Alloc(256);
223 PRVM_ShortStackTrace(prog, buf, 256);
232 Returns if this particular edict could get allocated by PRVM_ED_Alloc
235 qboolean PRVM_ED_CanAlloc(prvm_prog_t *prog, prvm_edict_t *e)
237 if(!e->priv.required->free)
239 if(prvm_reuseedicts_always_allow == realtime)
241 if(realtime <= e->priv.required->freetime + 0.1 && prvm_reuseedicts_neverinsameframe.integer)
242 return false; // never allow reuse in same frame (causes networking trouble)
243 if(e->priv.required->freetime < prog->starttime + prvm_reuseedicts_startuptime.value)
245 if(realtime > e->priv.required->freetime + 1)
247 return false; // entity slot still blocked because the entity was freed less than one second ago
254 Either finds a free edict, or allocates a new one.
255 Try to avoid reusing an entity that was recently freed, because it
256 can cause the client to think the entity morphed into something else
257 instead of being removed and recreated, which can cause interpolated
258 angles and bad trails.
261 prvm_edict_t *PRVM_ED_Alloc(prvm_prog_t *prog)
266 // the client qc dont need maxclients
267 // thus it doesnt need to use svs.maxclients
268 // AK: changed i=svs.maxclients+1
269 // AK: changed so the edict 0 wont spawn -> used as reserved/world entity
270 // although the menu/client has no world
271 for (i = prog->reserved_edicts + 1;i < prog->num_edicts;i++)
273 e = PRVM_EDICT_NUM(i);
274 if(PRVM_ED_CanAlloc(prog, e))
276 PRVM_ED_ClearEdict (prog, e);
281 if (i == prog->limit_edicts)
282 prog->error_cmd("%s: PRVM_ED_Alloc: no free edicts", prog->name);
285 if (prog->num_edicts >= prog->max_edicts)
286 PRVM_MEM_IncreaseEdicts(prog);
288 e = PRVM_EDICT_NUM(i);
290 PRVM_ED_ClearEdict(prog, e);
298 Marks the edict as free
299 FIXME: walk all entities and NULL out references to this entity
302 void PRVM_ED_Free(prvm_prog_t *prog, prvm_edict_t *ed)
304 // dont delete the null entity (world) or reserved edicts
305 if (ed - prog->edicts <= prog->reserved_edicts)
308 prog->free_edict(prog, ed);
310 ed->priv.required->free = true;
311 ed->priv.required->freetime = realtime;
312 if(ed->priv.required->allocation_origin)
314 Mem_Free((char *)ed->priv.required->allocation_origin);
315 ed->priv.required->allocation_origin = NULL;
319 //===========================================================================
326 static ddef_t *PRVM_ED_GlobalAtOfs (prvm_prog_t *prog, int ofs)
331 for (i = 0;i < prog->numglobaldefs;i++)
333 def = &prog->globaldefs[i];
345 ddef_t *PRVM_ED_FieldAtOfs (prvm_prog_t *prog, int ofs)
350 for (i = 0;i < prog->numfielddefs;i++)
352 def = &prog->fielddefs[i];
364 ddef_t *PRVM_ED_FindField (prvm_prog_t *prog, const char *name)
369 for (i = 0;i < prog->numfielddefs;i++)
371 def = &prog->fielddefs[i];
372 if (!strcmp(PRVM_GetString(prog, def->s_name), name))
383 ddef_t *PRVM_ED_FindGlobal (prvm_prog_t *prog, const char *name)
388 for (i = 0;i < prog->numglobaldefs;i++)
390 def = &prog->globaldefs[i];
391 if (!strcmp(PRVM_GetString(prog, def->s_name), name))
403 mfunction_t *PRVM_ED_FindFunction (prvm_prog_t *prog, const char *name)
408 for (i = 0;i < prog->numfunctions;i++)
410 func = &prog->functions[i];
411 if (!strcmp(PRVM_GetString(prog, func->s_name), name))
422 Returns a string describing *data in a type specific manner
425 static char *PRVM_ValueString (prvm_prog_t *prog, etype_t type, prvm_eval_t *val, char *line, size_t linelength)
431 type = (etype_t)((int) type & ~DEF_SAVEGLOBAL);
436 strlcpy (line, PRVM_GetString (prog, val->string), linelength);
440 if (n < 0 || n >= prog->max_edicts)
441 dpsnprintf (line, linelength, "entity %i (invalid!)", n);
443 dpsnprintf (line, linelength, "entity %i", n);
446 if ((unsigned int)val->function < (unsigned int)prog->progs_numfunctions)
448 f = prog->functions + val->function;
449 dpsnprintf (line, linelength, "%s()", PRVM_GetString(prog, f->s_name));
452 dpsnprintf (line, linelength, "function%i() (invalid!)", val->function);
455 def = PRVM_ED_FieldAtOfs ( prog, val->_int );
457 dpsnprintf (line, linelength, ".%s", PRVM_GetString(prog, def->s_name));
459 dpsnprintf (line, linelength, "field%i (invalid!)", val->_int );
462 dpsnprintf (line, linelength, "void");
465 // LadyHavoc: changed from %5.1f to %10.4f
466 dpsnprintf (line, linelength, FLOAT_LOSSLESS_FORMAT, val->_float);
469 // LadyHavoc: changed from %5.1f to %10.4f
470 dpsnprintf (line, linelength, "'" VECTOR_LOSSLESS_FORMAT "'", val->vector[0], val->vector[1], val->vector[2]);
473 dpsnprintf (line, linelength, "pointer");
476 dpsnprintf (line, linelength, "bad type %i", (int) type);
487 Returns a string describing *data in a type specific manner
488 Easier to parse than PR_ValueString
491 char *PRVM_UglyValueString (prvm_prog_t *prog, etype_t type, prvm_eval_t *val, char *line, size_t linelength)
498 type = (etype_t)((int)type & ~DEF_SAVEGLOBAL);
503 // Parse the string a bit to turn special characters
504 // (like newline, specifically) into escape codes,
505 // this fixes saving games from various mods
506 s = PRVM_GetString (prog, val->string);
507 for (i = 0;i < (int)linelength - 2 && *s;)
537 dpsnprintf (line, linelength, "%i", i);
540 if ((unsigned int)val->function < (unsigned int)prog->progs_numfunctions)
542 f = prog->functions + val->function;
543 strlcpy (line, PRVM_GetString (prog, f->s_name), linelength);
546 dpsnprintf (line, linelength, "bad function %i (invalid!)", val->function);
549 def = PRVM_ED_FieldAtOfs ( prog, val->_int );
551 dpsnprintf (line, linelength, ".%s", PRVM_GetString(prog, def->s_name));
553 dpsnprintf (line, linelength, "field%i (invalid!)", val->_int );
556 dpsnprintf (line, linelength, "void");
559 dpsnprintf (line, linelength, FLOAT_LOSSLESS_FORMAT, val->_float);
562 dpsnprintf (line, linelength, VECTOR_LOSSLESS_FORMAT, val->vector[0], val->vector[1], val->vector[2]);
565 dpsnprintf (line, linelength, "bad type %i", type);
576 Returns a string with a description and the contents of a global,
577 padded to 20 field width
580 char *PRVM_GlobalString (prvm_prog_t *prog, int ofs, char *line, size_t linelength)
586 char valuebuf[MAX_INPUTLINE];
588 val = (prvm_eval_t *)&prog->globals.fp[ofs];
589 def = PRVM_ED_GlobalAtOfs(prog, ofs);
591 dpsnprintf (line, linelength, "GLOBAL%i", ofs);
594 s = PRVM_ValueString (prog, (etype_t)def->type, val, valuebuf, sizeof(valuebuf));
595 dpsnprintf (line, linelength, "%s (=%s)", PRVM_GetString(prog, def->s_name), s);
599 //for ( ; i<20 ; i++)
600 // strcat (line," ");
606 char *PRVM_GlobalStringNoContents (prvm_prog_t *prog, int ofs, char *line, size_t linelength)
611 def = PRVM_ED_GlobalAtOfs(prog, ofs);
613 dpsnprintf (line, linelength, "GLOBAL%i", ofs);
615 dpsnprintf (line, linelength, "%s", PRVM_GetString(prog, def->s_name));
618 //for ( ; i<20 ; i++)
619 // strcat (line," ");
633 // LadyHavoc: optimized this to print out much more quickly (tempstring)
634 // LadyHavoc: changed to print out every 4096 characters (incase there are a lot of fields to print)
635 void PRVM_ED_Print(prvm_prog_t *prog, prvm_edict_t *ed, const char *wildcard_fieldname)
643 char tempstring[MAX_INPUTLINE], tempstring2[260]; // temporary string buffers
644 char valuebuf[MAX_INPUTLINE];
646 if (ed->priv.required->free)
648 Con_Printf("%s: FREE\n",prog->name);
653 dpsnprintf(tempstring, sizeof(tempstring), "\n%s EDICT %i:\n", prog->name, PRVM_NUM_FOR_EDICT(ed));
654 for (i = 1;i < prog->numfielddefs;i++)
656 d = &prog->fielddefs[i];
657 name = PRVM_GetString(prog, d->s_name);
658 if(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
659 continue; // skip _x, _y, _z vars
661 // Check Field Name Wildcard
662 if(wildcard_fieldname)
663 if( !matchpattern(name, wildcard_fieldname, 1) )
664 // Didn't match; skip
667 val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
669 // if the value is still all 0, skip the field
670 type = d->type & ~DEF_SAVEGLOBAL;
672 for (j=0 ; j<prvm_type_size[type] ; j++)
675 if (j == prvm_type_size[type])
678 if (strlen(name) > sizeof(tempstring2)-4)
680 memcpy (tempstring2, name, sizeof(tempstring2)-4);
681 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
682 tempstring2[sizeof(tempstring2)-1] = 0;
685 strlcat(tempstring, name, sizeof(tempstring));
686 for (l = strlen(name);l < 14;l++)
687 strlcat(tempstring, " ", sizeof(tempstring));
688 strlcat(tempstring, " ", sizeof(tempstring));
690 name = PRVM_ValueString(prog, (etype_t)d->type, val, valuebuf, sizeof(valuebuf));
691 if (strlen(name) > sizeof(tempstring2)-4)
693 memcpy (tempstring2, name, sizeof(tempstring2)-4);
694 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
695 tempstring2[sizeof(tempstring2)-1] = 0;
698 strlcat(tempstring, name, sizeof(tempstring));
699 strlcat(tempstring, "\n", sizeof(tempstring));
700 if (strlen(tempstring) >= sizeof(tempstring)/2)
702 Con_Print(tempstring);
707 Con_Print(tempstring);
717 extern cvar_t developer_entityparsing;
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);
1167 if (cvar && cvar->flags & CVAR_READONLY)
1169 Con_Printf("prvm_edictget: %s is read-only\n", cvar->name);
1172 Cvar_Get(cmd->cvars, Cmd_Argv(cmd, 4), s, cmd->cvars_flagsmask, NULL);
1175 Con_Printf("%s\n", s);
1181 static void PRVM_ED_GlobalGet_f(cmd_state_t *cmd)
1187 char valuebuf[MAX_INPUTLINE];
1189 if(Cmd_Argc(cmd) != 3 && Cmd_Argc(cmd) != 4)
1191 Con_Print("prvm_globalget <program name> <global> [<cvar>]\n");
1195 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
1198 key = PRVM_ED_FindGlobal(prog, Cmd_Argv(cmd, 2));
1201 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(cmd, 2), Cmd_Argv(cmd, 1) );
1205 v = (prvm_eval_t *) &prog->globals.fp[key->ofs];
1206 s = PRVM_UglyValueString(prog, (etype_t)key->type, v, valuebuf, sizeof(valuebuf));
1207 if(Cmd_Argc(cmd) == 4)
1209 cvar_t *cvar = Cvar_FindVar(cmd->cvars, Cmd_Argv(cmd, 3), cmd->cvars_flagsmask);
1210 if (cvar && cvar->flags & CVAR_READONLY)
1212 Con_Printf("prvm_globalget: %s is read-only\n", cvar->name);
1215 Cvar_Get(cmd->cvars, Cmd_Argv(cmd, 3), s, cmd->cvars_flagsmask, NULL);
1218 Con_Printf("%s\n", s);
1228 Console command to set a field of a specified edict
1231 static void PRVM_ED_EdictSet_f(cmd_state_t *cmd)
1237 if(Cmd_Argc(cmd) != 5)
1239 Con_Print("prvm_edictset <program name> <edict number> <field> <value>\n");
1243 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
1246 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(cmd, 2)));
1248 if((key = PRVM_ED_FindField(prog, Cmd_Argv(cmd, 3))) == 0)
1249 Con_Printf("Key %s not found !\n", Cmd_Argv(cmd, 3));
1251 PRVM_ED_ParseEpair(prog, ed, key, Cmd_Argv(cmd, 4), true);
1255 ====================
1258 Parses an edict out of the given string, returning the new position
1259 ed should be a properly initialized empty edict.
1260 Used for initial level load and for savegames.
1261 ====================
1263 const char *PRVM_ED_ParseEdict (prvm_prog_t *prog, const char *data, prvm_edict_t *ent)
1273 // go through all the dictionary pairs
1277 if (!COM_ParseToken_Simple(&data, false, false, true))
1278 prog->error_cmd("PRVM_ED_ParseEdict: EOF without closing brace");
1279 if (developer_entityparsing.integer)
1280 Con_Printf("Key: \"%s\"", com_token);
1281 if (com_token[0] == '}')
1284 // anglehack is to allow QuakeEd to write single scalar angles
1285 // and allow them to be turned into vectors. (FIXME...)
1286 if (!strcmp(com_token, "angle"))
1288 strlcpy (com_token, "angles", sizeof(com_token));
1294 // FIXME: change light to _light to get rid of this hack
1295 if (!strcmp(com_token, "light"))
1296 strlcpy (com_token, "light_lev", sizeof(com_token)); // hack for single light def
1298 strlcpy (keyname, com_token, sizeof(keyname));
1300 // another hack to fix keynames with trailing spaces
1301 n = strlen(keyname);
1302 while (n && keyname[n-1] == ' ')
1309 if (!COM_ParseToken_Simple(&data, false, false, true))
1310 prog->error_cmd("PRVM_ED_ParseEdict: EOF without closing brace");
1311 if (developer_entityparsing.integer)
1312 Con_Printf(" \"%s\"\n", com_token);
1314 if (com_token[0] == '}')
1315 prog->error_cmd("PRVM_ED_ParseEdict: closing brace without data");
1319 // ignore attempts to set key "" (this problem occurs in nehahra neh1m8.bsp)
1323 // keynames with a leading underscore are used for utility comments,
1324 // and are immediately discarded by quake
1325 if (keyname[0] == '_')
1328 key = PRVM_ED_FindField (prog, keyname);
1331 Con_DPrintf("%s: '%s' is not a field\n", prog->name, keyname);
1338 strlcpy (temp, com_token, sizeof(temp));
1339 dpsnprintf (com_token, sizeof(com_token), "0 %s 0", temp);
1342 if (!PRVM_ED_ParseEpair(prog, ent, key, com_token, strcmp(keyname, "wad") != 0))
1343 prog->error_cmd("PRVM_ED_ParseEdict: parse error");
1347 ent->priv.required->free = true;
1348 ent->priv.required->freetime = realtime;
1357 PRVM_ED_LoadFromFile
1359 The entities are directly placed in the array, rather than allocated with
1360 PRVM_ED_Alloc, because otherwise an error loading the map would have entity
1361 number references out of order.
1363 Creates a server's entity / program execution context by
1364 parsing textual entity definitions out of an ent file.
1366 Used for both fresh maps and savegame loads. A fresh map would also need
1367 to call PRVM_ED_CallSpawnFunctions () to let the objects initialize themselves.
1370 void PRVM_ED_LoadFromFile (prvm_prog_t *prog, const char *data)
1373 int parsed, inhibited, spawned, died;
1374 const char *funcname;
1383 prvm_reuseedicts_always_allow = realtime;
1388 // parse the opening brace
1389 if (!COM_ParseToken_Simple(&data, false, false, true))
1391 if (com_token[0] != '{')
1392 prog->error_cmd("PRVM_ED_LoadFromFile: %s: found %s when expecting {", prog->name, com_token);
1394 // CHANGED: this is not conform to PR_LoadFromFile
1395 if(prog->loadintoworld)
1397 prog->loadintoworld = false;
1398 ent = PRVM_EDICT_NUM(0);
1401 ent = PRVM_ED_Alloc(prog);
1404 if (ent != prog->edicts) // hack
1405 memset (ent->fields.fp, 0, prog->entityfields * sizeof(prvm_vec_t));
1407 data = PRVM_ED_ParseEdict (prog, data, ent);
1410 // remove the entity ?
1411 if(!prog->load_edict(prog, ent))
1413 PRVM_ED_Free(prog, ent);
1418 if (PRVM_serverfunction(SV_OnEntityPreSpawnFunction))
1421 PRVM_serverglobalfloat(time) = sv.time;
1422 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1423 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityPreSpawnFunction), "QC function SV_OnEntityPreSpawnFunction is missing");
1426 if(ent->priv.required->free)
1433 // immediately call spawn function, but only if there is a self global and a classname
1435 if(!ent->priv.required->free)
1437 if (!PRVM_alledictstring(ent, classname))
1439 Con_Print("No classname for:\n");
1440 PRVM_ED_Print(prog, ent, NULL);
1441 PRVM_ED_Free (prog, ent);
1445 // look for the spawn function
1446 funcname = PRVM_GetString(prog, PRVM_alledictstring(ent, classname));
1447 func = PRVM_ED_FindFunction (prog, va(vabuf, sizeof(vabuf), "spawnfunc_%s", funcname));
1449 if(!PRVM_allglobalfloat(require_spawnfunc_prefix))
1450 func = PRVM_ED_FindFunction (prog, funcname);
1454 // check for OnEntityNoSpawnFunction
1455 if (PRVM_serverfunction(SV_OnEntityNoSpawnFunction))
1458 PRVM_serverglobalfloat(time) = sv.time;
1459 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1460 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityNoSpawnFunction), "QC function SV_OnEntityNoSpawnFunction is missing");
1464 if (developer.integer > 0) // don't confuse non-developers with errors
1466 Con_Print("No spawn function for:\n");
1467 PRVM_ED_Print(prog, ent, NULL);
1469 PRVM_ED_Free (prog, ent);
1470 continue; // not included in "inhibited" count
1476 PRVM_serverglobalfloat(time) = sv.time;
1477 PRVM_allglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1478 prog->ExecuteProgram(prog, func - prog->functions, "");
1482 if(!ent->priv.required->free)
1483 if (PRVM_serverfunction(SV_OnEntityPostSpawnFunction))
1486 PRVM_serverglobalfloat(time) = sv.time;
1487 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1488 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityPostSpawnFunction), "QC function SV_OnEntityPostSpawnFunction is missing");
1492 if (ent->priv.required->free)
1496 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);
1498 prvm_reuseedicts_always_allow = 0;
1501 static void PRVM_FindOffsets(prvm_prog_t *prog)
1503 // field and global searches use -1 for NULL
1504 memset(&prog->fieldoffsets, -1, sizeof(prog->fieldoffsets));
1505 memset(&prog->globaloffsets, -1, sizeof(prog->globaloffsets));
1506 // function searches use 0 for NULL
1507 memset(&prog->funcoffsets, 0, sizeof(prog->funcoffsets));
1508 #define PRVM_DECLARE_serverglobalfloat(x)
1509 #define PRVM_DECLARE_serverglobalvector(x)
1510 #define PRVM_DECLARE_serverglobalstring(x)
1511 #define PRVM_DECLARE_serverglobaledict(x)
1512 #define PRVM_DECLARE_serverglobalfunction(x)
1513 #define PRVM_DECLARE_clientglobalfloat(x)
1514 #define PRVM_DECLARE_clientglobalvector(x)
1515 #define PRVM_DECLARE_clientglobalstring(x)
1516 #define PRVM_DECLARE_clientglobaledict(x)
1517 #define PRVM_DECLARE_clientglobalfunction(x)
1518 #define PRVM_DECLARE_menuglobalfloat(x)
1519 #define PRVM_DECLARE_menuglobalvector(x)
1520 #define PRVM_DECLARE_menuglobalstring(x)
1521 #define PRVM_DECLARE_menuglobaledict(x)
1522 #define PRVM_DECLARE_menuglobalfunction(x)
1523 #define PRVM_DECLARE_serverfieldfloat(x)
1524 #define PRVM_DECLARE_serverfieldvector(x)
1525 #define PRVM_DECLARE_serverfieldstring(x)
1526 #define PRVM_DECLARE_serverfieldedict(x)
1527 #define PRVM_DECLARE_serverfieldfunction(x)
1528 #define PRVM_DECLARE_clientfieldfloat(x)
1529 #define PRVM_DECLARE_clientfieldvector(x)
1530 #define PRVM_DECLARE_clientfieldstring(x)
1531 #define PRVM_DECLARE_clientfieldedict(x)
1532 #define PRVM_DECLARE_clientfieldfunction(x)
1533 #define PRVM_DECLARE_menufieldfloat(x)
1534 #define PRVM_DECLARE_menufieldvector(x)
1535 #define PRVM_DECLARE_menufieldstring(x)
1536 #define PRVM_DECLARE_menufieldedict(x)
1537 #define PRVM_DECLARE_menufieldfunction(x)
1538 #define PRVM_DECLARE_serverfunction(x)
1539 #define PRVM_DECLARE_clientfunction(x)
1540 #define PRVM_DECLARE_menufunction(x)
1541 #define PRVM_DECLARE_field(x) prog->fieldoffsets.x = PRVM_ED_FindFieldOffset(prog, #x);
1542 #define PRVM_DECLARE_global(x) prog->globaloffsets.x = PRVM_ED_FindGlobalOffset(prog, #x);
1543 #define PRVM_DECLARE_function(x) prog->funcoffsets.x = PRVM_ED_FindFunctionOffset(prog, #x);
1544 #include "prvm_offsets.h"
1545 #undef PRVM_DECLARE_serverglobalfloat
1546 #undef PRVM_DECLARE_serverglobalvector
1547 #undef PRVM_DECLARE_serverglobalstring
1548 #undef PRVM_DECLARE_serverglobaledict
1549 #undef PRVM_DECLARE_serverglobalfunction
1550 #undef PRVM_DECLARE_clientglobalfloat
1551 #undef PRVM_DECLARE_clientglobalvector
1552 #undef PRVM_DECLARE_clientglobalstring
1553 #undef PRVM_DECLARE_clientglobaledict
1554 #undef PRVM_DECLARE_clientglobalfunction
1555 #undef PRVM_DECLARE_menuglobalfloat
1556 #undef PRVM_DECLARE_menuglobalvector
1557 #undef PRVM_DECLARE_menuglobalstring
1558 #undef PRVM_DECLARE_menuglobaledict
1559 #undef PRVM_DECLARE_menuglobalfunction
1560 #undef PRVM_DECLARE_serverfieldfloat
1561 #undef PRVM_DECLARE_serverfieldvector
1562 #undef PRVM_DECLARE_serverfieldstring
1563 #undef PRVM_DECLARE_serverfieldedict
1564 #undef PRVM_DECLARE_serverfieldfunction
1565 #undef PRVM_DECLARE_clientfieldfloat
1566 #undef PRVM_DECLARE_clientfieldvector
1567 #undef PRVM_DECLARE_clientfieldstring
1568 #undef PRVM_DECLARE_clientfieldedict
1569 #undef PRVM_DECLARE_clientfieldfunction
1570 #undef PRVM_DECLARE_menufieldfloat
1571 #undef PRVM_DECLARE_menufieldvector
1572 #undef PRVM_DECLARE_menufieldstring
1573 #undef PRVM_DECLARE_menufieldedict
1574 #undef PRVM_DECLARE_menufieldfunction
1575 #undef PRVM_DECLARE_serverfunction
1576 #undef PRVM_DECLARE_clientfunction
1577 #undef PRVM_DECLARE_menufunction
1578 #undef PRVM_DECLARE_field
1579 #undef PRVM_DECLARE_global
1580 #undef PRVM_DECLARE_function
1585 typedef struct dpfield_s
1592 #define DPFIELDS (sizeof(dpfields) / sizeof(dpfield_t))
1594 dpfield_t dpfields[] =
1605 #define PO_HASHSIZE 16384
1606 typedef struct po_string_s
1609 struct po_string_s *nextonhashchain;
1614 po_string_t *hashtable[PO_HASHSIZE];
1617 static void PRVM_PO_UnparseString(char *out, const char *in, size_t outsize)
1626 case '\a': if(outsize >= 2) { *out++ = '\\'; *out++ = 'a'; outsize -= 2; } break;
1627 case '\b': if(outsize >= 2) { *out++ = '\\'; *out++ = 'b'; outsize -= 2; } break;
1628 case '\t': if(outsize >= 2) { *out++ = '\\'; *out++ = 't'; outsize -= 2; } break;
1629 case '\r': if(outsize >= 2) { *out++ = '\\'; *out++ = 'r'; outsize -= 2; } break;
1630 case '\n': if(outsize >= 2) { *out++ = '\\'; *out++ = 'n'; outsize -= 2; } break;
1631 case '\\': if(outsize >= 2) { *out++ = '\\'; *out++ = '\\'; outsize -= 2; } break;
1632 case '"': if(outsize >= 2) { *out++ = '\\'; *out++ = '"'; outsize -= 2; } break;
1634 if(*in >= 0 && *in <= 0x1F)
1639 *out++ = '0' + ((*in & 0700) >> 6);
1640 *out++ = '0' + ((*in & 0070) >> 3);
1641 *out++ = '0' + (*in & 0007) ;
1658 static void PRVM_PO_ParseString(char *out, const char *in, size_t outsize)
1671 case 'a': if(outsize > 0) { *out++ = '\a'; --outsize; } break;
1672 case 'b': if(outsize > 0) { *out++ = '\b'; --outsize; } break;
1673 case 't': if(outsize > 0) { *out++ = '\t'; --outsize; } break;
1674 case 'r': if(outsize > 0) { *out++ = '\r'; --outsize; } break;
1675 case 'n': if(outsize > 0) { *out++ = '\n'; --outsize; } break;
1676 case '\\': if(outsize > 0) { *out++ = '\\'; --outsize; } break;
1677 case '"': if(outsize > 0) { *out++ = '"'; --outsize; } break;
1678 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
1682 if(*in >= '0' && *in <= '7')
1685 *out = (*out << 3) | (*in - '0');
1688 if(*in >= '0' && *in <= '7')
1691 *out = (*out << 3) | (*in - '0');
1702 if(outsize > 0) { *out++ = *in; --outsize; }
1717 static po_t *PRVM_PO_Load(const char *filename, const char *filename2, mempool_t *pool)
1722 char inbuf[MAX_INPUTLINE];
1723 char decodedbuf[MAX_INPUTLINE];
1726 po_string_t thisstr;
1729 for (i = 0; i < 2; ++i)
1731 const char *buf = (const char *)
1732 FS_LoadFile((i > 0 ? filename : filename2), pool, true, NULL);
1733 // first read filename2, then read filename
1734 // so that progs.dat.de.po wins over common.de.po
1735 // and within file, last item wins
1742 po = (po_t *)Mem_Alloc(pool, sizeof(*po));
1743 memset(po, 0, sizeof(*po));
1746 memset(&thisstr, 0, sizeof(thisstr)); // hush compiler warning
1754 p = strchr(p, '\n');
1760 if(*p == '\r' || *p == '\n')
1765 if(!strncmp(p, "msgid \"", 7))
1770 else if(!strncmp(p, "msgstr \"", 8))
1777 p = strchr(p, '\n');
1787 q = strchr(p, '\n');
1794 if((size_t)(q - p) >= (size_t) sizeof(inbuf))
1796 strlcpy(inbuf, p, q - p); // not - 1, because this adds a NUL
1797 PRVM_PO_ParseString(decodedbuf + decodedpos, inbuf, sizeof(decodedbuf) - decodedpos);
1798 decodedpos += strlen(decodedbuf + decodedpos);
1808 Mem_Free(thisstr.key);
1809 thisstr.key = (char *)Mem_Alloc(pool, decodedpos + 1);
1810 memcpy(thisstr.key, decodedbuf, decodedpos + 1);
1812 else if(decodedpos > 0 && thisstr.key) // skip empty translation results
1814 thisstr.value = (char *)Mem_Alloc(pool, decodedpos + 1);
1815 memcpy(thisstr.value, decodedbuf, decodedpos + 1);
1816 hashindex = CRC_Block((const unsigned char *) thisstr.key, strlen(thisstr.key)) % PO_HASHSIZE;
1817 thisstr.nextonhashchain = po->hashtable[hashindex];
1818 po->hashtable[hashindex] = (po_string_t *)Mem_Alloc(pool, sizeof(thisstr));
1819 memcpy(po->hashtable[hashindex], &thisstr, sizeof(thisstr));
1820 memset(&thisstr, 0, sizeof(thisstr));
1824 Mem_Free((char *) buf);
1829 static const char *PRVM_PO_Lookup(po_t *po, const char *str)
1831 int hashindex = CRC_Block((const unsigned char *) str, strlen(str)) % PO_HASHSIZE;
1832 po_string_t *p = po->hashtable[hashindex];
1835 if(!strcmp(str, p->key))
1837 p = p->nextonhashchain;
1841 static void PRVM_PO_Destroy(po_t *po)
1844 for(i = 0; i < PO_HASHSIZE; ++i)
1846 po_string_t *p = po->hashtable[i];
1850 p = p->nextonhashchain;
1859 void PRVM_LeakTest(prvm_prog_t *prog);
1860 void PRVM_Prog_Reset(prvm_prog_t *prog)
1864 PRVM_LeakTest(prog);
1865 prog->reset_cmd(prog);
1866 Mem_FreePool(&prog->progs_mempool);
1868 PRVM_PO_Destroy((po_t *) prog->po);
1870 memset(prog,0,sizeof(prvm_prog_t));
1871 prog->break_statement = -1;
1872 prog->watch_global_type = ev_void;
1873 prog->watch_field_type = ev_void;
1881 static void PRVM_LoadLNO( prvm_prog_t *prog, const char *progname ) {
1882 fs_offset_t filesize;
1884 unsigned int *header;
1887 FS_StripExtension( progname, filename, sizeof( filename ) );
1888 strlcat( filename, ".lno", sizeof( filename ) );
1890 lno = FS_LoadFile( filename, tempmempool, false, &filesize );
1896 <Spike> SafeWrite (h, &lnotype, sizeof(int));
1897 <Spike> SafeWrite (h, &version, sizeof(int));
1898 <Spike> SafeWrite (h, &numglobaldefs, sizeof(int));
1899 <Spike> SafeWrite (h, &numpr_globals, sizeof(int));
1900 <Spike> SafeWrite (h, &numfielddefs, sizeof(int));
1901 <Spike> SafeWrite (h, &numstatements, sizeof(int));
1902 <Spike> SafeWrite (h, statement_linenums, numstatements*sizeof(int));
1904 if ((unsigned int)filesize < (6 + prog->progs_numstatements) * sizeof(int))
1910 header = (unsigned int *) lno;
1911 if( header[ 0 ] == *(unsigned int *) "LNOF" &&
1912 LittleLong( header[ 1 ] ) == 1 &&
1913 (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs_numglobaldefs &&
1914 (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs_numglobals &&
1915 (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs_numfielddefs &&
1916 (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs_numstatements )
1918 prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof( int ) );
1919 memcpy( prog->statement_linenums, header + 6, prog->progs_numstatements * sizeof( int ) );
1921 /* gmqcc suports columnums */
1922 if ((unsigned int)filesize > ((6 + 2 * prog->progs_numstatements) * sizeof( int )))
1924 prog->statement_columnnums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof( int ) );
1925 memcpy( prog->statement_columnnums, header + 6 + prog->progs_numstatements, prog->progs_numstatements * sizeof( int ) );
1936 static void PRVM_UpdateBreakpoints(prvm_prog_t *prog);
1937 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)
1940 dprograms_t *dprograms;
1941 dstatement_t *instatements;
1942 ddef_t *infielddefs;
1943 ddef_t *inglobaldefs;
1945 dfunction_t *infunctions;
1947 fs_offset_t filesize;
1948 int requiredglobalspace;
1965 prog->error_cmd("PRVM_LoadProgs: there is already a %s program loaded!", prog->name );
1967 Host_LockSession(); // all progs can use the session cvar
1968 Crypto_LoadKeys(); // all progs might use the keys at init time
1972 dprograms = (dprograms_t *) data;
1976 dprograms = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
1977 if (dprograms == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
1978 prog->error_cmd("PRVM_LoadProgs: couldn't load %s for %s", filename, prog->name);
1979 // TODO bounds check header fields (e.g. numstatements), they must never go behind end of file
1981 prog->profiletime = Sys_DirtyTime();
1982 prog->starttime = realtime;
1984 Con_DPrintf("%s programs occupy %iK.\n", prog->name, (int)(filesize/1024));
1986 requiredglobalspace = 0;
1987 for (i = 0;i < numrequiredglobals;i++)
1988 requiredglobalspace += required_global[i].type == ev_vector ? 3 : 1;
1990 prog->filecrc = CRC_Block((unsigned char *)dprograms, filesize);
1992 // byte swap the header
1993 prog->progs_version = LittleLong(dprograms->version);
1994 prog->progs_crc = LittleLong(dprograms->crc);
1995 if (prog->progs_version != PROG_VERSION)
1996 prog->error_cmd("%s: %s has wrong version number (%i should be %i)", prog->name, filename, prog->progs_version, PROG_VERSION);
1997 instatements = (dstatement_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_statements));
1998 prog->progs_numstatements = LittleLong(dprograms->numstatements);
1999 inglobaldefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globaldefs));
2000 prog->progs_numglobaldefs = LittleLong(dprograms->numglobaldefs);
2001 infielddefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_fielddefs));
2002 prog->progs_numfielddefs = LittleLong(dprograms->numfielddefs);
2003 infunctions = (dfunction_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_functions));
2004 prog->progs_numfunctions = LittleLong(dprograms->numfunctions);
2005 instrings = (char *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_strings));
2006 prog->progs_numstrings = LittleLong(dprograms->numstrings);
2007 inglobals = (int *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globals));
2008 prog->progs_numglobals = LittleLong(dprograms->numglobals);
2009 prog->progs_entityfields = LittleLong(dprograms->entityfields);
2011 prog->numstatements = prog->progs_numstatements;
2012 prog->numglobaldefs = prog->progs_numglobaldefs;
2013 prog->numfielddefs = prog->progs_numfielddefs;
2014 prog->numfunctions = prog->progs_numfunctions;
2015 prog->numstrings = prog->progs_numstrings;
2016 prog->numglobals = prog->progs_numglobals;
2017 prog->entityfields = prog->progs_entityfields;
2019 if (LittleLong(dprograms->ofs_strings) + prog->progs_numstrings > (int)filesize)
2020 prog->error_cmd("%s: %s strings go past end of file", prog->name, filename);
2021 prog->strings = (char *)Mem_Alloc(prog->progs_mempool, prog->progs_numstrings);
2022 memcpy(prog->strings, instrings, prog->progs_numstrings);
2023 prog->stringssize = prog->progs_numstrings;
2025 prog->numknownstrings = 0;
2026 prog->maxknownstrings = 0;
2027 prog->knownstrings = NULL;
2028 prog->knownstrings_flags = NULL;
2030 Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
2032 // we need to expand the globaldefs and fielddefs to include engine defs
2033 prog->globaldefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobaldefs + numrequiredglobals) * sizeof(ddef_t));
2034 prog->globals.fp = (prvm_vec_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobals + requiredglobalspace + 2) * sizeof(prvm_vec_t));
2035 // + 2 is because of an otherwise occurring overrun in RETURN instruction
2036 // when trying to return the last or second-last global
2037 // (RETURN always returns a vector, there is no RETURN_F instruction)
2038 prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numfielddefs + numrequiredfields) * sizeof(ddef_t));
2039 // we need to convert the statements to our memory format
2040 prog->statements = (mstatement_t *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(mstatement_t));
2041 // allocate space for profiling statement usage
2042 prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(*prog->statement_profile));
2043 prog->explicit_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(*prog->statement_profile));
2044 // functions need to be converted to the memory format
2045 prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs_numfunctions);
2047 for (i = 0;i < prog->progs_numfunctions;i++)
2049 prog->functions[i].first_statement = LittleLong(infunctions[i].first_statement);
2050 prog->functions[i].parm_start = LittleLong(infunctions[i].parm_start);
2051 prog->functions[i].s_name = LittleLong(infunctions[i].s_name);
2052 prog->functions[i].s_file = LittleLong(infunctions[i].s_file);
2053 prog->functions[i].numparms = LittleLong(infunctions[i].numparms);
2054 prog->functions[i].locals = LittleLong(infunctions[i].locals);
2055 memcpy(prog->functions[i].parm_size, infunctions[i].parm_size, sizeof(infunctions[i].parm_size));
2056 if(prog->functions[i].first_statement >= prog->numstatements)
2057 prog->error_cmd("PRVM_LoadProgs: out of bounds function statement (function %d) in %s", i, prog->name);
2058 // TODO bounds check parm_start, s_name, s_file, numparms, locals, parm_size
2061 // copy the globaldefs to the new globaldefs list
2062 for (i=0 ; i<prog->numglobaldefs ; i++)
2064 prog->globaldefs[i].type = LittleShort(inglobaldefs[i].type);
2065 prog->globaldefs[i].ofs = LittleShort(inglobaldefs[i].ofs);
2066 prog->globaldefs[i].s_name = LittleLong(inglobaldefs[i].s_name);
2067 // TODO bounds check ofs, s_name
2070 // append the required globals
2071 for (i = 0;i < numrequiredglobals;i++)
2073 prog->globaldefs[prog->numglobaldefs].type = required_global[i].type;
2074 prog->globaldefs[prog->numglobaldefs].ofs = prog->numglobals;
2075 prog->globaldefs[prog->numglobaldefs].s_name = PRVM_SetEngineString(prog, required_global[i].name);
2076 if (prog->globaldefs[prog->numglobaldefs].type == ev_vector)
2077 prog->numglobals += 3;
2080 prog->numglobaldefs++;
2083 // copy the progs fields to the new fields list
2084 for (i = 0;i < prog->numfielddefs;i++)
2086 prog->fielddefs[i].type = LittleShort(infielddefs[i].type);
2087 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
2088 prog->error_cmd("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", prog->name);
2089 prog->fielddefs[i].ofs = LittleShort(infielddefs[i].ofs);
2090 prog->fielddefs[i].s_name = LittleLong(infielddefs[i].s_name);
2091 // TODO bounds check ofs, s_name
2094 // append the required fields
2095 for (i = 0;i < numrequiredfields;i++)
2097 prog->fielddefs[prog->numfielddefs].type = required_field[i].type;
2098 prog->fielddefs[prog->numfielddefs].ofs = prog->entityfields;
2099 prog->fielddefs[prog->numfielddefs].s_name = PRVM_SetEngineString(prog, required_field[i].name);
2100 if (prog->fielddefs[prog->numfielddefs].type == ev_vector)
2101 prog->entityfields += 3;
2103 prog->entityfields++;
2104 prog->numfielddefs++;
2107 // LadyHavoc: TODO: reorder globals to match engine struct
2108 // LadyHavoc: TODO: reorder fields to match engine struct
2109 #define remapglobal(index) (index)
2110 #define remapfield(index) (index)
2113 // FIXME: LadyHavoc: this uses a crude way to identify integer constants, rather than checking for matching globaldefs and checking their type
2114 for (i = 0;i < prog->progs_numglobals;i++)
2116 u.i = LittleLong(inglobals[i]);
2117 // most globals are 0, we only need to deal with the ones that are not
2120 d = u.i & 0xFF800000;
2121 if ((d == 0xFF800000) || (d == 0))
2123 // Looks like an integer (expand to int64)
2124 prog->globals.ip[remapglobal(i)] = u.i;
2128 // Looks like a float (expand to double)
2129 prog->globals.fp[remapglobal(i)] = u.f;
2134 // LadyHavoc: TODO: support 32bit progs statement formats
2135 // copy, remap globals in statements, bounds check
2136 for (i = 0;i < prog->progs_numstatements;i++)
2138 op = (opcode_t)LittleShort(instatements[i].op);
2139 a = (unsigned short)LittleShort(instatements[i].a);
2140 b = (unsigned short)LittleShort(instatements[i].b);
2141 c = (unsigned short)LittleShort(instatements[i].c);
2147 if (a >= prog->progs_numglobals || b + i < 0 || b + i >= prog->progs_numstatements)
2148 prog->error_cmd("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", i, prog->name);
2149 prog->statements[i].op = op;
2150 prog->statements[i].operand[0] = remapglobal(a);
2151 prog->statements[i].operand[1] = -1;
2152 prog->statements[i].operand[2] = -1;
2153 prog->statements[i].jumpabsolute = i + b;
2157 if (a + i < 0 || a + i >= prog->progs_numstatements)
2158 prog->error_cmd("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, prog->name);
2159 prog->statements[i].op = op;
2160 prog->statements[i].operand[0] = -1;
2161 prog->statements[i].operand[1] = -1;
2162 prog->statements[i].operand[2] = -1;
2163 prog->statements[i].jumpabsolute = i + a;
2166 Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", (int)op, i, prog->name);
2168 // global global global
2203 if (a >= prog->progs_numglobals || b >= prog->progs_numglobals || c >= prog->progs_numglobals)
2204 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
2205 prog->statements[i].op = op;
2206 prog->statements[i].operand[0] = remapglobal(a);
2207 prog->statements[i].operand[1] = remapglobal(b);
2208 prog->statements[i].operand[2] = remapglobal(c);
2209 prog->statements[i].jumpabsolute = -1;
2211 // global none global
2217 if (a >= prog->progs_numglobals || c >= prog->progs_numglobals)
2218 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2219 prog->statements[i].op = op;
2220 prog->statements[i].operand[0] = remapglobal(a);
2221 prog->statements[i].operand[1] = -1;
2222 prog->statements[i].operand[2] = remapglobal(c);
2223 prog->statements[i].jumpabsolute = -1;
2239 if (a >= prog->progs_numglobals || b >= prog->progs_numglobals)
2240 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2241 prog->statements[i].op = op;
2242 prog->statements[i].operand[0] = remapglobal(a);
2243 prog->statements[i].operand[1] = remapglobal(b);
2244 prog->statements[i].operand[2] = -1;
2245 prog->statements[i].jumpabsolute = -1;
2249 if ( a < prog->progs_numglobals)
2250 if ( prog->globals.ip[remapglobal(a)] >= 0 )
2251 if ( prog->globals.ip[remapglobal(a)] < prog->progs_numfunctions )
2252 if ( prog->functions[prog->globals.ip[remapglobal(a)]].first_statement == -642 )
2253 ++prog->numexplicitcoveragestatements;
2264 if ( a >= prog->progs_numglobals)
2265 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2266 prog->statements[i].op = op;
2267 prog->statements[i].operand[0] = remapglobal(a);
2268 prog->statements[i].operand[1] = -1;
2269 prog->statements[i].operand[2] = -1;
2270 prog->statements[i].jumpabsolute = -1;
2274 if(prog->numstatements < 1)
2276 prog->error_cmd("PRVM_LoadProgs: empty program in %s", prog->name);
2278 else switch(prog->statements[prog->numstatements - 1].op)
2285 prog->error_cmd("PRVM_LoadProgs: program may fall off the edge (does not end with RETURN, GOTO or DONE) in %s", prog->name);
2289 // we're done with the file now
2291 Mem_Free(dprograms);
2294 // check required functions
2295 for(i=0 ; i < numrequiredfunc ; i++)
2296 if(PRVM_ED_FindFunction(prog, required_func[i]) == 0)
2297 prog->error_cmd("%s: %s not found in %s",prog->name, required_func[i], filename);
2299 PRVM_LoadLNO(prog, filename);
2301 PRVM_Init_Exec(prog);
2303 if(*prvm_language.string)
2304 // in CSQC we really shouldn't be able to change how stuff works... sorry for now
2305 // later idea: include a list of authorized .po file checksums with the csprogs
2307 qboolean deftrans = prog == CLVM_prog;
2308 const char *realfilename = (prog != CLVM_prog ? filename : csqc_progname.string);
2309 if(deftrans) // once we have dotranslate_ strings, ALWAYS use the opt-in method!
2311 for (i=0 ; i<prog->numglobaldefs ; i++)
2314 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2315 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2316 if(name && !strncmp(name, "dotranslate_", 12))
2323 if(!strcmp(prvm_language.string, "dump"))
2325 qfile_t *f = FS_OpenRealFile(va(vabuf, sizeof(vabuf), "%s.pot", realfilename), "w", false);
2326 Con_Printf("Dumping to %s.pot\n", realfilename);
2329 for (i=0 ; i<prog->numglobaldefs ; i++)
2332 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2333 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2334 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2336 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2337 const char *value = PRVM_GetString(prog, val->string);
2340 char buf[MAX_INPUTLINE];
2341 PRVM_PO_UnparseString(buf, value, sizeof(buf));
2342 FS_Printf(f, "msgid \"%s\"\nmsgstr \"\"\n\n", buf);
2351 po_t *po = PRVM_PO_Load(
2352 va(vabuf, sizeof(vabuf), "%s.%s.po", realfilename, prvm_language.string),
2353 va(vabuf2, sizeof(vabuf2), "common.%s.po", prvm_language.string),
2354 prog->progs_mempool);
2357 for (i=0 ; i<prog->numglobaldefs ; i++)
2360 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2361 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2362 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2364 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2365 const char *value = PRVM_GetString(prog, val->string);
2368 value = PRVM_PO_Lookup(po, value);
2370 val->string = PRVM_SetEngineString(prog, value);
2378 for (cvar = prog->console_cmd->cvars->vars; cvar; cvar = cvar->next)
2379 cvar->globaldefindex[prog - prvm_prog_list] = -1;
2381 for (i=0 ; i<prog->numglobaldefs ; i++)
2384 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2385 //Con_Printf("found var %s\n", name);
2387 && !strncmp(name, "autocvar_", 9)
2388 && !(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
2391 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2392 cvar = Cvar_FindVar(prog->console_cmd->cvars, name + 9, prog->console_cmd->cvars_flagsmask);
2393 //Con_Printf("PRVM_LoadProgs: autocvar global %s in %s, processing...\n", name, prog->name);
2398 Con_DPrintf("PRVM_LoadProgs: no cvar for autocvar global %s in %s, creating...\n", name, prog->name);
2399 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2402 if((float)((int)(val->_float)) == val->_float)
2403 dpsnprintf(buf, sizeof(buf), "%i", (int)(val->_float));
2405 dpsnprintf(buf, sizeof(buf), "%.9g", val->_float);
2409 dpsnprintf(buf, sizeof(buf), "%.9g %.9g %.9g", val->vector[0], val->vector[1], val->vector[2]); value = buf;
2412 value = PRVM_GetString(prog, val->string);
2415 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2418 cvar = Cvar_Get(prog->console_cmd->cvars, name + 9, value, prog->console_cmd->cvars_flagsmask, NULL);
2419 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2421 val->string = PRVM_SetEngineString(prog, cvar->string);
2422 cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2425 prog->error_cmd("PRVM_LoadProgs: could not create cvar for autocvar global %s in %s", name, prog->name);
2426 cvar->globaldefindex[prog - prvm_prog_list] = i;
2428 else if((cvar->flags & CVAR_PRIVATE) == 0)
2430 // MUST BE SYNCED WITH cvar.c Cvar_Set
2433 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2436 val->_float = cvar->value;
2440 VectorClear(val->vector);
2441 for (j = 0;j < 3;j++)
2443 while (*s && ISWHITESPACE(*s))
2447 val->vector[j] = atof(s);
2448 while (!ISWHITESPACE(*s))
2455 val->string = PRVM_SetEngineString(prog, cvar->string);
2456 cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2459 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2462 cvar->globaldefindex[prog - prvm_prog_list] = i;
2465 Con_Printf("PRVM_LoadProgs: private cvar for autocvar global %s in %s\n", name, prog->name);
2471 prog->loaded = TRUE;
2473 PRVM_UpdateBreakpoints(prog);
2475 // set flags & ddef_ts in prog
2479 PRVM_FindOffsets(prog);
2481 prog->init_cmd(prog);
2484 PRVM_MEM_Alloc(prog);
2486 // Inittime is at least the time when this function finished. However,
2487 // later events may bump it.
2488 prog->inittime = realtime;
2492 static void PRVM_Fields_f(cmd_state_t *cmd)
2495 int i, j, ednum, used, usedamount;
2497 char tempstring[MAX_INPUTLINE], tempstring2[260];
2507 Con_Print("no progs loaded\n");
2512 if(Cmd_Argc(cmd) != 2)
2514 Con_Print("prvm_fields <program name>\n");
2518 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2521 counts = (int *)Mem_Alloc(tempmempool, prog->numfielddefs * sizeof(int));
2522 for (ednum = 0;ednum < prog->max_edicts;ednum++)
2524 ed = PRVM_EDICT_NUM(ednum);
2525 if (ed->priv.required->free)
2527 for (i = 1;i < prog->numfielddefs;i++)
2529 d = &prog->fielddefs[i];
2530 name = PRVM_GetString(prog, d->s_name);
2531 if (name[strlen(name)-2] == '_')
2532 continue; // skip _x, _y, _z vars
2533 val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
2534 // if the value is still all 0, skip the field
2535 for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
2537 if (val->ivector[j])
2548 for (i = 0;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 switch(d->type & ~DEF_SAVEGLOBAL)
2557 strlcat(tempstring, "string ", sizeof(tempstring));
2560 strlcat(tempstring, "entity ", sizeof(tempstring));
2563 strlcat(tempstring, "function ", sizeof(tempstring));
2566 strlcat(tempstring, "field ", sizeof(tempstring));
2569 strlcat(tempstring, "void ", sizeof(tempstring));
2572 strlcat(tempstring, "float ", sizeof(tempstring));
2575 strlcat(tempstring, "vector ", sizeof(tempstring));
2578 strlcat(tempstring, "pointer ", sizeof(tempstring));
2581 dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
2582 strlcat(tempstring, tempstring2, sizeof(tempstring));
2585 if (strlen(name) > sizeof(tempstring2)-4)
2587 memcpy (tempstring2, name, sizeof(tempstring2)-4);
2588 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
2589 tempstring2[sizeof(tempstring2)-1] = 0;
2592 strlcat(tempstring, name, sizeof(tempstring));
2593 for (j = (int)strlen(name);j < 25;j++)
2594 strlcat(tempstring, " ", sizeof(tempstring));
2595 dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
2596 strlcat(tempstring, tempstring2, sizeof(tempstring));
2597 strlcat(tempstring, "\n", sizeof(tempstring));
2598 if (strlen(tempstring) >= sizeof(tempstring)/2)
2600 Con_Print(tempstring);
2606 usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
2610 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);
2613 static void PRVM_Globals_f(cmd_state_t *cmd)
2617 const char *wildcard;
2623 Con_Print("no progs loaded\n");
2626 if(Cmd_Argc (cmd) < 2 || Cmd_Argc(cmd) > 3)
2628 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
2632 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2635 if( Cmd_Argc(cmd) == 3)
2636 wildcard = Cmd_Argv(cmd, 2);
2640 Con_Printf("%s :", prog->name);
2642 for (i = 0;i < prog->numglobaldefs;i++)
2645 if( !matchpattern( PRVM_GetString(prog, prog->globaldefs[i].s_name), wildcard, 1) )
2650 Con_Printf("%s\n", PRVM_GetString(prog, prog->globaldefs[i].s_name));
2652 Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->numglobals, numculled, prog->numglobals * 4);
2660 static void PRVM_Global_f(cmd_state_t *cmd)
2664 char valuebuf[MAX_INPUTLINE];
2665 if( Cmd_Argc(cmd) != 3 ) {
2666 Con_Printf( "prvm_global <program name> <global name>\n" );
2670 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2673 global = PRVM_ED_FindGlobal( prog, Cmd_Argv(cmd, 2) );
2675 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(cmd, 2), Cmd_Argv(cmd, 1) );
2677 Con_Printf( "%s: %s\n", Cmd_Argv(cmd, 2), PRVM_ValueString( prog, (etype_t)global->type, PRVM_GLOBALFIELDVALUE(global->ofs), valuebuf, sizeof(valuebuf) ) );
2685 static void PRVM_GlobalSet_f(cmd_state_t *cmd)
2689 if( Cmd_Argc(cmd) != 4 ) {
2690 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2694 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2697 global = PRVM_ED_FindGlobal( prog, Cmd_Argv(cmd, 2) );
2699 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(cmd, 2), Cmd_Argv(cmd, 1) );
2701 PRVM_ED_ParseEpair( prog, NULL, global, Cmd_Argv(cmd, 3), true );
2705 ======================
2706 Break- and Watchpoints
2707 ======================
2711 char break_statement[256];
2712 char watch_global[256];
2714 char watch_field[256];
2717 static debug_data_t debug_data[PRVM_PROG_MAX];
2719 void PRVM_Breakpoint(prvm_prog_t *prog, int stack_index, const char *text)
2722 Con_Printf("PRVM_Breakpoint: %s\n", text);
2723 PRVM_PrintState(prog, stack_index);
2724 if (prvm_breakpointdump.integer)
2725 Host_Savegame_to(prog, va(vabuf, sizeof(vabuf), "breakpoint-%s.dmp", prog->name));
2728 void PRVM_Watchpoint(prvm_prog_t *prog, int stack_index, const char *text, etype_t type, prvm_eval_t *o, prvm_eval_t *n)
2730 size_t sz = sizeof(prvm_vec_t) * ((type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2731 if (memcmp(o, n, sz))
2734 char valuebuf_o[128];
2735 char valuebuf_n[128];
2736 PRVM_UglyValueString(prog, type, o, valuebuf_o, sizeof(valuebuf_o));
2737 PRVM_UglyValueString(prog, type, n, valuebuf_n, sizeof(valuebuf_n));
2738 dpsnprintf(buf, sizeof(buf), "%s: %s -> %s", text, valuebuf_o, valuebuf_n);
2739 PRVM_Breakpoint(prog, stack_index, buf);
2744 static void PRVM_UpdateBreakpoints(prvm_prog_t *prog)
2746 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2749 if (debug->break_statement[0])
2751 if (debug->break_statement[0] >= '0' && debug->break_statement[0] <= '9')
2753 prog->break_statement = atoi(debug->break_statement);
2754 prog->break_stack_index = 0;
2759 func = PRVM_ED_FindFunction (prog, debug->break_statement);
2762 Con_Printf("%s progs: no function or statement named %s to break on!\n", prog->name, debug->break_statement);
2763 prog->break_statement = -1;
2767 prog->break_statement = func->first_statement;
2768 prog->break_stack_index = 1;
2771 if (prog->break_statement >= -1)
2772 Con_Printf("%s progs: breakpoint is at statement %d\n", prog->name, prog->break_statement);
2775 prog->break_statement = -1;
2777 if (debug->watch_global[0])
2779 ddef_t *global = PRVM_ED_FindGlobal( prog, debug->watch_global );
2782 Con_Printf( "%s progs: no global named '%s' to watch!\n", prog->name, debug->watch_global );
2783 prog->watch_global_type = ev_void;
2787 size_t sz = sizeof(prvm_vec_t) * ((global->type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2788 prog->watch_global = global->ofs;
2789 prog->watch_global_type = (etype_t)global->type;
2790 memcpy(&prog->watch_global_value, PRVM_GLOBALFIELDVALUE(prog->watch_global), sz);
2792 if (prog->watch_global_type != ev_void)
2793 Con_Printf("%s progs: global watchpoint is at global index %d\n", prog->name, prog->watch_global);
2796 prog->watch_global_type = ev_void;
2798 if (debug->watch_field[0])
2800 ddef_t *field = PRVM_ED_FindField( prog, debug->watch_field );
2803 Con_Printf( "%s progs: no field named '%s' to watch!\n", prog->name, debug->watch_field );
2804 prog->watch_field_type = ev_void;
2808 size_t sz = sizeof(prvm_vec_t) * ((field->type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2809 prog->watch_edict = debug->watch_edict;
2810 prog->watch_field = field->ofs;
2811 prog->watch_field_type = (etype_t)field->type;
2812 if (prog->watch_edict < prog->num_edicts)
2813 memcpy(&prog->watch_edictfield_value, PRVM_EDICTFIELDVALUE(PRVM_EDICT_NUM(prog->watch_edict), prog->watch_field), sz);
2815 memset(&prog->watch_edictfield_value, 0, sz);
2817 if (prog->watch_edict != ev_void)
2818 Con_Printf("%s progs: edict field watchpoint is at edict %d field index %d\n", prog->name, prog->watch_edict, prog->watch_field);
2821 prog->watch_field_type = ev_void;
2824 static void PRVM_Breakpoint_f(cmd_state_t *cmd)
2828 if( Cmd_Argc(cmd) == 2 ) {
2829 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2832 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2833 debug->break_statement[0] = 0;
2835 PRVM_UpdateBreakpoints(prog);
2838 if( Cmd_Argc(cmd) != 3 ) {
2839 Con_Printf( "prvm_breakpoint <program name> <function name | statement>\n" );
2843 if (!(prog = PRVM_ProgFromString(Cmd_Argv(cmd, 1))))
2847 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2848 strlcpy(debug->break_statement, Cmd_Argv(cmd, 2), sizeof(debug->break_statement));
2850 PRVM_UpdateBreakpoints(prog);
2853 static void PRVM_GlobalWatchpoint_f(cmd_state_t *cmd)
2857 if( Cmd_Argc(cmd) == 2 ) {
2858 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2861 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2862 debug->watch_global[0] = 0;
2864 PRVM_UpdateBreakpoints(prog);
2867 if( Cmd_Argc(cmd) != 3 ) {
2868 Con_Printf( "prvm_globalwatchpoint <program name> <global name>\n" );
2872 if (!(prog = PRVM_ProgFromString(Cmd_Argv(cmd, 1))))
2876 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2877 strlcpy(debug->watch_global, Cmd_Argv(cmd, 2), sizeof(debug->watch_global));
2879 PRVM_UpdateBreakpoints(prog);
2882 static void PRVM_EdictWatchpoint_f(cmd_state_t *cmd)
2886 if( Cmd_Argc(cmd) == 2 ) {
2887 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2890 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2891 debug->watch_field[0] = 0;
2893 PRVM_UpdateBreakpoints(prog);
2896 if( Cmd_Argc(cmd) != 4 ) {
2897 Con_Printf( "prvm_edictwatchpoint <program name> <edict number> <field name>\n" );
2901 if (!(prog = PRVM_ProgFromString(Cmd_Argv(cmd, 1))))
2905 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2906 debug->watch_edict = atoi(Cmd_Argv(cmd, 2));
2907 strlcpy(debug->watch_field, Cmd_Argv(cmd, 3), sizeof(debug->watch_field));
2909 PRVM_UpdateBreakpoints(prog);
2917 void PRVM_Init (void)
2919 Cmd_AddCommand(&cmd_client, "prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2920 Cmd_AddCommand(&cmd_client, "prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2921 Cmd_AddCommand(&cmd_client, "prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2922 Cmd_AddCommand(&cmd_client, "prvm_profile", PRVM_Profile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu)");
2923 Cmd_AddCommand(&cmd_client, "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");
2924 Cmd_AddCommand(&cmd_client, "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)");
2925 Cmd_AddCommand(&cmd_client, "prvm_fields", PRVM_Fields_f, "prints usage statistics on properties (how many entities have non-zero values) in the selected VM (server, client, menu)");
2926 Cmd_AddCommand(&cmd_client, "prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2927 Cmd_AddCommand(&cmd_client, "prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2928 Cmd_AddCommand(&cmd_client, "prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2929 Cmd_AddCommand(&cmd_client, "prvm_edictset", PRVM_ED_EdictSet_f, "changes value of a specified property of a specified entity in the selected VM (server, client, menu)");
2930 Cmd_AddCommand(&cmd_client, "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");
2931 Cmd_AddCommand(&cmd_client, "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");
2932 Cmd_AddCommand(&cmd_client, "prvm_printfunction", PRVM_PrintFunction_f, "prints a disassembly (QuakeC instructions) of the specified function in the selected VM (server, client, menu)");
2933 Cmd_AddCommand(&cmd_client, "cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2934 Cmd_AddCommand(&cmd_client, "menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2935 Cmd_AddCommand(&cmd_client, "sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2936 Cmd_AddCommand(&cmd_client, "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");
2937 Cmd_AddCommand(&cmd_client, "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");
2938 Cmd_AddCommand(&cmd_client, "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");
2940 Cmd_AddCommand(&cmd_server, "prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2941 Cmd_AddCommand(&cmd_server, "prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2942 Cmd_AddCommand(&cmd_server, "prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2943 Cmd_AddCommand(&cmd_server, "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_server, "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_server, "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_server, "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_server, "prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2948 Cmd_AddCommand(&cmd_server, "prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2949 Cmd_AddCommand(&cmd_server, "prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2950 Cmd_AddCommand(&cmd_server, "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_server, "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_server, "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_server, "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_server, "cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2955 Cmd_AddCommand(&cmd_server, "menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2956 Cmd_AddCommand(&cmd_server, "sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2957 Cmd_AddCommand(&cmd_server, "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_server, "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_server, "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);
2979 // COMMANDLINEOPTION: PRVM: -norunaway disables the runaway loop check (it might be impossible to exit DarkPlaces if used!)
2980 prvm_runawaycheck = !COM_CheckParm("-norunaway");
2990 void PRVM_Prog_Init(prvm_prog_t *prog, cmd_state_t *cmd)
2992 PRVM_Prog_Reset(prog);
2993 prog->leaktest_active = prvm_leaktest.integer != 0;
2994 prog->console_cmd = cmd;
2997 // LadyHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
2998 unsigned int PRVM_EDICT_NUM_ERROR(prvm_prog_t *prog, unsigned int n, const char *filename, int fileline)
3000 prog->error_cmd("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", prog->name, n, filename, fileline);
3004 #define PRVM_KNOWNSTRINGBASE 0x40000000
3006 const char *PRVM_GetString(prvm_prog_t *prog, int num)
3011 VM_Warning(prog, "PRVM_GetString: Invalid string offset (%i < 0)\n", num);
3014 else if (num < prog->stringssize)
3016 // constant string from progs.dat
3017 return prog->strings + num;
3019 else if (num <= prog->stringssize + prog->tempstringsbuf.maxsize)
3021 // tempstring returned by engine to QC (becomes invalid after returning to engine)
3022 num -= prog->stringssize;
3023 if (num < prog->tempstringsbuf.cursize)
3024 return (char *)prog->tempstringsbuf.data + num;
3027 VM_Warning(prog, "PRVM_GetString: Invalid temp-string offset (%i >= %i prog->tempstringsbuf.cursize)\n", num, prog->tempstringsbuf.cursize);
3031 else if (num & PRVM_KNOWNSTRINGBASE)
3034 num = num - PRVM_KNOWNSTRINGBASE;
3035 if (num >= 0 && num < prog->numknownstrings)
3037 if (!prog->knownstrings[num])
3039 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
3042 // refresh the garbage collection on the string - this guards
3043 // against a certain sort of repeated migration to earlier
3044 // points in the scan that could otherwise result in the string
3045 // being freed for being unused
3046 prog->knownstrings_flags[num] = (prog->knownstrings_flags[num] & ~KNOWNSTRINGFLAG_GCPRUNE) | KNOWNSTRINGFLAG_GCMARK;
3047 return prog->knownstrings[num];
3051 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
3057 // invalid string offset
3058 VM_Warning(prog, "PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
3063 const char *PRVM_ChangeEngineString(prvm_prog_t *prog, int i, const char *s)
3066 i = i - PRVM_KNOWNSTRINGBASE;
3067 if (i < 0 || i >= prog->numknownstrings)
3068 prog->error_cmd("PRVM_ChangeEngineString: string index %i is out of bounds", i);
3069 else if ((prog->knownstrings_flags[i] & KNOWNSTRINGFLAG_ENGINE) == 0)
3070 prog->error_cmd("PRVM_ChangeEngineString: string index %i is not an engine string", i);
3071 old = prog->knownstrings[i];
3072 prog->knownstrings[i] = s;
3076 static void PRVM_NewKnownString(prvm_prog_t *prog, int i, int flags, const char *s)
3078 if (i >= prog->numknownstrings)
3080 if (i >= prog->maxknownstrings)
3082 const char **oldstrings = prog->knownstrings;
3083 const unsigned char *oldstrings_flags = prog->knownstrings_flags;
3084 const char **oldstrings_origin = prog->knownstrings_origin;
3085 prog->maxknownstrings += 128;
3086 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3087 prog->knownstrings_flags = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
3088 if (prog->leaktest_active)
3089 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3090 if (prog->numknownstrings)
3092 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
3093 memcpy((char **)prog->knownstrings_flags, oldstrings_flags, prog->numknownstrings * sizeof(unsigned char));
3094 if (prog->leaktest_active)
3095 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
3098 prog->numknownstrings++;
3100 prog->firstfreeknownstring = i + 1;
3101 prog->knownstrings[i] = s;
3102 // it's in use right now, spare it until the next gc pass - that said, it is not freeable so this is probably moot
3103 prog->knownstrings_flags[i] = flags;
3104 if (prog->leaktest_active)
3105 prog->knownstrings_origin[i] = NULL;
3108 int PRVM_SetEngineString(prvm_prog_t *prog, const char *s)
3113 if (s >= prog->strings && s <= prog->strings + prog->stringssize)
3114 prog->error_cmd("PRVM_SetEngineString: s in prog->strings area");
3115 // if it's in the tempstrings area, use a reserved range
3116 // (otherwise we'd get millions of useless string offsets cluttering the database)
3117 if (s >= (char *)prog->tempstringsbuf.data && s < (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.maxsize)
3118 return prog->stringssize + (s - (char *)prog->tempstringsbuf.data);
3119 // see if it's a known string address
3120 for (i = 0;i < prog->numknownstrings;i++)
3121 if (prog->knownstrings[i] == s)
3122 return PRVM_KNOWNSTRINGBASE + i;
3123 // new unknown engine string
3124 if (developer_insane.integer)
3125 Con_DPrintf("new engine string %p = \"%s\"\n", s, s);
3126 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3127 if (!prog->knownstrings[i])
3129 PRVM_NewKnownString(prog, i, KNOWNSTRINGFLAG_GCMARK | KNOWNSTRINGFLAG_ENGINE, s);
3130 return PRVM_KNOWNSTRINGBASE + i;
3133 // temp string handling
3135 // all tempstrings go into this buffer consecutively, and it is reset
3136 // whenever PRVM_ExecuteProgram returns to the engine
3137 // (technically each PRVM_ExecuteProgram call saves the cursize value and
3138 // restores it on return, so multiple recursive calls can share the same
3140 // the buffer size is automatically grown as needed
3142 int PRVM_SetTempString(prvm_prog_t *prog, const char *s)
3148 size = (int)strlen(s) + 1;
3149 if (developer_insane.integer)
3150 Con_DPrintf("PRVM_SetTempString: cursize %i, size %i\n", prog->tempstringsbuf.cursize, size);
3151 if (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
3153 sizebuf_t old = prog->tempstringsbuf;
3154 if (prog->tempstringsbuf.cursize + size >= 1<<28)
3155 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);
3156 prog->tempstringsbuf.maxsize = max(prog->tempstringsbuf.maxsize, 65536);
3157 while (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
3158 prog->tempstringsbuf.maxsize *= 2;
3159 if (prog->tempstringsbuf.maxsize != old.maxsize || prog->tempstringsbuf.data == NULL)
3161 Con_DPrintf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, prog->tempstringsbuf.maxsize/1024);
3162 prog->tempstringsbuf.data = (unsigned char *) Mem_Alloc(prog->progs_mempool, prog->tempstringsbuf.maxsize);
3166 memcpy(prog->tempstringsbuf.data, old.data, old.cursize);
3171 t = (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.cursize;
3173 prog->tempstringsbuf.cursize += size;
3174 return PRVM_SetEngineString(prog, t);
3177 int PRVM_AllocString(prvm_prog_t *prog, size_t bufferlength, char **pointer)
3187 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3188 if (!prog->knownstrings[i])
3190 s = PRVM_Alloc(bufferlength);
3191 PRVM_NewKnownString(prog, i, KNOWNSTRINGFLAG_GCMARK, s);
3192 if(prog->leaktest_active)
3193 prog->knownstrings_origin[i] = PRVM_AllocationOrigin(prog);
3195 *pointer = (char *)(prog->knownstrings[i]);
3196 return PRVM_KNOWNSTRINGBASE + i;
3199 void PRVM_FreeString(prvm_prog_t *prog, int num)
3202 prog->error_cmd("PRVM_FreeString: attempt to free a NULL string");
3203 else if (num >= 0 && num < prog->stringssize)
3204 prog->error_cmd("PRVM_FreeString: attempt to free a constant string");
3205 else if (num >= PRVM_KNOWNSTRINGBASE && num < PRVM_KNOWNSTRINGBASE + prog->numknownstrings)
3207 num = num - PRVM_KNOWNSTRINGBASE;
3208 if (!prog->knownstrings[num])
3209 prog->error_cmd("PRVM_FreeString: attempt to free a non-existent or already freed string");
3210 if (!prog->knownstrings_flags[num])
3211 prog->error_cmd("PRVM_FreeString: attempt to free a string owned by the engine");
3212 PRVM_Free((char *)prog->knownstrings[num]);
3213 if(prog->leaktest_active)
3214 if(prog->knownstrings_origin[num])
3215 PRVM_Free((char *)prog->knownstrings_origin[num]);
3216 prog->knownstrings[num] = NULL;
3217 prog->knownstrings_flags[num] = 0;
3218 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
3221 prog->error_cmd("PRVM_FreeString: invalid string offset %i", num);
3224 static qboolean PRVM_IsStringReferenced(prvm_prog_t *prog, string_t string)
3228 for (i = 0;i < prog->numglobaldefs;i++)
3230 ddef_t *d = &prog->globaldefs[i];
3231 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3233 if(string == PRVM_GLOBALFIELDSTRING(d->ofs))
3237 for(j = 0; j < prog->num_edicts; ++j)
3239 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3240 if (ed->priv.required->free)
3242 for (i=0; i<prog->numfielddefs; ++i)
3244 ddef_t *d = &prog->fielddefs[i];
3245 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3247 if(string == PRVM_EDICTFIELDSTRING(ed, d->ofs))
3255 static qboolean PRVM_IsEdictRelevant(prvm_prog_t *prog, prvm_edict_t *edict)
3259 if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
3260 return true; // world or clients
3261 if (edict->priv.required->freetime <= prog->inittime)
3262 return true; // created during startup
3263 if (prog == SVVM_prog)
3265 if(PRVM_serveredictfloat(edict, solid)) // can block other stuff, or is a trigger?
3267 if(PRVM_serveredictfloat(edict, modelindex)) // visible ent?
3269 if(PRVM_serveredictfloat(edict, effects)) // particle effect?
3271 if(PRVM_serveredictfunction(edict, think)) // has a think function?
3272 if(PRVM_serveredictfloat(edict, nextthink) > 0) // that actually will eventually run?
3274 if(PRVM_serveredictfloat(edict, takedamage))
3276 if(*prvm_leaktest_ignore_classnames.string)
3278 if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_serveredictstring(edict, classname)))))
3282 else if (prog == CLVM_prog)
3284 // TODO someone add more stuff here
3285 if(PRVM_clientedictfloat(edict, entnum)) // csqc networked
3287 if(PRVM_clientedictfloat(edict, modelindex)) // visible ent?
3289 if(PRVM_clientedictfloat(edict, effects)) // particle effect?
3291 if(PRVM_clientedictfunction(edict, think)) // has a think function?
3292 if(PRVM_clientedictfloat(edict, nextthink) > 0) // that actually will eventually run?
3294 if(*prvm_leaktest_ignore_classnames.string)
3296 if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_clientedictstring(edict, classname)))))
3302 // menu prog does not have classnames
3307 static qboolean PRVM_IsEdictReferenced(prvm_prog_t *prog, prvm_edict_t *edict, int mark)
3310 int edictnum = PRVM_NUM_FOR_EDICT(edict);
3311 const char *targetname = NULL;
3313 if (prog == SVVM_prog && prvm_leaktest_follow_targetname.integer)
3314 targetname = PRVM_GetString(prog, PRVM_serveredictstring(edict, targetname));
3317 if(!*targetname) // ""
3320 for(j = 0; j < prog->num_edicts; ++j)
3322 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3323 if (ed->priv.required->mark < mark)
3329 const char *target = PRVM_GetString(prog, PRVM_serveredictstring(ed, target));
3331 if(!strcmp(target, targetname))
3334 for (i=0; i<prog->numfielddefs; ++i)
3336 ddef_t *d = &prog->fielddefs[i];
3337 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3339 if(edictnum == PRVM_EDICTFIELDEDICT(ed, d->ofs))
3347 static void PRVM_MarkReferencedEdicts(prvm_prog_t *prog)
3353 // Stage 1: world, all entities that are relevant, and all entities that are referenced by globals.
3355 for(j = 0; j < prog->num_edicts; ++j)
3357 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3358 if(ed->priv.required->free)
3360 ed->priv.required->mark = PRVM_IsEdictRelevant(prog, ed) ? stage : 0;
3362 for (i = 0;i < prog->numglobaldefs;i++)
3364 ddef_t *d = &prog->globaldefs[i];
3366 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3368 j = PRVM_GLOBALFIELDEDICT(d->ofs);
3369 if (i < 0 || j >= prog->max_edicts) {
3370 Con_Printf("Invalid entity reference from global %s.\n", PRVM_GetString(prog, d->s_name));
3373 ed = PRVM_EDICT_NUM(j);;
3374 ed->priv.required->mark = stage;
3377 // Future stages: all entities that are referenced by an entity of the previous stage.
3381 for(j = 0; j < prog->num_edicts; ++j)
3383 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3384 if(ed->priv.required->free)
3386 if(ed->priv.required->mark)
3388 if(PRVM_IsEdictReferenced(prog, ed, stage))
3390 ed->priv.required->mark = stage + 1;
3397 Con_DPrintf("leak check used %d stages to find all references\n", stage);
3400 void PRVM_LeakTest(prvm_prog_t *prog)
3403 qboolean leaked = false;
3405 if(!prog->leaktest_active)
3409 for (i = 0; i < prog->numknownstrings; ++i)
3411 if(prog->knownstrings[i])
3412 if(prog->knownstrings_flags[i])
3413 if(prog->knownstrings_origin[i])
3414 if(!PRVM_IsStringReferenced(prog, PRVM_KNOWNSTRINGBASE + i))
3416 Con_Printf("Unreferenced string found!\n Value: %s\n Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
3422 PRVM_MarkReferencedEdicts(prog);
3423 for(j = 0; j < prog->num_edicts; ++j)
3425 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3426 if(ed->priv.required->free)
3428 if(!ed->priv.required->mark)
3429 if(ed->priv.required->allocation_origin)
3431 Con_Printf("Unreferenced edict found!\n Allocated at: %s\n", ed->priv.required->allocation_origin);
3432 PRVM_ED_Print(prog, ed, NULL);
3437 ed->priv.required->mark = 0; // clear marks again when done
3440 for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
3442 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
3444 if(stringbuffer->origin)
3446 Con_Printf("Open string buffer handle found!\n Allocated at: %s\n", stringbuffer->origin);
3451 for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
3453 if(prog->openfiles[i])
3454 if(prog->openfiles_origin[i])
3456 Con_Printf("Open file handle found!\n Allocated at: %s\n", prog->openfiles_origin[i]);
3461 for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
3463 if(prog->opensearches[i])
3464 if(prog->opensearches_origin[i])
3466 Con_Printf("Open search handle found!\n Allocated at: %s\n", prog->opensearches_origin[i]);
3472 Con_Printf("Congratulations. No leaks found.\n");
3475 void PRVM_GarbageCollection(prvm_prog_t *prog)
3477 int limit = prvm_garbagecollection_scan_limit.integer;
3478 prvm_prog_garbagecollection_state_t *gc = &prog->gc;
3479 if (!prvm_garbagecollection_enable.integer)
3482 // we like to limit how much scanning we do so it doesn't put a significant
3483 // burden on the cpu, so each of these are not complete scans, we also like
3484 // to have consistent cpu usage so we do a bit of work on each category of
3485 // leaked object every frame
3491 case PRVM_GC_GLOBALS_MARK:
3492 for (; gc->globals_mark_progress < prog->numglobaldefs && (limit--) > 0; gc->globals_mark_progress++)
3494 ddef_t *d = &prog->globaldefs[gc->globals_mark_progress];
3499 prvm_int_t s = prog->globals.ip[d->ofs];
3500 if (s & PRVM_KNOWNSTRINGBASE)
3502 prvm_int_t num = s - PRVM_KNOWNSTRINGBASE;
3503 if (!prog->knownstrings[num])
3506 Con_DPrintf("PRVM_GarbageCollection: Found bogus strzone reference in global %i (global name: \"%s\"), erasing reference", d->ofs, PRVM_GetString(prog, d->s_name));
3507 prog->globals.ip[d->ofs] = 0;
3510 prog->knownstrings_flags[num] = (prog->knownstrings_flags[num] | KNOWNSTRINGFLAG_GCMARK) & ~KNOWNSTRINGFLAG_GCPRUNE;
3518 if (gc->globals_mark_progress >= prog->numglobaldefs)
3521 case PRVM_GC_FIELDS_MARK:
3522 for (; gc->fields_mark_progress < prog->numfielddefs && limit > 0;)
3524 ddef_t *d = &prog->fielddefs[gc->fields_mark_progress];
3528 //for (gc-> entityindex = 0; entityindex < prog->num_edicts; entityindex++)
3529 for (;gc->fields_mark_progress_entity < prog->num_edicts && (limit--) > 0;gc->fields_mark_progress_entity++)
3531 int entityindex = gc->fields_mark_progress_entity;
3532 prvm_int_t s = prog->edictsfields.ip[entityindex * prog->entityfields + d->ofs];
3533 if (s & PRVM_KNOWNSTRINGBASE)
3535 prvm_int_t num = s - PRVM_KNOWNSTRINGBASE;
3536 if (!prog->knownstrings[num])
3539 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));
3540 prog->edictsfields.ip[entityindex * prog->entityfields + d->ofs] = 0;
3543 prog->knownstrings_flags[num] = (prog->knownstrings_flags[num] | KNOWNSTRINGFLAG_GCMARK) & ~KNOWNSTRINGFLAG_GCPRUNE;
3546 if (gc->fields_mark_progress_entity >= prog->num_edicts)
3548 gc->fields_mark_progress_entity = 0;
3549 gc->fields_mark_progress++;
3553 gc->fields_mark_progress_entity = 0;
3554 gc->fields_mark_progress++;
3558 if (gc->fields_mark_progress >= prog->numfielddefs)
3561 case PRVM_GC_KNOWNSTRINGS_SWEEP:
3562 // free any strzone'd strings that are not marked
3563 if (!prvm_garbagecollection_strings.integer)
3568 for (;gc->knownstrings_sweep_progress < prog->numknownstrings && (limit--) > 0;gc->knownstrings_sweep_progress++)
3570 int num = gc->knownstrings_sweep_progress;
3571 if (prog->knownstrings[num] && (prog->knownstrings_flags[num] & (KNOWNSTRINGFLAG_GCMARK | KNOWNSTRINGFLAG_ENGINE)) == 0)
3573 if (prog->knownstrings_flags[num] & KNOWNSTRINGFLAG_GCPRUNE)
3575 // string has been marked for pruning two passes in a row
3576 if (prvm_garbagecollection_notify.integer)
3577 Con_DPrintf("prvm_garbagecollection_notify: %s: freeing unreferenced string %i: \"%s\"\n", prog->name, num, prog->knownstrings[num]);
3578 Mem_Free((char *)prog->knownstrings[num]);
3579 prog->knownstrings[num] = NULL;
3580 prog->knownstrings_flags[num] = 0;
3581 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
3585 // mark it for pruning next pass
3586 prog->knownstrings_flags[num] |= KNOWNSTRINGFLAG_GCPRUNE;
3590 if (gc->knownstrings_sweep_progress >= prog->numknownstrings)
3595 memset(gc, 0, sizeof(*gc));