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"};
48 static double prvm_reuseedicts_always_allow = 0;
49 qboolean prvm_runawaycheck = true;
51 //============================================================================
59 static void PRVM_MEM_Alloc(prvm_prog_t *prog)
63 // reserve space for the null entity aka world
64 // check bound of max_edicts
65 prog->max_edicts = bound(1 + prog->reserved_edicts, prog->max_edicts, prog->limit_edicts);
66 prog->num_edicts = bound(1 + prog->reserved_edicts, prog->num_edicts, prog->max_edicts);
68 // edictprivate_size has to be min as big prvm_edict_private_t
69 prog->edictprivate_size = max(prog->edictprivate_size,(int)sizeof(prvm_edict_private_t));
72 prog->edicts = (prvm_edict_t *)Mem_Alloc(prog->progs_mempool,prog->limit_edicts * sizeof(prvm_edict_t));
74 // alloc edict private space
75 prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size);
78 prog->entityfieldsarea = prog->entityfields * prog->max_edicts;
79 prog->edictsfields = (prvm_vec_t *)Mem_Alloc(prog->progs_mempool, prog->entityfieldsarea * sizeof(prvm_vec_t));
82 for(i = 0; i < prog->max_edicts; i++)
84 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
85 prog->edicts[i].fields.fp = prog->edictsfields + i * prog->entityfields;
91 PRVM_MEM_IncreaseEdicts
94 void PRVM_MEM_IncreaseEdicts(prvm_prog_t *prog)
98 if(prog->max_edicts >= prog->limit_edicts)
101 prog->begin_increase_edicts(prog);
104 prog->max_edicts = min(prog->max_edicts + 256, prog->limit_edicts);
106 prog->entityfieldsarea = prog->entityfields * prog->max_edicts;
107 prog->edictsfields = (prvm_vec_t*)Mem_Realloc(prog->progs_mempool, (void *)prog->edictsfields, prog->entityfieldsarea * sizeof(prvm_vec_t));
108 prog->edictprivate = (void *)Mem_Realloc(prog->progs_mempool, (void *)prog->edictprivate, prog->max_edicts * prog->edictprivate_size);
110 //set e and v pointers
111 for(i = 0; i < prog->max_edicts; i++)
113 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
114 prog->edicts[i].fields.fp = prog->edictsfields + i * prog->entityfields;
117 prog->end_increase_edicts(prog);
120 //============================================================================
123 int PRVM_ED_FindFieldOffset(prvm_prog_t *prog, const char *field)
126 d = PRVM_ED_FindField(prog, field);
132 int PRVM_ED_FindGlobalOffset(prvm_prog_t *prog, const char *global)
135 d = PRVM_ED_FindGlobal(prog, global);
141 func_t PRVM_ED_FindFunctionOffset(prvm_prog_t *prog, const char *function)
144 f = PRVM_ED_FindFunction(prog, function);
147 return (func_t)(f - prog->functions);
155 prvm_prog_t *PRVM_ProgFromString(const char *str)
157 if (!strcmp(str, "server"))
159 if (!strcmp(str, "client"))
162 if (!strcmp(str, "menu"))
170 PRVM_FriendlyProgFromString
173 prvm_prog_t *PRVM_FriendlyProgFromString(const char *str)
175 prvm_prog_t *prog = PRVM_ProgFromString(str);
178 Con_Printf("%s: unknown program name\n", str);
183 Con_Printf("%s: program is not loaded\n", str);
193 Sets everything to NULL.
195 Nota bene: this also marks the entity as allocated if it has been previously
196 freed and sets the allocation origin.
199 void PRVM_ED_ClearEdict(prvm_prog_t *prog, prvm_edict_t *e)
201 memset(e->fields.fp, 0, prog->entityfields * sizeof(prvm_vec_t));
202 e->priv.required->free = false;
203 e->priv.required->freetime = realtime;
204 if(e->priv.required->allocation_origin)
205 Mem_Free((char *)e->priv.required->allocation_origin);
206 e->priv.required->allocation_origin = PRVM_AllocationOrigin(prog);
208 // AK: Let the init_edict function determine if something needs to be initialized
209 prog->init_edict(prog, e);
212 const char *PRVM_AllocationOrigin(prvm_prog_t *prog)
215 if(prog->leaktest_active)
216 if(prog->depth > 0) // actually in QC code and not just parsing the entities block of a map/savegame
218 buf = (char *)PRVM_Alloc(256);
219 PRVM_ShortStackTrace(prog, buf, 256);
228 Returns if this particular edict could get allocated by PRVM_ED_Alloc
231 qboolean PRVM_ED_CanAlloc(prvm_prog_t *prog, prvm_edict_t *e)
233 if(!e->priv.required->free)
235 if(prvm_reuseedicts_always_allow == realtime)
237 if(realtime <= e->priv.required->freetime + 0.1 && prvm_reuseedicts_neverinsameframe.integer)
238 return false; // never allow reuse in same frame (causes networking trouble)
239 if(e->priv.required->freetime < prog->starttime + prvm_reuseedicts_startuptime.value)
241 if(realtime > e->priv.required->freetime + 1)
243 return false; // entity slot still blocked because the entity was freed less than one second ago
250 Either finds a free edict, or allocates a new one.
251 Try to avoid reusing an entity that was recently freed, because it
252 can cause the client to think the entity morphed into something else
253 instead of being removed and recreated, which can cause interpolated
254 angles and bad trails.
257 prvm_edict_t *PRVM_ED_Alloc(prvm_prog_t *prog)
262 // the client qc dont need maxclients
263 // thus it doesnt need to use svs.maxclients
264 // AK: changed i=svs.maxclients+1
265 // AK: changed so the edict 0 wont spawn -> used as reserved/world entity
266 // although the menu/client has no world
267 for (i = prog->reserved_edicts + 1;i < prog->num_edicts;i++)
269 e = PRVM_EDICT_NUM(i);
270 if(PRVM_ED_CanAlloc(prog, e))
272 PRVM_ED_ClearEdict (prog, e);
277 if (i == prog->limit_edicts)
278 prog->error_cmd("%s: PRVM_ED_Alloc: no free edicts", prog->name);
281 if (prog->num_edicts >= prog->max_edicts)
282 PRVM_MEM_IncreaseEdicts(prog);
284 e = PRVM_EDICT_NUM(i);
286 PRVM_ED_ClearEdict(prog, e);
294 Marks the edict as free
295 FIXME: walk all entities and NULL out references to this entity
298 void PRVM_ED_Free(prvm_prog_t *prog, prvm_edict_t *ed)
300 // dont delete the null entity (world) or reserved edicts
301 if (ed - prog->edicts <= prog->reserved_edicts)
304 prog->free_edict(prog, ed);
306 ed->priv.required->free = true;
307 ed->priv.required->freetime = realtime;
308 if(ed->priv.required->allocation_origin)
310 Mem_Free((char *)ed->priv.required->allocation_origin);
311 ed->priv.required->allocation_origin = NULL;
315 //===========================================================================
322 static ddef_t *PRVM_ED_GlobalAtOfs (prvm_prog_t *prog, int ofs)
327 for (i = 0;i < prog->numglobaldefs;i++)
329 def = &prog->globaldefs[i];
341 ddef_t *PRVM_ED_FieldAtOfs (prvm_prog_t *prog, int ofs)
346 for (i = 0;i < prog->numfielddefs;i++)
348 def = &prog->fielddefs[i];
360 ddef_t *PRVM_ED_FindField (prvm_prog_t *prog, const char *name)
365 for (i = 0;i < prog->numfielddefs;i++)
367 def = &prog->fielddefs[i];
368 if (!strcmp(PRVM_GetString(prog, def->s_name), name))
379 ddef_t *PRVM_ED_FindGlobal (prvm_prog_t *prog, const char *name)
384 for (i = 0;i < prog->numglobaldefs;i++)
386 def = &prog->globaldefs[i];
387 if (!strcmp(PRVM_GetString(prog, def->s_name), name))
399 mfunction_t *PRVM_ED_FindFunction (prvm_prog_t *prog, const char *name)
404 for (i = 0;i < prog->numfunctions;i++)
406 func = &prog->functions[i];
407 if (!strcmp(PRVM_GetString(prog, func->s_name), name))
418 Returns a string describing *data in a type specific manner
421 static char *PRVM_ValueString (prvm_prog_t *prog, etype_t type, prvm_eval_t *val, char *line, size_t linelength)
427 type = (etype_t)((int) type & ~DEF_SAVEGLOBAL);
432 strlcpy (line, PRVM_GetString (prog, val->string), linelength);
436 if (n < 0 || n >= prog->max_edicts)
437 dpsnprintf (line, linelength, "entity %i (invalid!)", n);
439 dpsnprintf (line, linelength, "entity %i", n);
442 if ((unsigned int)val->function < (unsigned int)prog->progs_numfunctions)
444 f = prog->functions + val->function;
445 dpsnprintf (line, linelength, "%s()", PRVM_GetString(prog, f->s_name));
448 dpsnprintf (line, linelength, "function%i() (invalid!)", val->function);
451 def = PRVM_ED_FieldAtOfs ( prog, val->_int );
453 dpsnprintf (line, linelength, ".%s", PRVM_GetString(prog, def->s_name));
455 dpsnprintf (line, linelength, "field%i (invalid!)", val->_int );
458 dpsnprintf (line, linelength, "void");
461 // LadyHavoc: changed from %5.1f to %10.4f
462 dpsnprintf (line, linelength, FLOAT_LOSSLESS_FORMAT, val->_float);
465 // LadyHavoc: changed from %5.1f to %10.4f
466 dpsnprintf (line, linelength, "'" VECTOR_LOSSLESS_FORMAT "'", val->vector[0], val->vector[1], val->vector[2]);
469 dpsnprintf (line, linelength, "pointer");
472 dpsnprintf (line, linelength, "bad type %i", (int) type);
483 Returns a string describing *data in a type specific manner
484 Easier to parse than PR_ValueString
487 char *PRVM_UglyValueString (prvm_prog_t *prog, etype_t type, prvm_eval_t *val, char *line, size_t linelength)
494 type = (etype_t)((int)type & ~DEF_SAVEGLOBAL);
499 // Parse the string a bit to turn special characters
500 // (like newline, specifically) into escape codes,
501 // this fixes saving games from various mods
502 s = PRVM_GetString (prog, val->string);
503 for (i = 0;i < (int)linelength - 2 && *s;)
533 dpsnprintf (line, linelength, "%i", i);
536 if ((unsigned int)val->function < (unsigned int)prog->progs_numfunctions)
538 f = prog->functions + val->function;
539 strlcpy (line, PRVM_GetString (prog, f->s_name), linelength);
542 dpsnprintf (line, linelength, "bad function %i (invalid!)", val->function);
545 def = PRVM_ED_FieldAtOfs ( prog, val->_int );
547 dpsnprintf (line, linelength, ".%s", PRVM_GetString(prog, def->s_name));
549 dpsnprintf (line, linelength, "field%i (invalid!)", val->_int );
552 dpsnprintf (line, linelength, "void");
555 dpsnprintf (line, linelength, FLOAT_LOSSLESS_FORMAT, val->_float);
558 dpsnprintf (line, linelength, VECTOR_LOSSLESS_FORMAT, val->vector[0], val->vector[1], val->vector[2]);
561 dpsnprintf (line, linelength, "bad type %i", type);
572 Returns a string with a description and the contents of a global,
573 padded to 20 field width
576 char *PRVM_GlobalString (prvm_prog_t *prog, int ofs, char *line, size_t linelength)
582 char valuebuf[MAX_INPUTLINE];
584 val = (prvm_eval_t *)&prog->globals.fp[ofs];
585 def = PRVM_ED_GlobalAtOfs(prog, ofs);
587 dpsnprintf (line, linelength, "GLOBAL%i", ofs);
590 s = PRVM_ValueString (prog, (etype_t)def->type, val, valuebuf, sizeof(valuebuf));
591 dpsnprintf (line, linelength, "%s (=%s)", PRVM_GetString(prog, def->s_name), s);
595 //for ( ; i<20 ; i++)
596 // strcat (line," ");
602 char *PRVM_GlobalStringNoContents (prvm_prog_t *prog, int ofs, char *line, size_t linelength)
607 def = PRVM_ED_GlobalAtOfs(prog, ofs);
609 dpsnprintf (line, linelength, "GLOBAL%i", ofs);
611 dpsnprintf (line, linelength, "%s", PRVM_GetString(prog, def->s_name));
614 //for ( ; i<20 ; i++)
615 // strcat (line," ");
629 // LadyHavoc: optimized this to print out much more quickly (tempstring)
630 // LadyHavoc: changed to print out every 4096 characters (incase there are a lot of fields to print)
631 void PRVM_ED_Print(prvm_prog_t *prog, prvm_edict_t *ed, const char *wildcard_fieldname)
639 char tempstring[MAX_INPUTLINE], tempstring2[260]; // temporary string buffers
640 char valuebuf[MAX_INPUTLINE];
642 if (ed->priv.required->free)
644 Con_Printf("%s: FREE\n",prog->name);
649 dpsnprintf(tempstring, sizeof(tempstring), "\n%s EDICT %i:\n", prog->name, PRVM_NUM_FOR_EDICT(ed));
650 for (i = 1;i < prog->numfielddefs;i++)
652 d = &prog->fielddefs[i];
653 name = PRVM_GetString(prog, d->s_name);
654 if(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
655 continue; // skip _x, _y, _z vars
657 // Check Field Name Wildcard
658 if(wildcard_fieldname)
659 if( !matchpattern(name, wildcard_fieldname, 1) )
660 // Didn't match; skip
663 val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
665 // if the value is still all 0, skip the field
666 type = d->type & ~DEF_SAVEGLOBAL;
668 for (j=0 ; j<prvm_type_size[type] ; j++)
671 if (j == prvm_type_size[type])
674 if (strlen(name) > sizeof(tempstring2)-4)
676 memcpy (tempstring2, name, sizeof(tempstring2)-4);
677 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
678 tempstring2[sizeof(tempstring2)-1] = 0;
681 strlcat(tempstring, name, sizeof(tempstring));
682 for (l = strlen(name);l < 14;l++)
683 strlcat(tempstring, " ", sizeof(tempstring));
684 strlcat(tempstring, " ", sizeof(tempstring));
686 name = PRVM_ValueString(prog, (etype_t)d->type, val, valuebuf, sizeof(valuebuf));
687 if (strlen(name) > sizeof(tempstring2)-4)
689 memcpy (tempstring2, name, sizeof(tempstring2)-4);
690 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
691 tempstring2[sizeof(tempstring2)-1] = 0;
694 strlcat(tempstring, name, sizeof(tempstring));
695 strlcat(tempstring, "\n", sizeof(tempstring));
696 if (strlen(tempstring) >= sizeof(tempstring)/2)
698 Con_Print(tempstring);
703 Con_Print(tempstring);
713 extern cvar_t developer_entityparsing;
714 void PRVM_ED_Write (prvm_prog_t *prog, qfile_t *f, prvm_edict_t *ed)
722 char valuebuf[MAX_INPUTLINE];
726 if (ed->priv.required->free)
732 for (i = 1;i < prog->numfielddefs;i++)
734 d = &prog->fielddefs[i];
735 name = PRVM_GetString(prog, d->s_name);
737 if(developer_entityparsing.integer)
738 Con_Printf("PRVM_ED_Write: at entity %d field %s\n", PRVM_NUM_FOR_EDICT(ed), name);
740 //if(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
741 if(strlen(name) > 1 && name[strlen(name)-2] == '_')
742 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?)
744 val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
746 // if the value is still all 0, skip the field
747 type = d->type & ~DEF_SAVEGLOBAL;
748 for (j=0 ; j<prvm_type_size[type] ; j++)
751 if (j == prvm_type_size[type])
754 FS_Printf(f,"\"%s\" ",name);
755 prog->statestring = va(vabuf, sizeof(vabuf), "PRVM_ED_Write, ent=%d, name=%s", i, name);
756 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString(prog, (etype_t)d->type, val, valuebuf, sizeof(valuebuf)));
757 prog->statestring = NULL;
763 void PRVM_ED_PrintNum (prvm_prog_t *prog, int ent, const char *wildcard_fieldname)
765 PRVM_ED_Print(prog, PRVM_EDICT_NUM(ent), wildcard_fieldname);
770 PRVM_ED_PrintEdicts_f
772 For debugging, prints all the entities in the current server
775 void PRVM_ED_PrintEdicts_f(cmd_state_t *cmd)
779 const char *wildcard_fieldname;
781 if(Cmd_Argc(cmd) < 2 || Cmd_Argc(cmd) > 3)
783 Con_Print("prvm_edicts <program name> <optional field name wildcard>\n");
787 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
790 if( Cmd_Argc(cmd) == 3)
791 wildcard_fieldname = Cmd_Argv(cmd, 2);
793 wildcard_fieldname = NULL;
795 Con_Printf("%s: %i entities\n", prog->name, prog->num_edicts);
796 for (i=0 ; i<prog->num_edicts ; i++)
797 PRVM_ED_PrintNum (prog, i, wildcard_fieldname);
804 For debugging, prints a single edict
807 static void PRVM_ED_PrintEdict_f(cmd_state_t *cmd)
811 const char *wildcard_fieldname;
813 if(Cmd_Argc(cmd) < 3 || Cmd_Argc(cmd) > 4)
815 Con_Print("prvm_edict <program name> <edict number> <optional field name wildcard>\n");
819 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
822 i = atoi (Cmd_Argv(cmd, 2));
823 if (i >= prog->num_edicts)
825 Con_Print("Bad edict number\n");
828 if( Cmd_Argc(cmd) == 4)
829 // Optional Wildcard Provided
830 wildcard_fieldname = Cmd_Argv(cmd, 3);
833 wildcard_fieldname = NULL;
834 PRVM_ED_PrintNum (prog, i, wildcard_fieldname);
844 // 2 possibilities : 1. just displaying the active edict count
845 // 2. making a function pointer [x]
846 static void PRVM_ED_Count_f(cmd_state_t *cmd)
850 if(Cmd_Argc(cmd) != 2)
852 Con_Print("prvm_count <program name>\n");
856 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
859 prog->count_edicts(prog);
863 ==============================================================================
867 FIXME: need to tag constants, doesn't really work
868 ==============================================================================
876 void PRVM_ED_WriteGlobals (prvm_prog_t *prog, qfile_t *f)
883 char valuebuf[MAX_INPUTLINE];
886 for (i = 0;i < prog->numglobaldefs;i++)
888 def = &prog->globaldefs[i];
890 if ( !(def->type & DEF_SAVEGLOBAL) )
892 type &= ~DEF_SAVEGLOBAL;
894 if (type != ev_string && type != ev_float && type != ev_entity)
897 name = PRVM_GetString(prog, def->s_name);
899 if(developer_entityparsing.integer)
900 Con_Printf("PRVM_ED_WriteGlobals: at global %s\n", name);
902 prog->statestring = va(vabuf, sizeof(vabuf), "PRVM_ED_WriteGlobals, name=%s", name);
903 FS_Printf(f,"\"%s\" ", name);
904 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString(prog, (etype_t)type, (prvm_eval_t *)&prog->globals.fp[def->ofs], valuebuf, sizeof(valuebuf)));
905 prog->statestring = NULL;
915 void PRVM_ED_ParseGlobals (prvm_prog_t *prog, const char *data)
917 char keyname[MAX_INPUTLINE];
923 if (!COM_ParseToken_Simple(&data, false, false, true))
924 prog->error_cmd("PRVM_ED_ParseGlobals: EOF without closing brace");
925 if (com_token[0] == '}')
928 if (developer_entityparsing.integer)
929 Con_Printf("Key: \"%s\"", com_token);
931 strlcpy (keyname, com_token, sizeof(keyname));
934 if (!COM_ParseToken_Simple(&data, false, true, true))
935 prog->error_cmd("PRVM_ED_ParseGlobals: EOF without closing brace");
937 if (developer_entityparsing.integer)
938 Con_Printf(" \"%s\"\n", com_token);
940 if (com_token[0] == '}')
941 prog->error_cmd("PRVM_ED_ParseGlobals: closing brace without data");
943 key = PRVM_ED_FindGlobal (prog, keyname);
946 Con_DPrintf("'%s' is not a global on %s\n", keyname, prog->name);
950 if (!PRVM_ED_ParseEpair(prog, NULL, key, com_token, true))
951 prog->error_cmd("PRVM_ED_ParseGlobals: parse error");
955 //============================================================================
962 Can parse either fields or globals
963 returns false if error
966 qboolean PRVM_ED_ParseEpair(prvm_prog_t *prog, prvm_edict_t *ent, ddef_t *key, const char *s, qboolean parsebackslash)
975 val = (prvm_eval_t *)(ent->fields.fp + key->ofs);
977 val = (prvm_eval_t *)(prog->globals.fp + key->ofs);
978 switch (key->type & ~DEF_SAVEGLOBAL)
981 l = (int)strlen(s) + 1;
982 val->string = PRVM_AllocString(prog, l, &new_p);
983 for (i = 0;i < l;i++)
985 if (s[i] == '\\' && s[i+1] && parsebackslash)
990 else if (s[i] == 'r')
1001 while (*s && ISWHITESPACE(*s))
1003 val->_float = atof(s);
1007 for (i = 0;i < 3;i++)
1009 while (*s && ISWHITESPACE(*s))
1013 val->vector[i] = atof(s);
1014 while (!ISWHITESPACE(*s))
1022 while (*s && ISWHITESPACE(*s))
1025 if (i >= prog->limit_edicts)
1026 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);
1027 while (i >= prog->max_edicts)
1028 PRVM_MEM_IncreaseEdicts(prog);
1029 // if IncreaseEdicts was called the base pointer needs to be updated
1031 val = (prvm_eval_t *)(ent->fields.fp + key->ofs);
1032 val->edict = PRVM_EDICT_TO_PROG(PRVM_EDICT_NUM((int)i));
1038 Con_DPrintf("PRVM_ED_ParseEpair: Bogus field name %s in %s\n", s, prog->name);
1041 def = PRVM_ED_FindField(prog, s + 1);
1044 Con_DPrintf("PRVM_ED_ParseEpair: Can't find field %s in %s\n", s, prog->name);
1047 val->_int = def->ofs;
1051 func = PRVM_ED_FindFunction(prog, s);
1054 Con_Printf("PRVM_ED_ParseEpair: Can't find function %s in %s\n", s, prog->name);
1057 val->function = func - prog->functions;
1061 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);
1071 Console command to send a string to QC function GameCommand of the
1075 sv_cmd adminmsg 3 "do not teamkill"
1076 cl_cmd someclientcommand
1077 menu_cmd somemenucommand
1079 All progs can support this extension; sg calls it in server QC, cg in client
1083 static void PRVM_GameCommand(cmd_state_t *cmd, const char *whichprogs, const char *whichcmd)
1086 if(Cmd_Argc(cmd) < 1)
1088 Con_Printf("%s text...\n", whichcmd);
1092 if (!(prog = PRVM_FriendlyProgFromString(whichprogs)))
1095 if(!PRVM_allfunction(GameCommand))
1097 Con_Printf("%s program do not support GameCommand!\n", whichprogs);
1101 int restorevm_tempstringsbuf_cursize;
1106 restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
1107 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(prog, s ? s : "");
1108 prog->ExecuteProgram(prog, PRVM_allfunction(GameCommand), "QC function GameCommand is missing");
1109 prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1112 static void PRVM_GameCommand_Server_f(cmd_state_t *cmd)
1114 PRVM_GameCommand(cmd, "server", "sv_cmd");
1116 static void PRVM_GameCommand_Client_f(cmd_state_t *cmd)
1118 PRVM_GameCommand(cmd, "client", "cl_cmd");
1120 static void PRVM_GameCommand_Menu_f(cmd_state_t *cmd)
1122 PRVM_GameCommand(cmd, "menu", "menu_cmd");
1129 Console command to load a field of a specified edict
1132 static void PRVM_ED_EdictGet_f(cmd_state_t *cmd)
1139 char valuebuf[MAX_INPUTLINE];
1141 if(Cmd_Argc(cmd) != 4 && Cmd_Argc(cmd) != 5)
1143 Con_Print("prvm_edictget <program name> <edict number> <field> [<cvar>]\n");
1147 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
1150 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(cmd, 2)));
1152 if((key = PRVM_ED_FindField(prog, Cmd_Argv(cmd, 3))) == 0)
1154 Con_Printf("Key %s not found !\n", Cmd_Argv(cmd, 3));
1158 v = (prvm_eval_t *)(ed->fields.fp + key->ofs);
1159 s = PRVM_UglyValueString(prog, (etype_t)key->type, v, valuebuf, sizeof(valuebuf));
1160 if(Cmd_Argc(cmd) == 5)
1162 cvar_t *cvar = Cvar_FindVar(cmd->cvars, Cmd_Argv(cmd, 4), cmd->cvars_flagsmask);
1163 if (cvar && cvar->flags & CVAR_READONLY)
1165 Con_Printf("prvm_edictget: %s is read-only\n", cvar->name);
1168 Cvar_Get(cmd->cvars, Cmd_Argv(cmd, 4), s, cmd->cvars_flagsmask, NULL);
1171 Con_Printf("%s\n", s);
1177 static void PRVM_ED_GlobalGet_f(cmd_state_t *cmd)
1183 char valuebuf[MAX_INPUTLINE];
1185 if(Cmd_Argc(cmd) != 3 && Cmd_Argc(cmd) != 4)
1187 Con_Print("prvm_globalget <program name> <global> [<cvar>]\n");
1191 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
1194 key = PRVM_ED_FindGlobal(prog, Cmd_Argv(cmd, 2));
1197 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(cmd, 2), Cmd_Argv(cmd, 1) );
1201 v = (prvm_eval_t *) &prog->globals.fp[key->ofs];
1202 s = PRVM_UglyValueString(prog, (etype_t)key->type, v, valuebuf, sizeof(valuebuf));
1203 if(Cmd_Argc(cmd) == 4)
1205 cvar_t *cvar = Cvar_FindVar(cmd->cvars, Cmd_Argv(cmd, 3), cmd->cvars_flagsmask);
1206 if (cvar && cvar->flags & CVAR_READONLY)
1208 Con_Printf("prvm_globalget: %s is read-only\n", cvar->name);
1211 Cvar_Get(cmd->cvars, Cmd_Argv(cmd, 3), s, cmd->cvars_flagsmask, NULL);
1214 Con_Printf("%s\n", s);
1224 Console command to set a field of a specified edict
1227 static void PRVM_ED_EdictSet_f(cmd_state_t *cmd)
1233 if(Cmd_Argc(cmd) != 5)
1235 Con_Print("prvm_edictset <program name> <edict number> <field> <value>\n");
1239 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
1242 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(cmd, 2)));
1244 if((key = PRVM_ED_FindField(prog, Cmd_Argv(cmd, 3))) == 0)
1245 Con_Printf("Key %s not found !\n", Cmd_Argv(cmd, 3));
1247 PRVM_ED_ParseEpair(prog, ed, key, Cmd_Argv(cmd, 4), true);
1251 ====================
1254 Parses an edict out of the given string, returning the new position
1255 ed should be a properly initialized empty edict.
1256 Used for initial level load and for savegames.
1257 ====================
1259 const char *PRVM_ED_ParseEdict (prvm_prog_t *prog, const char *data, prvm_edict_t *ent)
1269 // go through all the dictionary pairs
1273 if (!COM_ParseToken_Simple(&data, false, false, true))
1274 prog->error_cmd("PRVM_ED_ParseEdict: EOF without closing brace");
1275 if (developer_entityparsing.integer)
1276 Con_Printf("Key: \"%s\"", com_token);
1277 if (com_token[0] == '}')
1280 // anglehack is to allow QuakeEd to write single scalar angles
1281 // and allow them to be turned into vectors. (FIXME...)
1282 if (!strcmp(com_token, "angle"))
1284 strlcpy (com_token, "angles", sizeof(com_token));
1290 // FIXME: change light to _light to get rid of this hack
1291 if (!strcmp(com_token, "light"))
1292 strlcpy (com_token, "light_lev", sizeof(com_token)); // hack for single light def
1294 strlcpy (keyname, com_token, sizeof(keyname));
1296 // another hack to fix keynames with trailing spaces
1297 n = strlen(keyname);
1298 while (n && keyname[n-1] == ' ')
1305 if (!COM_ParseToken_Simple(&data, false, false, true))
1306 prog->error_cmd("PRVM_ED_ParseEdict: EOF without closing brace");
1307 if (developer_entityparsing.integer)
1308 Con_Printf(" \"%s\"\n", com_token);
1310 if (com_token[0] == '}')
1311 prog->error_cmd("PRVM_ED_ParseEdict: closing brace without data");
1315 // ignore attempts to set key "" (this problem occurs in nehahra neh1m8.bsp)
1319 // keynames with a leading underscore are used for utility comments,
1320 // and are immediately discarded by quake
1321 if (keyname[0] == '_')
1324 key = PRVM_ED_FindField (prog, keyname);
1327 Con_DPrintf("%s: '%s' is not a field\n", prog->name, keyname);
1334 strlcpy (temp, com_token, sizeof(temp));
1335 dpsnprintf (com_token, sizeof(com_token), "0 %s 0", temp);
1338 if (!PRVM_ED_ParseEpair(prog, ent, key, com_token, strcmp(keyname, "wad") != 0))
1339 prog->error_cmd("PRVM_ED_ParseEdict: parse error");
1343 ent->priv.required->free = true;
1344 ent->priv.required->freetime = realtime;
1353 PRVM_ED_LoadFromFile
1355 The entities are directly placed in the array, rather than allocated with
1356 PRVM_ED_Alloc, because otherwise an error loading the map would have entity
1357 number references out of order.
1359 Creates a server's entity / program execution context by
1360 parsing textual entity definitions out of an ent file.
1362 Used for both fresh maps and savegame loads. A fresh map would also need
1363 to call PRVM_ED_CallSpawnFunctions () to let the objects initialize themselves.
1366 void PRVM_ED_LoadFromFile (prvm_prog_t *prog, const char *data)
1369 int parsed, inhibited, spawned, died;
1370 const char *funcname;
1379 prvm_reuseedicts_always_allow = realtime;
1384 // parse the opening brace
1385 if (!COM_ParseToken_Simple(&data, false, false, true))
1387 if (com_token[0] != '{')
1388 prog->error_cmd("PRVM_ED_LoadFromFile: %s: found %s when expecting {", prog->name, com_token);
1390 // CHANGED: this is not conform to PR_LoadFromFile
1391 if(prog->loadintoworld)
1393 prog->loadintoworld = false;
1394 ent = PRVM_EDICT_NUM(0);
1397 ent = PRVM_ED_Alloc(prog);
1400 if (ent != prog->edicts) // hack
1401 memset (ent->fields.fp, 0, prog->entityfields * sizeof(prvm_vec_t));
1403 data = PRVM_ED_ParseEdict (prog, data, ent);
1406 // remove the entity ?
1407 if(!prog->load_edict(prog, ent))
1409 PRVM_ED_Free(prog, ent);
1414 if (PRVM_serverfunction(SV_OnEntityPreSpawnFunction))
1417 PRVM_serverglobalfloat(time) = sv.time;
1418 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1419 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityPreSpawnFunction), "QC function SV_OnEntityPreSpawnFunction is missing");
1422 if(ent->priv.required->free)
1429 // immediately call spawn function, but only if there is a self global and a classname
1431 if(!ent->priv.required->free)
1433 if (!PRVM_alledictstring(ent, classname))
1435 Con_Print("No classname for:\n");
1436 PRVM_ED_Print(prog, ent, NULL);
1437 PRVM_ED_Free (prog, ent);
1441 // look for the spawn function
1442 funcname = PRVM_GetString(prog, PRVM_alledictstring(ent, classname));
1443 func = PRVM_ED_FindFunction (prog, va(vabuf, sizeof(vabuf), "spawnfunc_%s", funcname));
1445 if(!PRVM_allglobalfloat(require_spawnfunc_prefix))
1446 func = PRVM_ED_FindFunction (prog, funcname);
1450 // check for OnEntityNoSpawnFunction
1451 if (PRVM_serverfunction(SV_OnEntityNoSpawnFunction))
1454 PRVM_serverglobalfloat(time) = sv.time;
1455 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1456 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityNoSpawnFunction), "QC function SV_OnEntityNoSpawnFunction is missing");
1460 if (developer.integer > 0) // don't confuse non-developers with errors
1462 Con_Print("No spawn function for:\n");
1463 PRVM_ED_Print(prog, ent, NULL);
1465 PRVM_ED_Free (prog, ent);
1466 continue; // not included in "inhibited" count
1472 PRVM_serverglobalfloat(time) = sv.time;
1473 PRVM_allglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1474 prog->ExecuteProgram(prog, func - prog->functions, "");
1478 if(!ent->priv.required->free)
1479 if (PRVM_serverfunction(SV_OnEntityPostSpawnFunction))
1482 PRVM_serverglobalfloat(time) = sv.time;
1483 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1484 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityPostSpawnFunction), "QC function SV_OnEntityPostSpawnFunction is missing");
1488 if (ent->priv.required->free)
1492 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);
1494 prvm_reuseedicts_always_allow = 0;
1497 static void PRVM_FindOffsets(prvm_prog_t *prog)
1499 // field and global searches use -1 for NULL
1500 memset(&prog->fieldoffsets, -1, sizeof(prog->fieldoffsets));
1501 memset(&prog->globaloffsets, -1, sizeof(prog->globaloffsets));
1502 // function searches use 0 for NULL
1503 memset(&prog->funcoffsets, 0, sizeof(prog->funcoffsets));
1504 #define PRVM_DECLARE_serverglobalfloat(x)
1505 #define PRVM_DECLARE_serverglobalvector(x)
1506 #define PRVM_DECLARE_serverglobalstring(x)
1507 #define PRVM_DECLARE_serverglobaledict(x)
1508 #define PRVM_DECLARE_serverglobalfunction(x)
1509 #define PRVM_DECLARE_clientglobalfloat(x)
1510 #define PRVM_DECLARE_clientglobalvector(x)
1511 #define PRVM_DECLARE_clientglobalstring(x)
1512 #define PRVM_DECLARE_clientglobaledict(x)
1513 #define PRVM_DECLARE_clientglobalfunction(x)
1514 #define PRVM_DECLARE_menuglobalfloat(x)
1515 #define PRVM_DECLARE_menuglobalvector(x)
1516 #define PRVM_DECLARE_menuglobalstring(x)
1517 #define PRVM_DECLARE_menuglobaledict(x)
1518 #define PRVM_DECLARE_menuglobalfunction(x)
1519 #define PRVM_DECLARE_serverfieldfloat(x)
1520 #define PRVM_DECLARE_serverfieldvector(x)
1521 #define PRVM_DECLARE_serverfieldstring(x)
1522 #define PRVM_DECLARE_serverfieldedict(x)
1523 #define PRVM_DECLARE_serverfieldfunction(x)
1524 #define PRVM_DECLARE_clientfieldfloat(x)
1525 #define PRVM_DECLARE_clientfieldvector(x)
1526 #define PRVM_DECLARE_clientfieldstring(x)
1527 #define PRVM_DECLARE_clientfieldedict(x)
1528 #define PRVM_DECLARE_clientfieldfunction(x)
1529 #define PRVM_DECLARE_menufieldfloat(x)
1530 #define PRVM_DECLARE_menufieldvector(x)
1531 #define PRVM_DECLARE_menufieldstring(x)
1532 #define PRVM_DECLARE_menufieldedict(x)
1533 #define PRVM_DECLARE_menufieldfunction(x)
1534 #define PRVM_DECLARE_serverfunction(x)
1535 #define PRVM_DECLARE_clientfunction(x)
1536 #define PRVM_DECLARE_menufunction(x)
1537 #define PRVM_DECLARE_field(x) prog->fieldoffsets.x = PRVM_ED_FindFieldOffset(prog, #x);
1538 #define PRVM_DECLARE_global(x) prog->globaloffsets.x = PRVM_ED_FindGlobalOffset(prog, #x);
1539 #define PRVM_DECLARE_function(x) prog->funcoffsets.x = PRVM_ED_FindFunctionOffset(prog, #x);
1540 #include "prvm_offsets.h"
1541 #undef PRVM_DECLARE_serverglobalfloat
1542 #undef PRVM_DECLARE_serverglobalvector
1543 #undef PRVM_DECLARE_serverglobalstring
1544 #undef PRVM_DECLARE_serverglobaledict
1545 #undef PRVM_DECLARE_serverglobalfunction
1546 #undef PRVM_DECLARE_clientglobalfloat
1547 #undef PRVM_DECLARE_clientglobalvector
1548 #undef PRVM_DECLARE_clientglobalstring
1549 #undef PRVM_DECLARE_clientglobaledict
1550 #undef PRVM_DECLARE_clientglobalfunction
1551 #undef PRVM_DECLARE_menuglobalfloat
1552 #undef PRVM_DECLARE_menuglobalvector
1553 #undef PRVM_DECLARE_menuglobalstring
1554 #undef PRVM_DECLARE_menuglobaledict
1555 #undef PRVM_DECLARE_menuglobalfunction
1556 #undef PRVM_DECLARE_serverfieldfloat
1557 #undef PRVM_DECLARE_serverfieldvector
1558 #undef PRVM_DECLARE_serverfieldstring
1559 #undef PRVM_DECLARE_serverfieldedict
1560 #undef PRVM_DECLARE_serverfieldfunction
1561 #undef PRVM_DECLARE_clientfieldfloat
1562 #undef PRVM_DECLARE_clientfieldvector
1563 #undef PRVM_DECLARE_clientfieldstring
1564 #undef PRVM_DECLARE_clientfieldedict
1565 #undef PRVM_DECLARE_clientfieldfunction
1566 #undef PRVM_DECLARE_menufieldfloat
1567 #undef PRVM_DECLARE_menufieldvector
1568 #undef PRVM_DECLARE_menufieldstring
1569 #undef PRVM_DECLARE_menufieldedict
1570 #undef PRVM_DECLARE_menufieldfunction
1571 #undef PRVM_DECLARE_serverfunction
1572 #undef PRVM_DECLARE_clientfunction
1573 #undef PRVM_DECLARE_menufunction
1574 #undef PRVM_DECLARE_field
1575 #undef PRVM_DECLARE_global
1576 #undef PRVM_DECLARE_function
1581 typedef struct dpfield_s
1588 #define DPFIELDS (sizeof(dpfields) / sizeof(dpfield_t))
1590 dpfield_t dpfields[] =
1601 #define PO_HASHSIZE 16384
1602 typedef struct po_string_s
1605 struct po_string_s *nextonhashchain;
1610 po_string_t *hashtable[PO_HASHSIZE];
1613 static void PRVM_PO_UnparseString(char *out, const char *in, size_t outsize)
1622 case '\a': if(outsize >= 2) { *out++ = '\\'; *out++ = 'a'; outsize -= 2; } break;
1623 case '\b': if(outsize >= 2) { *out++ = '\\'; *out++ = 'b'; outsize -= 2; } break;
1624 case '\t': if(outsize >= 2) { *out++ = '\\'; *out++ = 't'; outsize -= 2; } break;
1625 case '\r': if(outsize >= 2) { *out++ = '\\'; *out++ = 'r'; outsize -= 2; } break;
1626 case '\n': if(outsize >= 2) { *out++ = '\\'; *out++ = 'n'; outsize -= 2; } break;
1627 case '\\': if(outsize >= 2) { *out++ = '\\'; *out++ = '\\'; outsize -= 2; } break;
1628 case '"': if(outsize >= 2) { *out++ = '\\'; *out++ = '"'; outsize -= 2; } break;
1630 if(*in >= 0 && *in <= 0x1F)
1635 *out++ = '0' + ((*in & 0700) >> 6);
1636 *out++ = '0' + ((*in & 0070) >> 3);
1637 *out++ = '0' + (*in & 0007) ;
1654 static void PRVM_PO_ParseString(char *out, const char *in, size_t outsize)
1667 case 'a': if(outsize > 0) { *out++ = '\a'; --outsize; } break;
1668 case 'b': if(outsize > 0) { *out++ = '\b'; --outsize; } break;
1669 case 't': if(outsize > 0) { *out++ = '\t'; --outsize; } break;
1670 case 'r': if(outsize > 0) { *out++ = '\r'; --outsize; } break;
1671 case 'n': if(outsize > 0) { *out++ = '\n'; --outsize; } break;
1672 case '\\': if(outsize > 0) { *out++ = '\\'; --outsize; } break;
1673 case '"': if(outsize > 0) { *out++ = '"'; --outsize; } break;
1674 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
1678 if(*in >= '0' && *in <= '7')
1681 *out = (*out << 3) | (*in - '0');
1684 if(*in >= '0' && *in <= '7')
1687 *out = (*out << 3) | (*in - '0');
1698 if(outsize > 0) { *out++ = *in; --outsize; }
1713 static po_t *PRVM_PO_Load(const char *filename, const char *filename2, mempool_t *pool)
1718 char inbuf[MAX_INPUTLINE];
1719 char decodedbuf[MAX_INPUTLINE];
1722 po_string_t thisstr;
1725 for (i = 0; i < 2; ++i)
1727 const char *buf = (const char *)
1728 FS_LoadFile((i > 0 ? filename : filename2), pool, true, NULL);
1729 // first read filename2, then read filename
1730 // so that progs.dat.de.po wins over common.de.po
1731 // and within file, last item wins
1738 po = (po_t *)Mem_Alloc(pool, sizeof(*po));
1739 memset(po, 0, sizeof(*po));
1742 memset(&thisstr, 0, sizeof(thisstr)); // hush compiler warning
1750 p = strchr(p, '\n');
1756 if(*p == '\r' || *p == '\n')
1761 if(!strncmp(p, "msgid \"", 7))
1766 else if(!strncmp(p, "msgstr \"", 8))
1773 p = strchr(p, '\n');
1783 q = strchr(p, '\n');
1790 if((size_t)(q - p) >= (size_t) sizeof(inbuf))
1792 strlcpy(inbuf, p, q - p); // not - 1, because this adds a NUL
1793 PRVM_PO_ParseString(decodedbuf + decodedpos, inbuf, sizeof(decodedbuf) - decodedpos);
1794 decodedpos += strlen(decodedbuf + decodedpos);
1804 Mem_Free(thisstr.key);
1805 thisstr.key = (char *)Mem_Alloc(pool, decodedpos + 1);
1806 memcpy(thisstr.key, decodedbuf, decodedpos + 1);
1808 else if(decodedpos > 0 && thisstr.key) // skip empty translation results
1810 thisstr.value = (char *)Mem_Alloc(pool, decodedpos + 1);
1811 memcpy(thisstr.value, decodedbuf, decodedpos + 1);
1812 hashindex = CRC_Block((const unsigned char *) thisstr.key, strlen(thisstr.key)) % PO_HASHSIZE;
1813 thisstr.nextonhashchain = po->hashtable[hashindex];
1814 po->hashtable[hashindex] = (po_string_t *)Mem_Alloc(pool, sizeof(thisstr));
1815 memcpy(po->hashtable[hashindex], &thisstr, sizeof(thisstr));
1816 memset(&thisstr, 0, sizeof(thisstr));
1820 Mem_Free((char *) buf);
1825 static const char *PRVM_PO_Lookup(po_t *po, const char *str)
1827 int hashindex = CRC_Block((const unsigned char *) str, strlen(str)) % PO_HASHSIZE;
1828 po_string_t *p = po->hashtable[hashindex];
1831 if(!strcmp(str, p->key))
1833 p = p->nextonhashchain;
1837 static void PRVM_PO_Destroy(po_t *po)
1840 for(i = 0; i < PO_HASHSIZE; ++i)
1842 po_string_t *p = po->hashtable[i];
1846 p = p->nextonhashchain;
1855 void PRVM_LeakTest(prvm_prog_t *prog);
1856 void PRVM_Prog_Reset(prvm_prog_t *prog)
1860 PRVM_LeakTest(prog);
1861 prog->reset_cmd(prog);
1862 Mem_FreePool(&prog->progs_mempool);
1864 PRVM_PO_Destroy((po_t *) prog->po);
1866 memset(prog,0,sizeof(prvm_prog_t));
1867 prog->break_statement = -1;
1868 prog->watch_global_type = ev_void;
1869 prog->watch_field_type = ev_void;
1877 static void PRVM_LoadLNO( prvm_prog_t *prog, const char *progname ) {
1878 fs_offset_t filesize;
1880 unsigned int *header;
1883 FS_StripExtension( progname, filename, sizeof( filename ) );
1884 strlcat( filename, ".lno", sizeof( filename ) );
1886 lno = FS_LoadFile( filename, tempmempool, false, &filesize );
1892 <Spike> SafeWrite (h, &lnotype, sizeof(int));
1893 <Spike> SafeWrite (h, &version, sizeof(int));
1894 <Spike> SafeWrite (h, &numglobaldefs, sizeof(int));
1895 <Spike> SafeWrite (h, &numpr_globals, sizeof(int));
1896 <Spike> SafeWrite (h, &numfielddefs, sizeof(int));
1897 <Spike> SafeWrite (h, &numstatements, sizeof(int));
1898 <Spike> SafeWrite (h, statement_linenums, numstatements*sizeof(int));
1900 if ((unsigned int)filesize < (6 + prog->progs_numstatements) * sizeof(int))
1906 header = (unsigned int *) lno;
1907 if( header[ 0 ] == *(unsigned int *) "LNOF" &&
1908 LittleLong( header[ 1 ] ) == 1 &&
1909 (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs_numglobaldefs &&
1910 (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs_numglobals &&
1911 (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs_numfielddefs &&
1912 (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs_numstatements )
1914 prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof( int ) );
1915 memcpy( prog->statement_linenums, header + 6, prog->progs_numstatements * sizeof( int ) );
1917 /* gmqcc suports columnums */
1918 if ((unsigned int)filesize > ((6 + 2 * prog->progs_numstatements) * sizeof( int )))
1920 prog->statement_columnnums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof( int ) );
1921 memcpy( prog->statement_columnnums, header + 6 + prog->progs_numstatements, prog->progs_numstatements * sizeof( int ) );
1932 static void PRVM_UpdateBreakpoints(prvm_prog_t *prog);
1933 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)
1936 dprograms_t *dprograms;
1937 dstatement_t *instatements;
1938 ddef_t *infielddefs;
1939 ddef_t *inglobaldefs;
1941 dfunction_t *infunctions;
1943 fs_offset_t filesize;
1944 int requiredglobalspace;
1961 prog->error_cmd("PRVM_LoadProgs: there is already a %s program loaded!", prog->name );
1963 Host_LockSession(); // all progs can use the session cvar
1964 Crypto_LoadKeys(); // all progs might use the keys at init time
1968 dprograms = (dprograms_t *) data;
1972 dprograms = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
1973 if (dprograms == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
1974 prog->error_cmd("PRVM_LoadProgs: couldn't load %s for %s", filename, prog->name);
1975 // TODO bounds check header fields (e.g. numstatements), they must never go behind end of file
1977 prog->profiletime = Sys_DirtyTime();
1978 prog->starttime = realtime;
1980 Con_DPrintf("%s programs occupy %iK.\n", prog->name, (int)(filesize/1024));
1982 requiredglobalspace = 0;
1983 for (i = 0;i < numrequiredglobals;i++)
1984 requiredglobalspace += required_global[i].type == ev_vector ? 3 : 1;
1986 prog->filecrc = CRC_Block((unsigned char *)dprograms, filesize);
1988 // byte swap the header
1989 prog->progs_version = LittleLong(dprograms->version);
1990 prog->progs_crc = LittleLong(dprograms->crc);
1991 if (prog->progs_version != PROG_VERSION)
1992 prog->error_cmd("%s: %s has wrong version number (%i should be %i)", prog->name, filename, prog->progs_version, PROG_VERSION);
1993 instatements = (dstatement_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_statements));
1994 prog->progs_numstatements = LittleLong(dprograms->numstatements);
1995 inglobaldefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globaldefs));
1996 prog->progs_numglobaldefs = LittleLong(dprograms->numglobaldefs);
1997 infielddefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_fielddefs));
1998 prog->progs_numfielddefs = LittleLong(dprograms->numfielddefs);
1999 infunctions = (dfunction_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_functions));
2000 prog->progs_numfunctions = LittleLong(dprograms->numfunctions);
2001 instrings = (char *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_strings));
2002 prog->progs_numstrings = LittleLong(dprograms->numstrings);
2003 inglobals = (int *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globals));
2004 prog->progs_numglobals = LittleLong(dprograms->numglobals);
2005 prog->progs_entityfields = LittleLong(dprograms->entityfields);
2007 prog->numstatements = prog->progs_numstatements;
2008 prog->numglobaldefs = prog->progs_numglobaldefs;
2009 prog->numfielddefs = prog->progs_numfielddefs;
2010 prog->numfunctions = prog->progs_numfunctions;
2011 prog->numstrings = prog->progs_numstrings;
2012 prog->numglobals = prog->progs_numglobals;
2013 prog->entityfields = prog->progs_entityfields;
2015 if (LittleLong(dprograms->ofs_strings) + prog->progs_numstrings > (int)filesize)
2016 prog->error_cmd("%s: %s strings go past end of file", prog->name, filename);
2017 prog->strings = (char *)Mem_Alloc(prog->progs_mempool, prog->progs_numstrings);
2018 memcpy(prog->strings, instrings, prog->progs_numstrings);
2019 prog->stringssize = prog->progs_numstrings;
2021 prog->numknownstrings = 0;
2022 prog->maxknownstrings = 0;
2023 prog->knownstrings = NULL;
2024 prog->knownstrings_freeable = NULL;
2026 Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
2028 // we need to expand the globaldefs and fielddefs to include engine defs
2029 prog->globaldefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobaldefs + numrequiredglobals) * sizeof(ddef_t));
2030 prog->globals.fp = (prvm_vec_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobals + requiredglobalspace + 2) * sizeof(prvm_vec_t));
2031 // + 2 is because of an otherwise occurring overrun in RETURN instruction
2032 // when trying to return the last or second-last global
2033 // (RETURN always returns a vector, there is no RETURN_F instruction)
2034 prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numfielddefs + numrequiredfields) * sizeof(ddef_t));
2035 // we need to convert the statements to our memory format
2036 prog->statements = (mstatement_t *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(mstatement_t));
2037 // allocate space for profiling statement usage
2038 prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(*prog->statement_profile));
2039 prog->explicit_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(*prog->statement_profile));
2040 // functions need to be converted to the memory format
2041 prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs_numfunctions);
2043 for (i = 0;i < prog->progs_numfunctions;i++)
2045 prog->functions[i].first_statement = LittleLong(infunctions[i].first_statement);
2046 prog->functions[i].parm_start = LittleLong(infunctions[i].parm_start);
2047 prog->functions[i].s_name = LittleLong(infunctions[i].s_name);
2048 prog->functions[i].s_file = LittleLong(infunctions[i].s_file);
2049 prog->functions[i].numparms = LittleLong(infunctions[i].numparms);
2050 prog->functions[i].locals = LittleLong(infunctions[i].locals);
2051 memcpy(prog->functions[i].parm_size, infunctions[i].parm_size, sizeof(infunctions[i].parm_size));
2052 if(prog->functions[i].first_statement >= prog->numstatements)
2053 prog->error_cmd("PRVM_LoadProgs: out of bounds function statement (function %d) in %s", i, prog->name);
2054 // TODO bounds check parm_start, s_name, s_file, numparms, locals, parm_size
2057 // copy the globaldefs to the new globaldefs list
2058 for (i=0 ; i<prog->numglobaldefs ; i++)
2060 prog->globaldefs[i].type = LittleShort(inglobaldefs[i].type);
2061 prog->globaldefs[i].ofs = LittleShort(inglobaldefs[i].ofs);
2062 prog->globaldefs[i].s_name = LittleLong(inglobaldefs[i].s_name);
2063 // TODO bounds check ofs, s_name
2066 // append the required globals
2067 for (i = 0;i < numrequiredglobals;i++)
2069 prog->globaldefs[prog->numglobaldefs].type = required_global[i].type;
2070 prog->globaldefs[prog->numglobaldefs].ofs = prog->numglobals;
2071 prog->globaldefs[prog->numglobaldefs].s_name = PRVM_SetEngineString(prog, required_global[i].name);
2072 if (prog->globaldefs[prog->numglobaldefs].type == ev_vector)
2073 prog->numglobals += 3;
2076 prog->numglobaldefs++;
2079 // copy the progs fields to the new fields list
2080 for (i = 0;i < prog->numfielddefs;i++)
2082 prog->fielddefs[i].type = LittleShort(infielddefs[i].type);
2083 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
2084 prog->error_cmd("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", prog->name);
2085 prog->fielddefs[i].ofs = LittleShort(infielddefs[i].ofs);
2086 prog->fielddefs[i].s_name = LittleLong(infielddefs[i].s_name);
2087 // TODO bounds check ofs, s_name
2090 // append the required fields
2091 for (i = 0;i < numrequiredfields;i++)
2093 prog->fielddefs[prog->numfielddefs].type = required_field[i].type;
2094 prog->fielddefs[prog->numfielddefs].ofs = prog->entityfields;
2095 prog->fielddefs[prog->numfielddefs].s_name = PRVM_SetEngineString(prog, required_field[i].name);
2096 if (prog->fielddefs[prog->numfielddefs].type == ev_vector)
2097 prog->entityfields += 3;
2099 prog->entityfields++;
2100 prog->numfielddefs++;
2103 // LadyHavoc: TODO: reorder globals to match engine struct
2104 // LadyHavoc: TODO: reorder fields to match engine struct
2105 #define remapglobal(index) (index)
2106 #define remapfield(index) (index)
2109 // FIXME: LadyHavoc: this uses a crude way to identify integer constants, rather than checking for matching globaldefs and checking their type
2110 for (i = 0;i < prog->progs_numglobals;i++)
2112 u.i = LittleLong(inglobals[i]);
2113 // most globals are 0, we only need to deal with the ones that are not
2116 d = u.i & 0xFF800000;
2117 if ((d == 0xFF800000) || (d == 0))
2119 // Looks like an integer (expand to int64)
2120 prog->globals.ip[remapglobal(i)] = u.i;
2124 // Looks like a float (expand to double)
2125 prog->globals.fp[remapglobal(i)] = u.f;
2130 // LadyHavoc: TODO: support 32bit progs statement formats
2131 // copy, remap globals in statements, bounds check
2132 for (i = 0;i < prog->progs_numstatements;i++)
2134 op = (opcode_t)LittleShort(instatements[i].op);
2135 a = (unsigned short)LittleShort(instatements[i].a);
2136 b = (unsigned short)LittleShort(instatements[i].b);
2137 c = (unsigned short)LittleShort(instatements[i].c);
2143 if (a >= prog->progs_numglobals || b + i < 0 || b + i >= prog->progs_numstatements)
2144 prog->error_cmd("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", i, prog->name);
2145 prog->statements[i].op = op;
2146 prog->statements[i].operand[0] = remapglobal(a);
2147 prog->statements[i].operand[1] = -1;
2148 prog->statements[i].operand[2] = -1;
2149 prog->statements[i].jumpabsolute = i + b;
2153 if (a + i < 0 || a + i >= prog->progs_numstatements)
2154 prog->error_cmd("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, prog->name);
2155 prog->statements[i].op = op;
2156 prog->statements[i].operand[0] = -1;
2157 prog->statements[i].operand[1] = -1;
2158 prog->statements[i].operand[2] = -1;
2159 prog->statements[i].jumpabsolute = i + a;
2162 Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", (int)op, i, prog->name);
2164 // global global global
2199 if (a >= prog->progs_numglobals || b >= prog->progs_numglobals || c >= prog->progs_numglobals)
2200 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
2201 prog->statements[i].op = op;
2202 prog->statements[i].operand[0] = remapglobal(a);
2203 prog->statements[i].operand[1] = remapglobal(b);
2204 prog->statements[i].operand[2] = remapglobal(c);
2205 prog->statements[i].jumpabsolute = -1;
2207 // global none global
2213 if (a >= prog->progs_numglobals || c >= prog->progs_numglobals)
2214 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2215 prog->statements[i].op = op;
2216 prog->statements[i].operand[0] = remapglobal(a);
2217 prog->statements[i].operand[1] = -1;
2218 prog->statements[i].operand[2] = remapglobal(c);
2219 prog->statements[i].jumpabsolute = -1;
2235 if (a >= prog->progs_numglobals || b >= prog->progs_numglobals)
2236 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2237 prog->statements[i].op = op;
2238 prog->statements[i].operand[0] = remapglobal(a);
2239 prog->statements[i].operand[1] = remapglobal(b);
2240 prog->statements[i].operand[2] = -1;
2241 prog->statements[i].jumpabsolute = -1;
2245 if ( a < prog->progs_numglobals)
2246 if ( prog->globals.ip[remapglobal(a)] >= 0 )
2247 if ( prog->globals.ip[remapglobal(a)] < prog->progs_numfunctions )
2248 if ( prog->functions[prog->globals.ip[remapglobal(a)]].first_statement == -642 )
2249 ++prog->numexplicitcoveragestatements;
2260 if ( a >= prog->progs_numglobals)
2261 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2262 prog->statements[i].op = op;
2263 prog->statements[i].operand[0] = remapglobal(a);
2264 prog->statements[i].operand[1] = -1;
2265 prog->statements[i].operand[2] = -1;
2266 prog->statements[i].jumpabsolute = -1;
2270 if(prog->numstatements < 1)
2272 prog->error_cmd("PRVM_LoadProgs: empty program in %s", prog->name);
2274 else switch(prog->statements[prog->numstatements - 1].op)
2281 prog->error_cmd("PRVM_LoadProgs: program may fall off the edge (does not end with RETURN, GOTO or DONE) in %s", prog->name);
2285 // we're done with the file now
2287 Mem_Free(dprograms);
2290 // check required functions
2291 for(i=0 ; i < numrequiredfunc ; i++)
2292 if(PRVM_ED_FindFunction(prog, required_func[i]) == 0)
2293 prog->error_cmd("%s: %s not found in %s",prog->name, required_func[i], filename);
2295 PRVM_LoadLNO(prog, filename);
2297 PRVM_Init_Exec(prog);
2299 if(*prvm_language.string)
2300 // in CSQC we really shouldn't be able to change how stuff works... sorry for now
2301 // later idea: include a list of authorized .po file checksums with the csprogs
2303 qboolean deftrans = prog == CLVM_prog;
2304 const char *realfilename = (prog != CLVM_prog ? filename : csqc_progname.string);
2305 if(deftrans) // once we have dotranslate_ strings, ALWAYS use the opt-in method!
2307 for (i=0 ; i<prog->numglobaldefs ; i++)
2310 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2311 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2312 if(name && !strncmp(name, "dotranslate_", 12))
2319 if(!strcmp(prvm_language.string, "dump"))
2321 qfile_t *f = FS_OpenRealFile(va(vabuf, sizeof(vabuf), "%s.pot", realfilename), "w", false);
2322 Con_Printf("Dumping to %s.pot\n", realfilename);
2325 for (i=0 ; i<prog->numglobaldefs ; i++)
2328 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2329 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2330 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2332 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2333 const char *value = PRVM_GetString(prog, val->string);
2336 char buf[MAX_INPUTLINE];
2337 PRVM_PO_UnparseString(buf, value, sizeof(buf));
2338 FS_Printf(f, "msgid \"%s\"\nmsgstr \"\"\n\n", buf);
2347 po_t *po = PRVM_PO_Load(
2348 va(vabuf, sizeof(vabuf), "%s.%s.po", realfilename, prvm_language.string),
2349 va(vabuf2, sizeof(vabuf2), "common.%s.po", prvm_language.string),
2350 prog->progs_mempool);
2353 for (i=0 ; i<prog->numglobaldefs ; i++)
2356 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2357 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2358 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2360 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2361 const char *value = PRVM_GetString(prog, val->string);
2364 value = PRVM_PO_Lookup(po, value);
2366 val->string = PRVM_SetEngineString(prog, value);
2374 for (cvar = prog->console_cmd->cvars->vars; cvar; cvar = cvar->next)
2375 cvar->globaldefindex[prog - prvm_prog_list] = -1;
2377 for (i=0 ; i<prog->numglobaldefs ; i++)
2380 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2381 //Con_Printf("found var %s\n", name);
2383 && !strncmp(name, "autocvar_", 9)
2384 && !(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
2387 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2388 cvar = Cvar_FindVar(prog->console_cmd->cvars, name + 9, prog->console_cmd->cvars_flagsmask);
2389 //Con_Printf("PRVM_LoadProgs: autocvar global %s in %s, processing...\n", name, prog->name);
2394 Con_DPrintf("PRVM_LoadProgs: no cvar for autocvar global %s in %s, creating...\n", name, prog->name);
2395 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2398 if((float)((int)(val->_float)) == val->_float)
2399 dpsnprintf(buf, sizeof(buf), "%i", (int)(val->_float));
2401 dpsnprintf(buf, sizeof(buf), "%.9g", val->_float);
2405 dpsnprintf(buf, sizeof(buf), "%.9g %.9g %.9g", val->vector[0], val->vector[1], val->vector[2]); value = buf;
2408 value = PRVM_GetString(prog, val->string);
2411 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2414 cvar = Cvar_Get(prog->console_cmd->cvars, name + 9, value, prog->console_cmd->cvars_flagsmask, NULL);
2415 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2417 val->string = PRVM_SetEngineString(prog, cvar->string);
2418 cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2421 prog->error_cmd("PRVM_LoadProgs: could not create cvar for autocvar global %s in %s", name, prog->name);
2422 cvar->globaldefindex[prog - prvm_prog_list] = i;
2424 else if((cvar->flags & CVAR_PRIVATE) == 0)
2426 // MUST BE SYNCED WITH cvar.c Cvar_Set
2429 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2432 val->_float = cvar->value;
2436 VectorClear(val->vector);
2437 for (j = 0;j < 3;j++)
2439 while (*s && ISWHITESPACE(*s))
2443 val->vector[j] = atof(s);
2444 while (!ISWHITESPACE(*s))
2451 val->string = PRVM_SetEngineString(prog, cvar->string);
2452 cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2455 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2458 cvar->globaldefindex[prog - prvm_prog_list] = i;
2461 Con_Printf("PRVM_LoadProgs: private cvar for autocvar global %s in %s\n", name, prog->name);
2467 prog->loaded = TRUE;
2469 PRVM_UpdateBreakpoints(prog);
2471 // set flags & ddef_ts in prog
2475 PRVM_FindOffsets(prog);
2477 prog->init_cmd(prog);
2480 PRVM_MEM_Alloc(prog);
2482 // Inittime is at least the time when this function finished. However,
2483 // later events may bump it.
2484 prog->inittime = realtime;
2488 static void PRVM_Fields_f(cmd_state_t *cmd)
2491 int i, j, ednum, used, usedamount;
2493 char tempstring[MAX_INPUTLINE], tempstring2[260];
2503 Con_Print("no progs loaded\n");
2508 if(Cmd_Argc(cmd) != 2)
2510 Con_Print("prvm_fields <program name>\n");
2514 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2517 counts = (int *)Mem_Alloc(tempmempool, prog->numfielddefs * sizeof(int));
2518 for (ednum = 0;ednum < prog->max_edicts;ednum++)
2520 ed = PRVM_EDICT_NUM(ednum);
2521 if (ed->priv.required->free)
2523 for (i = 1;i < prog->numfielddefs;i++)
2525 d = &prog->fielddefs[i];
2526 name = PRVM_GetString(prog, d->s_name);
2527 if (name[strlen(name)-2] == '_')
2528 continue; // skip _x, _y, _z vars
2529 val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
2530 // if the value is still all 0, skip the field
2531 for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
2533 if (val->ivector[j])
2544 for (i = 0;i < prog->numfielddefs;i++)
2546 d = &prog->fielddefs[i];
2547 name = PRVM_GetString(prog, d->s_name);
2548 if (name[strlen(name)-2] == '_')
2549 continue; // skip _x, _y, _z vars
2550 switch(d->type & ~DEF_SAVEGLOBAL)
2553 strlcat(tempstring, "string ", sizeof(tempstring));
2556 strlcat(tempstring, "entity ", sizeof(tempstring));
2559 strlcat(tempstring, "function ", sizeof(tempstring));
2562 strlcat(tempstring, "field ", sizeof(tempstring));
2565 strlcat(tempstring, "void ", sizeof(tempstring));
2568 strlcat(tempstring, "float ", sizeof(tempstring));
2571 strlcat(tempstring, "vector ", sizeof(tempstring));
2574 strlcat(tempstring, "pointer ", sizeof(tempstring));
2577 dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
2578 strlcat(tempstring, tempstring2, sizeof(tempstring));
2581 if (strlen(name) > sizeof(tempstring2)-4)
2583 memcpy (tempstring2, name, sizeof(tempstring2)-4);
2584 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
2585 tempstring2[sizeof(tempstring2)-1] = 0;
2588 strlcat(tempstring, name, sizeof(tempstring));
2589 for (j = (int)strlen(name);j < 25;j++)
2590 strlcat(tempstring, " ", sizeof(tempstring));
2591 dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
2592 strlcat(tempstring, tempstring2, sizeof(tempstring));
2593 strlcat(tempstring, "\n", sizeof(tempstring));
2594 if (strlen(tempstring) >= sizeof(tempstring)/2)
2596 Con_Print(tempstring);
2602 usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
2606 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);
2609 static void PRVM_Globals_f(cmd_state_t *cmd)
2613 const char *wildcard;
2619 Con_Print("no progs loaded\n");
2622 if(Cmd_Argc (cmd) < 2 || Cmd_Argc(cmd) > 3)
2624 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
2628 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2631 if( Cmd_Argc(cmd) == 3)
2632 wildcard = Cmd_Argv(cmd, 2);
2636 Con_Printf("%s :", prog->name);
2638 for (i = 0;i < prog->numglobaldefs;i++)
2641 if( !matchpattern( PRVM_GetString(prog, prog->globaldefs[i].s_name), wildcard, 1) )
2646 Con_Printf("%s\n", PRVM_GetString(prog, prog->globaldefs[i].s_name));
2648 Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->numglobals, numculled, prog->numglobals * 4);
2656 static void PRVM_Global_f(cmd_state_t *cmd)
2660 char valuebuf[MAX_INPUTLINE];
2661 if( Cmd_Argc(cmd) != 3 ) {
2662 Con_Printf( "prvm_global <program name> <global name>\n" );
2666 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2669 global = PRVM_ED_FindGlobal( prog, Cmd_Argv(cmd, 2) );
2671 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(cmd, 2), Cmd_Argv(cmd, 1) );
2673 Con_Printf( "%s: %s\n", Cmd_Argv(cmd, 2), PRVM_ValueString( prog, (etype_t)global->type, PRVM_GLOBALFIELDVALUE(global->ofs), valuebuf, sizeof(valuebuf) ) );
2681 static void PRVM_GlobalSet_f(cmd_state_t *cmd)
2685 if( Cmd_Argc(cmd) != 4 ) {
2686 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2690 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2693 global = PRVM_ED_FindGlobal( prog, Cmd_Argv(cmd, 2) );
2695 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(cmd, 2), Cmd_Argv(cmd, 1) );
2697 PRVM_ED_ParseEpair( prog, NULL, global, Cmd_Argv(cmd, 3), true );
2701 ======================
2702 Break- and Watchpoints
2703 ======================
2707 char break_statement[256];
2708 char watch_global[256];
2710 char watch_field[256];
2713 static debug_data_t debug_data[PRVM_PROG_MAX];
2715 void PRVM_Breakpoint(prvm_prog_t *prog, int stack_index, const char *text)
2718 Con_Printf("PRVM_Breakpoint: %s\n", text);
2719 PRVM_PrintState(prog, stack_index);
2720 if (prvm_breakpointdump.integer)
2721 Host_Savegame_to(prog, va(vabuf, sizeof(vabuf), "breakpoint-%s.dmp", prog->name));
2724 void PRVM_Watchpoint(prvm_prog_t *prog, int stack_index, const char *text, etype_t type, prvm_eval_t *o, prvm_eval_t *n)
2726 size_t sz = sizeof(prvm_vec_t) * ((type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2727 if (memcmp(o, n, sz))
2730 char valuebuf_o[128];
2731 char valuebuf_n[128];
2732 PRVM_UglyValueString(prog, type, o, valuebuf_o, sizeof(valuebuf_o));
2733 PRVM_UglyValueString(prog, type, n, valuebuf_n, sizeof(valuebuf_n));
2734 dpsnprintf(buf, sizeof(buf), "%s: %s -> %s", text, valuebuf_o, valuebuf_n);
2735 PRVM_Breakpoint(prog, stack_index, buf);
2740 static void PRVM_UpdateBreakpoints(prvm_prog_t *prog)
2742 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2745 if (debug->break_statement[0])
2747 if (debug->break_statement[0] >= '0' && debug->break_statement[0] <= '9')
2749 prog->break_statement = atoi(debug->break_statement);
2750 prog->break_stack_index = 0;
2755 func = PRVM_ED_FindFunction (prog, debug->break_statement);
2758 Con_Printf("%s progs: no function or statement named %s to break on!\n", prog->name, debug->break_statement);
2759 prog->break_statement = -1;
2763 prog->break_statement = func->first_statement;
2764 prog->break_stack_index = 1;
2767 if (prog->break_statement >= -1)
2768 Con_Printf("%s progs: breakpoint is at statement %d\n", prog->name, prog->break_statement);
2771 prog->break_statement = -1;
2773 if (debug->watch_global[0])
2775 ddef_t *global = PRVM_ED_FindGlobal( prog, debug->watch_global );
2778 Con_Printf( "%s progs: no global named '%s' to watch!\n", prog->name, debug->watch_global );
2779 prog->watch_global_type = ev_void;
2783 size_t sz = sizeof(prvm_vec_t) * ((global->type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2784 prog->watch_global = global->ofs;
2785 prog->watch_global_type = (etype_t)global->type;
2786 memcpy(&prog->watch_global_value, PRVM_GLOBALFIELDVALUE(prog->watch_global), sz);
2788 if (prog->watch_global_type != ev_void)
2789 Con_Printf("%s progs: global watchpoint is at global index %d\n", prog->name, prog->watch_global);
2792 prog->watch_global_type = ev_void;
2794 if (debug->watch_field[0])
2796 ddef_t *field = PRVM_ED_FindField( prog, debug->watch_field );
2799 Con_Printf( "%s progs: no field named '%s' to watch!\n", prog->name, debug->watch_field );
2800 prog->watch_field_type = ev_void;
2804 size_t sz = sizeof(prvm_vec_t) * ((field->type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2805 prog->watch_edict = debug->watch_edict;
2806 prog->watch_field = field->ofs;
2807 prog->watch_field_type = (etype_t)field->type;
2808 if (prog->watch_edict < prog->num_edicts)
2809 memcpy(&prog->watch_edictfield_value, PRVM_EDICTFIELDVALUE(PRVM_EDICT_NUM(prog->watch_edict), prog->watch_field), sz);
2811 memset(&prog->watch_edictfield_value, 0, sz);
2813 if (prog->watch_edict != ev_void)
2814 Con_Printf("%s progs: edict field watchpoint is at edict %d field index %d\n", prog->name, prog->watch_edict, prog->watch_field);
2817 prog->watch_field_type = ev_void;
2820 static void PRVM_Breakpoint_f(cmd_state_t *cmd)
2824 if( Cmd_Argc(cmd) == 2 ) {
2825 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2828 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2829 debug->break_statement[0] = 0;
2831 PRVM_UpdateBreakpoints(prog);
2834 if( Cmd_Argc(cmd) != 3 ) {
2835 Con_Printf( "prvm_breakpoint <program name> <function name | statement>\n" );
2839 if (!(prog = PRVM_ProgFromString(Cmd_Argv(cmd, 1))))
2843 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2844 strlcpy(debug->break_statement, Cmd_Argv(cmd, 2), sizeof(debug->break_statement));
2846 PRVM_UpdateBreakpoints(prog);
2849 static void PRVM_GlobalWatchpoint_f(cmd_state_t *cmd)
2853 if( Cmd_Argc(cmd) == 2 ) {
2854 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2857 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2858 debug->watch_global[0] = 0;
2860 PRVM_UpdateBreakpoints(prog);
2863 if( Cmd_Argc(cmd) != 3 ) {
2864 Con_Printf( "prvm_globalwatchpoint <program name> <global name>\n" );
2868 if (!(prog = PRVM_ProgFromString(Cmd_Argv(cmd, 1))))
2872 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2873 strlcpy(debug->watch_global, Cmd_Argv(cmd, 2), sizeof(debug->watch_global));
2875 PRVM_UpdateBreakpoints(prog);
2878 static void PRVM_EdictWatchpoint_f(cmd_state_t *cmd)
2882 if( Cmd_Argc(cmd) == 2 ) {
2883 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2886 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2887 debug->watch_field[0] = 0;
2889 PRVM_UpdateBreakpoints(prog);
2892 if( Cmd_Argc(cmd) != 4 ) {
2893 Con_Printf( "prvm_edictwatchpoint <program name> <edict number> <field name>\n" );
2897 if (!(prog = PRVM_ProgFromString(Cmd_Argv(cmd, 1))))
2901 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2902 debug->watch_edict = atoi(Cmd_Argv(cmd, 2));
2903 strlcpy(debug->watch_field, Cmd_Argv(cmd, 3), sizeof(debug->watch_field));
2905 PRVM_UpdateBreakpoints(prog);
2913 void PRVM_Init (void)
2915 Cmd_AddCommand(&cmd_client, "prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2916 Cmd_AddCommand(&cmd_client, "prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2917 Cmd_AddCommand(&cmd_client, "prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2918 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)");
2919 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");
2920 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)");
2921 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)");
2922 Cmd_AddCommand(&cmd_client, "prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2923 Cmd_AddCommand(&cmd_client, "prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2924 Cmd_AddCommand(&cmd_client, "prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2925 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)");
2926 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");
2927 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");
2928 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)");
2929 Cmd_AddCommand(&cmd_client, "cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2930 Cmd_AddCommand(&cmd_client, "menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2931 Cmd_AddCommand(&cmd_client, "sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2932 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");
2933 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");
2934 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");
2936 Cmd_AddCommand(&cmd_server, "prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2937 Cmd_AddCommand(&cmd_server, "prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2938 Cmd_AddCommand(&cmd_server, "prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2939 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)");
2940 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");
2941 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)");
2942 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)");
2943 Cmd_AddCommand(&cmd_server, "prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2944 Cmd_AddCommand(&cmd_server, "prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2945 Cmd_AddCommand(&cmd_server, "prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2946 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)");
2947 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");
2948 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");
2949 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)");
2950 Cmd_AddCommand(&cmd_server, "cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2951 Cmd_AddCommand(&cmd_server, "menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2952 Cmd_AddCommand(&cmd_server, "sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2953 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");
2954 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");
2955 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");
2957 Cvar_RegisterVariable (&prvm_language);
2958 Cvar_RegisterVariable (&prvm_traceqc);
2959 Cvar_RegisterVariable (&prvm_statementprofiling);
2960 Cvar_RegisterVariable (&prvm_timeprofiling);
2961 Cvar_RegisterVariable (&prvm_coverage);
2962 Cvar_RegisterVariable (&prvm_backtraceforwarnings);
2963 Cvar_RegisterVariable (&prvm_leaktest);
2964 Cvar_RegisterVariable (&prvm_leaktest_follow_targetname);
2965 Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames);
2966 Cvar_RegisterVariable (&prvm_errordump);
2967 Cvar_RegisterVariable (&prvm_breakpointdump);
2968 Cvar_RegisterVariable (&prvm_reuseedicts_startuptime);
2969 Cvar_RegisterVariable (&prvm_reuseedicts_neverinsameframe);
2971 // COMMANDLINEOPTION: PRVM: -norunaway disables the runaway loop check (it might be impossible to exit DarkPlaces if used!)
2972 prvm_runawaycheck = !COM_CheckParm("-norunaway");
2982 void PRVM_Prog_Init(prvm_prog_t *prog, cmd_state_t *cmd)
2984 PRVM_Prog_Reset(prog);
2985 prog->leaktest_active = prvm_leaktest.integer != 0;
2986 prog->console_cmd = cmd;
2989 // LadyHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
2990 unsigned int PRVM_EDICT_NUM_ERROR(prvm_prog_t *prog, unsigned int n, const char *filename, int fileline)
2992 prog->error_cmd("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", prog->name, n, filename, fileline);
2996 #define PRVM_KNOWNSTRINGBASE 0x40000000
2998 const char *PRVM_GetString(prvm_prog_t *prog, int num)
3003 VM_Warning(prog, "PRVM_GetString: Invalid string offset (%i < 0)\n", num);
3006 else if (num < prog->stringssize)
3008 // constant string from progs.dat
3009 return prog->strings + num;
3011 else if (num <= prog->stringssize + prog->tempstringsbuf.maxsize)
3013 // tempstring returned by engine to QC (becomes invalid after returning to engine)
3014 num -= prog->stringssize;
3015 if (num < prog->tempstringsbuf.cursize)
3016 return (char *)prog->tempstringsbuf.data + num;
3019 VM_Warning(prog, "PRVM_GetString: Invalid temp-string offset (%i >= %i prog->tempstringsbuf.cursize)\n", num, prog->tempstringsbuf.cursize);
3023 else if (num & PRVM_KNOWNSTRINGBASE)
3026 num = num - PRVM_KNOWNSTRINGBASE;
3027 if (num >= 0 && num < prog->numknownstrings)
3029 if (!prog->knownstrings[num])
3031 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
3034 return prog->knownstrings[num];
3038 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
3044 // invalid string offset
3045 VM_Warning(prog, "PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
3050 const char *PRVM_ChangeEngineString(prvm_prog_t *prog, int i, const char *s)
3053 i = i - PRVM_KNOWNSTRINGBASE;
3054 if(i < 0 || i >= prog->numknownstrings)
3055 prog->error_cmd("PRVM_ChangeEngineString: s is not an engine string");
3056 old = prog->knownstrings[i];
3057 prog->knownstrings[i] = s;
3061 int PRVM_SetEngineString(prvm_prog_t *prog, const char *s)
3066 if (s >= prog->strings && s <= prog->strings + prog->stringssize)
3067 prog->error_cmd("PRVM_SetEngineString: s in prog->strings area");
3068 // if it's in the tempstrings area, use a reserved range
3069 // (otherwise we'd get millions of useless string offsets cluttering the database)
3070 if (s >= (char *)prog->tempstringsbuf.data && s < (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.maxsize)
3071 return prog->stringssize + (s - (char *)prog->tempstringsbuf.data);
3072 // see if it's a known string address
3073 for (i = 0;i < prog->numknownstrings;i++)
3074 if (prog->knownstrings[i] == s)
3075 return PRVM_KNOWNSTRINGBASE + i;
3076 // new unknown engine string
3077 if (developer_insane.integer)
3078 Con_DPrintf("new engine string %p = \"%s\"\n", s, s);
3079 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3080 if (!prog->knownstrings[i])
3082 if (i >= prog->numknownstrings)
3084 if (i >= prog->maxknownstrings)
3086 const char **oldstrings = prog->knownstrings;
3087 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
3088 const char **oldstrings_origin = prog->knownstrings_origin;
3089 prog->maxknownstrings += 128;
3090 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3091 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
3092 if(prog->leaktest_active)
3093 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3094 if (prog->numknownstrings)
3096 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
3097 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
3098 if(prog->leaktest_active)
3099 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
3102 prog->numknownstrings++;
3104 prog->firstfreeknownstring = i + 1;
3105 prog->knownstrings[i] = s;
3106 prog->knownstrings_freeable[i] = false;
3107 if(prog->leaktest_active)
3108 prog->knownstrings_origin[i] = NULL;
3109 return PRVM_KNOWNSTRINGBASE + i;
3112 // temp string handling
3114 // all tempstrings go into this buffer consecutively, and it is reset
3115 // whenever PRVM_ExecuteProgram returns to the engine
3116 // (technically each PRVM_ExecuteProgram call saves the cursize value and
3117 // restores it on return, so multiple recursive calls can share the same
3119 // the buffer size is automatically grown as needed
3121 int PRVM_SetTempString(prvm_prog_t *prog, const char *s)
3127 size = (int)strlen(s) + 1;
3128 if (developer_insane.integer)
3129 Con_DPrintf("PRVM_SetTempString: cursize %i, size %i\n", prog->tempstringsbuf.cursize, size);
3130 if (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
3132 sizebuf_t old = prog->tempstringsbuf;
3133 if (prog->tempstringsbuf.cursize + size >= 1<<28)
3134 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);
3135 prog->tempstringsbuf.maxsize = max(prog->tempstringsbuf.maxsize, 65536);
3136 while (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
3137 prog->tempstringsbuf.maxsize *= 2;
3138 if (prog->tempstringsbuf.maxsize != old.maxsize || prog->tempstringsbuf.data == NULL)
3140 Con_DPrintf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, prog->tempstringsbuf.maxsize/1024);
3141 prog->tempstringsbuf.data = (unsigned char *) Mem_Alloc(prog->progs_mempool, prog->tempstringsbuf.maxsize);
3145 memcpy(prog->tempstringsbuf.data, old.data, old.cursize);
3150 t = (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.cursize;
3152 prog->tempstringsbuf.cursize += size;
3153 return PRVM_SetEngineString(prog, t);
3156 int PRVM_AllocString(prvm_prog_t *prog, size_t bufferlength, char **pointer)
3165 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3166 if (!prog->knownstrings[i])
3168 if (i >= prog->numknownstrings)
3170 if (i >= prog->maxknownstrings)
3172 const char **oldstrings = prog->knownstrings;
3173 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
3174 const char **oldstrings_origin = prog->knownstrings_origin;
3175 prog->maxknownstrings += 128;
3176 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3177 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
3178 if(prog->leaktest_active)
3179 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3180 if (prog->numknownstrings)
3182 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
3183 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
3184 if(prog->leaktest_active)
3185 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
3188 Mem_Free((char **)oldstrings);
3189 if (oldstrings_freeable)
3190 Mem_Free((unsigned char *)oldstrings_freeable);
3191 if (oldstrings_origin)
3192 Mem_Free((char **)oldstrings_origin);
3194 prog->numknownstrings++;
3196 prog->firstfreeknownstring = i + 1;
3197 prog->knownstrings[i] = (char *)PRVM_Alloc(bufferlength);
3198 prog->knownstrings_freeable[i] = true;
3199 if(prog->leaktest_active)
3200 prog->knownstrings_origin[i] = PRVM_AllocationOrigin(prog);
3202 *pointer = (char *)(prog->knownstrings[i]);
3203 return PRVM_KNOWNSTRINGBASE + i;
3206 void PRVM_FreeString(prvm_prog_t *prog, int num)
3209 prog->error_cmd("PRVM_FreeString: attempt to free a NULL string");
3210 else if (num >= 0 && num < prog->stringssize)
3211 prog->error_cmd("PRVM_FreeString: attempt to free a constant string");
3212 else if (num >= PRVM_KNOWNSTRINGBASE && num < PRVM_KNOWNSTRINGBASE + prog->numknownstrings)
3214 num = num - PRVM_KNOWNSTRINGBASE;
3215 if (!prog->knownstrings[num])
3216 prog->error_cmd("PRVM_FreeString: attempt to free a non-existent or already freed string");
3217 if (!prog->knownstrings_freeable[num])
3218 prog->error_cmd("PRVM_FreeString: attempt to free a string owned by the engine");
3219 PRVM_Free((char *)prog->knownstrings[num]);
3220 if(prog->leaktest_active)
3221 if(prog->knownstrings_origin[num])
3222 PRVM_Free((char *)prog->knownstrings_origin[num]);
3223 prog->knownstrings[num] = NULL;
3224 prog->knownstrings_freeable[num] = false;
3225 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
3228 prog->error_cmd("PRVM_FreeString: invalid string offset %i", num);
3231 static qboolean PRVM_IsStringReferenced(prvm_prog_t *prog, string_t string)
3235 for (i = 0;i < prog->numglobaldefs;i++)
3237 ddef_t *d = &prog->globaldefs[i];
3238 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3240 if(string == PRVM_GLOBALFIELDSTRING(d->ofs))
3244 for(j = 0; j < prog->num_edicts; ++j)
3246 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3247 if (ed->priv.required->free)
3249 for (i=0; i<prog->numfielddefs; ++i)
3251 ddef_t *d = &prog->fielddefs[i];
3252 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3254 if(string == PRVM_EDICTFIELDSTRING(ed, d->ofs))
3262 static qboolean PRVM_IsEdictRelevant(prvm_prog_t *prog, prvm_edict_t *edict)
3266 if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
3267 return true; // world or clients
3268 if (edict->priv.required->freetime <= prog->inittime)
3269 return true; // created during startup
3270 if (prog == SVVM_prog)
3272 if(PRVM_serveredictfloat(edict, solid)) // can block other stuff, or is a trigger?
3274 if(PRVM_serveredictfloat(edict, modelindex)) // visible ent?
3276 if(PRVM_serveredictfloat(edict, effects)) // particle effect?
3278 if(PRVM_serveredictfunction(edict, think)) // has a think function?
3279 if(PRVM_serveredictfloat(edict, nextthink) > 0) // that actually will eventually run?
3281 if(PRVM_serveredictfloat(edict, takedamage))
3283 if(*prvm_leaktest_ignore_classnames.string)
3285 if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_serveredictstring(edict, classname)))))
3289 else if (prog == CLVM_prog)
3291 // TODO someone add more stuff here
3292 if(PRVM_clientedictfloat(edict, entnum)) // csqc networked
3294 if(PRVM_clientedictfloat(edict, modelindex)) // visible ent?
3296 if(PRVM_clientedictfloat(edict, effects)) // particle effect?
3298 if(PRVM_clientedictfunction(edict, think)) // has a think function?
3299 if(PRVM_clientedictfloat(edict, nextthink) > 0) // that actually will eventually run?
3301 if(*prvm_leaktest_ignore_classnames.string)
3303 if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_clientedictstring(edict, classname)))))
3309 // menu prog does not have classnames
3314 static qboolean PRVM_IsEdictReferenced(prvm_prog_t *prog, prvm_edict_t *edict, int mark)
3317 int edictnum = PRVM_NUM_FOR_EDICT(edict);
3318 const char *targetname = NULL;
3320 if (prog == SVVM_prog && prvm_leaktest_follow_targetname.integer)
3321 targetname = PRVM_GetString(prog, PRVM_serveredictstring(edict, targetname));
3324 if(!*targetname) // ""
3327 for(j = 0; j < prog->num_edicts; ++j)
3329 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3330 if (ed->priv.required->mark < mark)
3336 const char *target = PRVM_GetString(prog, PRVM_serveredictstring(ed, target));
3338 if(!strcmp(target, targetname))
3341 for (i=0; i<prog->numfielddefs; ++i)
3343 ddef_t *d = &prog->fielddefs[i];
3344 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3346 if(edictnum == PRVM_EDICTFIELDEDICT(ed, d->ofs))
3354 static void PRVM_MarkReferencedEdicts(prvm_prog_t *prog)
3360 // Stage 1: world, all entities that are relevant, and all entities that are referenced by globals.
3362 for(j = 0; j < prog->num_edicts; ++j)
3364 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3365 if(ed->priv.required->free)
3367 ed->priv.required->mark = PRVM_IsEdictRelevant(prog, ed) ? stage : 0;
3369 for (i = 0;i < prog->numglobaldefs;i++)
3371 ddef_t *d = &prog->globaldefs[i];
3373 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3375 j = PRVM_GLOBALFIELDEDICT(d->ofs);
3376 if (i < 0 || j >= prog->max_edicts) {
3377 Con_Printf("Invalid entity reference from global %s.\n", PRVM_GetString(prog, d->s_name));
3380 ed = PRVM_EDICT_NUM(j);;
3381 ed->priv.required->mark = stage;
3384 // Future stages: all entities that are referenced by an entity of the previous stage.
3388 for(j = 0; j < prog->num_edicts; ++j)
3390 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3391 if(ed->priv.required->free)
3393 if(ed->priv.required->mark)
3395 if(PRVM_IsEdictReferenced(prog, ed, stage))
3397 ed->priv.required->mark = stage + 1;
3404 Con_DPrintf("leak check used %d stages to find all references\n", stage);
3407 void PRVM_LeakTest(prvm_prog_t *prog)
3410 qboolean leaked = false;
3412 if(!prog->leaktest_active)
3416 for (i = 0; i < prog->numknownstrings; ++i)
3418 if(prog->knownstrings[i])
3419 if(prog->knownstrings_freeable[i])
3420 if(prog->knownstrings_origin[i])
3421 if(!PRVM_IsStringReferenced(prog, PRVM_KNOWNSTRINGBASE + i))
3423 Con_Printf("Unreferenced string found!\n Value: %s\n Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
3429 PRVM_MarkReferencedEdicts(prog);
3430 for(j = 0; j < prog->num_edicts; ++j)
3432 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3433 if(ed->priv.required->free)
3435 if(!ed->priv.required->mark)
3436 if(ed->priv.required->allocation_origin)
3438 Con_Printf("Unreferenced edict found!\n Allocated at: %s\n", ed->priv.required->allocation_origin);
3439 PRVM_ED_Print(prog, ed, NULL);
3444 ed->priv.required->mark = 0; // clear marks again when done
3447 for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
3449 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
3451 if(stringbuffer->origin)
3453 Con_Printf("Open string buffer handle found!\n Allocated at: %s\n", stringbuffer->origin);
3458 for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
3460 if(prog->openfiles[i])
3461 if(prog->openfiles_origin[i])
3463 Con_Printf("Open file handle found!\n Allocated at: %s\n", prog->openfiles_origin[i]);
3468 for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
3470 if(prog->opensearches[i])
3471 if(prog->opensearches_origin[i])
3473 Con_Printf("Open search handle found!\n Allocated at: %s\n", prog->opensearches_origin[i]);
3479 Con_Printf("Congratulations. No leaks found.\n");